使用匿名函数后的javascript闭包

时间:2013-11-11 08:11:09

标签: javascript closures

我正在努力关闭,我知道在循环中,新函数使用迭代器的最后一个值引用闭包

所以以下函数的结果是三次“item3 undefined”

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push( function() {alert(item + ' ' + list[i])} );
  }
  return result;
}

function testList() {
  var fnlist = buildList([1,2,3]);
  // using j only to help prevent confusion - could use i
  for (var j = 0; j < fnlist.length; j++) {
    fnlist[j]();
  }
} 

然后我知道匿名函数可以引出范围,所以我将第一个函数编辑为:

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    (function(){
      var item = 'item' + list[i];
      result.push( function() {alert(item + ' ' + list[i])} );

    })();
  }
  return result;
}

但结果是“item1 undefined”,“item2 undefined”,“item3 undefined”,

所以我的问题是,在使用范围后,为什么结果仍然未定义

3 个答案:

答案 0 :(得分:0)

您应该将i传递给匿名函数:

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    (function(i){
      var item = 'item' + list[i];
      result.push( function() {alert(item + ' ' + list[i])} );

    })(i);
  }
  return result;
}

答案 1 :(得分:0)

i仍然引用绑定到父作用域的变量,而不是FunctionExpression引入的变量。

正确的解决方法是将新变量绑定到新范围

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    (function(i){
      var item = 'item' + list[i];
      result.push( function() {alert(item + ' ' + list[i])} );

    })(i);
  }
  return result;
}

注意传递给函数的i

答案 2 :(得分:0)

假设此代码的目的只是为了学习;你创建一个匿名函数,但你仍然指的是前一个范围中的i,所以你不要改变你最初编写的第一个代码; i仍然具有最后一个值(list.length)。

为了避免这种情况,您需要在您创建的函数范围内具有i的当前值:

function buildList(list) {
  var result = [];

  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push(function (index) { 
      return function() {alert(item + ' ' + list[index])}
    }(i));
  }

  return result;
}

或者,您可以使用bind进行部分申请:

function buildList(list) {
  var result = [];

  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push(function(index) {alert(item + ' ' + list[index])}.bind(null, i))
  }

  return result;
}

在ES6或启用了JS 1.8.5的Firefox中,您还可以使用声明块范围变量的let

function buildList(list) {
  var result = [];

  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    let index = i;
    result.push(() =>alert(item + ' ' + list[index]));
  }

  return result;
}

在最后一个示例中,还有ES6 arrow functions