闭包出错了——为什么匿名函数 return 最后
Closures gone awry - why does anonymous function return last
我正在阅读这篇文章 (#3) on JS Closures,并试图理解当外部函数的变量随 for 循环发生变化时闭包出错的这一点...
function celebIDCreator (theCelebs) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebs.length; i++) {
console.log("outer: " + i);
//this anon function returns after the loop has finished
theCelebs[i]["id"] = function () {
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in outer function, not in For loop...
}
}
console.log("outside for loop: " + i);
return theCelebs;
}
var celebs = [{name : "Stallone", id: 0}, {name : "Cruise", id : 0}, {name : "Willis", id : 0}];
var create = celebIDCreator (celebs);
var stalloneID = create[0];
给出输出:
outer: 0
outer: 1
outer: 2
outside for loop: 3
inner: 3
我很困惑为什么内部匿名函数的输出是 console.logged 最后一行,这篇文章的这一部分指的是什么?究竟是哪里的外部变量被改变导致了这个问题?
函数有问题
theCelebs[i]["id"] = function () {
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in outer function, not in For loop...
}
函数内部i
的值引用function celebIDCreator
的词法环境和匿名function(){
的变量环境。由于变量环境内部没有定义,所以 i
必须从词法环境中获取。此外,由于该函数稍后被调用,当从词法环境中获取 i
时,它已被 for
循环修改,现在是最终值 (3)。
替代方法包括使用 IIFE 关闭 i
值。
theCelebs[i]["id"] = (function (i) {
return function(){
console.log("inner: " + i);
return uniqueID + i; //this is now accessing the i declared in the IIFE
}
})(i);
基本上可以归结为:
嵌套函数可以访问包含它的函数的范围。在您发布的示例中,变量 i
是 3 在 for 循环之后的父函数中。内部函数引用外部函数中的变量。
在本文的第二个示例(使用 IIFE)中,嵌套函数接受变量 i
作为函数 (IIFE) 的参数 j
。在 JavaScript 中,数字按值传递,因此内部函数获取 i
当前值的副本,而不是对 i
.
的引用
要理解的关键点是闭包捕获变量,而不是当前值。考虑例如:
function f() {
var i = 1;
return [function() { return ++i; },
function() { return i*2; }];
}
这里的两个闭包共享同一个变量i
,所以你会得到这个行为:
x = f();
console.log(x[0]()); // ==> 2
console.log(x[1]()); // ==> 4
console.log(x[0]()); // ==> 3
console.log(x[1]()); // ==> 6
这意味着,如果您只是在循环中创建闭包,所有闭包都会捕获相同的变量,并且值将是退出循环后的值。
常见的解决方案是使用
return (function(i){return function(){return ++i;}})(i);
相当于
return (function(i2){return function(){return ++i2;}})(i);
换句话说,闭包不再捕获外部 i
变量,而是捕获中间函数的 i2
参数(在每次迭代时创建的另一个变量)。使用这个技巧,您基本上是在创建一个闭包,该闭包捕获 i
的 当前值 ,并且一旦 i
稍后发生突变,它就不会受到影响。
闭包存储对外部函数变量的引用。
stalloneID.id 只是存储功能,但重要的是这里没有执行 yet.If 你登录你会看到下面的内容
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in
外部函数,不在 For 循环中...
所以它仍然能够达到i.When的当前值你执行它(stalloneID.id())因为这个函数通过引用存储"i",它采用最新的值"i" 即 3
问题是由于闭包(inisde 函数)通过引用而不是值 访问外部函数中 i 的值。
在这种特殊情况下,当 for 循环结束并且函数 returns 时,闭包使用 i(3) 的 更新值,因此 103 是返回。
我们可以通过使用立即调用函数表达式(IIFE)来解决这个问题,如下所示:
for(i=0;i<theCelebs.length;i++)
{
theCelebs[0].id=function(j) // j=i , passed on invocation
{
return function()
{
return uniqueId+j;//in each loop , correct value of j(i) is
//saved in array
}
}() // IIFE so that value is returned immediately
}(i) // IIFE with current value of i as parameter to closure
我正在阅读这篇文章 (#3) on JS Closures,并试图理解当外部函数的变量随 for 循环发生变化时闭包出错的这一点...
function celebIDCreator (theCelebs) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebs.length; i++) {
console.log("outer: " + i);
//this anon function returns after the loop has finished
theCelebs[i]["id"] = function () {
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in outer function, not in For loop...
}
}
console.log("outside for loop: " + i);
return theCelebs;
}
var celebs = [{name : "Stallone", id: 0}, {name : "Cruise", id : 0}, {name : "Willis", id : 0}];
var create = celebIDCreator (celebs);
var stalloneID = create[0];
给出输出:
outer: 0
outer: 1
outer: 2
outside for loop: 3
inner: 3
我很困惑为什么内部匿名函数的输出是 console.logged 最后一行,这篇文章的这一部分指的是什么?究竟是哪里的外部变量被改变导致了这个问题?
函数有问题
theCelebs[i]["id"] = function () {
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in outer function, not in For loop...
}
函数内部i
的值引用function celebIDCreator
的词法环境和匿名function(){
的变量环境。由于变量环境内部没有定义,所以 i
必须从词法环境中获取。此外,由于该函数稍后被调用,当从词法环境中获取 i
时,它已被 for
循环修改,现在是最终值 (3)。
替代方法包括使用 IIFE 关闭 i
值。
theCelebs[i]["id"] = (function (i) {
return function(){
console.log("inner: " + i);
return uniqueID + i; //this is now accessing the i declared in the IIFE
}
})(i);
基本上可以归结为:
嵌套函数可以访问包含它的函数的范围。在您发布的示例中,变量 i
是 3 在 for 循环之后的父函数中。内部函数引用外部函数中的变量。
在本文的第二个示例(使用 IIFE)中,嵌套函数接受变量 i
作为函数 (IIFE) 的参数 j
。在 JavaScript 中,数字按值传递,因此内部函数获取 i
当前值的副本,而不是对 i
.
要理解的关键点是闭包捕获变量,而不是当前值。考虑例如:
function f() {
var i = 1;
return [function() { return ++i; },
function() { return i*2; }];
}
这里的两个闭包共享同一个变量i
,所以你会得到这个行为:
x = f();
console.log(x[0]()); // ==> 2
console.log(x[1]()); // ==> 4
console.log(x[0]()); // ==> 3
console.log(x[1]()); // ==> 6
这意味着,如果您只是在循环中创建闭包,所有闭包都会捕获相同的变量,并且值将是退出循环后的值。
常见的解决方案是使用
return (function(i){return function(){return ++i;}})(i);
相当于
return (function(i2){return function(){return ++i2;}})(i);
换句话说,闭包不再捕获外部 i
变量,而是捕获中间函数的 i2
参数(在每次迭代时创建的另一个变量)。使用这个技巧,您基本上是在创建一个闭包,该闭包捕获 i
的 当前值 ,并且一旦 i
稍后发生突变,它就不会受到影响。
闭包存储对外部函数变量的引用。
stalloneID.id 只是存储功能,但重要的是这里没有执行 yet.If 你登录你会看到下面的内容
console.log("inner: " + i);
return uniqueID + i; //this is accessing the i declared in
外部函数,不在 For 循环中...
所以它仍然能够达到i.When的当前值你执行它(stalloneID.id())因为这个函数通过引用存储"i",它采用最新的值"i" 即 3
问题是由于闭包(inisde 函数)通过引用而不是值 访问外部函数中 i 的值。
在这种特殊情况下,当 for 循环结束并且函数 returns 时,闭包使用 i(3) 的 更新值,因此 103 是返回。
我们可以通过使用立即调用函数表达式(IIFE)来解决这个问题,如下所示:
for(i=0;i<theCelebs.length;i++)
{
theCelebs[0].id=function(j) // j=i , passed on invocation
{
return function()
{
return uniqueId+j;//in each loop , correct value of j(i) is
//saved in array
}
}() // IIFE so that value is returned immediately
}(i) // IIFE with current value of i as parameter to closure