简体   繁体   中英

javascript closure after using anonymous function

I'm working on closure so hard, and I know that in the loop, new function refer closure using the last value of the iterator

so the following function's result is three times "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]();
  }
} 

and then I know that anonymous function can induce scope, so I edit the first function as:

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;
}

but the result is "item1 undefined", "item2 undefined", "item3 undefined",

so my question is, why is the result still undefined after I using scope?

You should pass the i to the anonymous function:

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 still refers to a variable bound to the parent scope, not the one introduced by the FunctionExpression.

The correct fix is to bind a new variable to the new scope

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;
}

Note the i being passed into the function.

Assuming that the purpose of this code is just to learn; you create an anonymous function but you're still referring to the i in the previous scope, so you do not change anything from the first code you wrote initially; i still has the last value ( list.length ).

In order to avoid that, you need to have the current value of i in the scope of the function you've created:

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;
}

Alternatively you can use bind , to have partial application:

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;
}

In ES6, or in Firefox with JS 1.8.5 enabled, you can use also let that declares block scope variable:

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;
}

In the last example there is also the ES6 arrow functions .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM