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.