为什么不在函数副本内分配变量而不是引用它
Why doesn't assigning variables inside a function copy instead of referencing it
我有这个代码:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function () {
console.log(i);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
发生这种情况是因为 i
始终是对 makeArmy()
函数内部外部 i
的引用。
如果我们调整代码为:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
// copy the varible i into the while lexical scope
let j = i
let shooter = function () {
console.log(j);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
army[0](); // 0
army[1](); // 1
army[2](); // 2
以上代码按预期工作,因为我们没有在 shooter()
中引用变量 i
,而是将其复制到 j
中,从而使其可用于射手函数本身范围。
现在,我的问题是,为什么在函数本身内部复制 i
变量不起作用?即使我仍在有效地复制变量?我在这里错过了什么?
代码:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function () {
let j = i;
console.log(j);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
当您在射手函数中调用 console.log(i)
时,i
的值指的是 while 循环结束后的值,因此 i 将是它的最后一个值。在您的情况下,i
的值随时间变化,显示它的最后一个值。当您将其分配给变量 (let j = i
) 时,您复制的是该值而不是引用,因此它会为您提供所需的结果。
因为您复制的时间 i
已经是 10
。
其实在inner函数里加上let j = i
,跟直接用i
没什么区别,因为那个赋值也是只有当您调用该内部函数,如前所述,此时 i
已经是 10
,因为调用发生在循环完成很久之后,例如army[0]()
.
正如您所指出的,如果您将 let j = i
移到内部函数之外(但仍在循环内),它就会起作用。这样,对局部变量 j
的赋值会在每次迭代中立即发生,内部函数稍后会引用这 10 个不同的 j
及其先前设置的正确值i
具有您想要的相应值的时间点。
顺便说一下,如果你使用 for
循环,这个问题就不会存在,因为 for
和 let
有一些“魔法”可以创建 semi-separated 每次迭代的范围(确切的工作方式是 a bit more complicated)。
for (let i = 0; i < 10; i++) {
let shooter = function () {
console.log(i);
};
shooters.push(shooter);
}
我有这个代码:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function () {
console.log(i);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
发生这种情况是因为 i
始终是对 makeArmy()
函数内部外部 i
的引用。
如果我们调整代码为:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
// copy the varible i into the while lexical scope
let j = i
let shooter = function () {
console.log(j);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
army[0](); // 0
army[1](); // 1
army[2](); // 2
以上代码按预期工作,因为我们没有在 shooter()
中引用变量 i
,而是将其复制到 j
中,从而使其可用于射手函数本身范围。
现在,我的问题是,为什么在函数本身内部复制 i
变量不起作用?即使我仍在有效地复制变量?我在这里错过了什么?
代码:
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function () {
let j = i;
console.log(j);
// that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
当您在射手函数中调用 console.log(i)
时,i
的值指的是 while 循环结束后的值,因此 i 将是它的最后一个值。在您的情况下,i
的值随时间变化,显示它的最后一个值。当您将其分配给变量 (let j = i
) 时,您复制的是该值而不是引用,因此它会为您提供所需的结果。
因为您复制的时间 i
已经是 10
。
其实在inner函数里加上let j = i
,跟直接用i
没什么区别,因为那个赋值也是只有当您调用该内部函数,如前所述,此时 i
已经是 10
,因为调用发生在循环完成很久之后,例如army[0]()
.
正如您所指出的,如果您将 let j = i
移到内部函数之外(但仍在循环内),它就会起作用。这样,对局部变量 j
的赋值会在每次迭代中立即发生,内部函数稍后会引用这 10 个不同的 j
及其先前设置的正确值i
具有您想要的相应值的时间点。
顺便说一下,如果你使用 for
循环,这个问题就不会存在,因为 for
和 let
有一些“魔法”可以创建 semi-separated 每次迭代的范围(确切的工作方式是 a bit more complicated)。
for (let i = 0; i < 10; i++) {
let shooter = function () {
console.log(i);
};
shooters.push(shooter);
}