提问人:mishar 提问时间:8/21/2023 更新时间:8/22/2023 访问量:29
JavaScript 闭包和块范围的变量在循环内存管理中
JavaScript Closures and Block-Scoped Variables In Loop Memory Management
问:
这个问题是几个不同的(我认为是相关的)问题,我在下面给出,但总的来说,我试图理解 David Flanagan 的 O'Reilly 关于 JavaScript 的书中的一段代码,它给出了一个示例,即使用循环来创建意外的闭包:
function constfuncs() {
let funcs = [];
for(var i = 0; i < 10; i++) {
funcs[i] = () => i;
}
return funcs;
}
let funcs = constfuncs();
funcs[5]() //10, rather than 5
上面的代码示例旨在使数组的每个元素都是一个函数,该函数可以在没有参数的情况下调用,以返回一个数值等效于其索引的值;但是,它没有成功做到这一点。我想我在很大程度上理解了为什么这是:,因为它是使用 声明的,作用域是函数,而不是该循环的块,因此存储在 中的每个函数使用的函数相同,并在该循环中结束,因此 中返回的十个函数中的任何一个在调用时总是计算为 。funcs
funcs
var i
var
constfuncs
for
i
funcs
i
10
funcs
10
在本书的下一页,它指出要解决上述问题,只需进行一行更改:只需使用块范围,因此使用而不是在该循环中,如下所示:i
let i
var i
for
function constfuncs() {
let funcs = [];
for(let i = 0; i < 10; i++) {
funcs[i] = () => i;
}
return funcs;
}
let funcs = constfuncs();
funcs[5]() //is now 5, as desired
然而,我不明白的是这如何解决问题。 对我来说,似乎只存在一次,但创建的十个函数中的每一个似乎都可以访问自己的内存版本/副本。如果在循环中声明了一个新的块范围的局部变量,该变量在每次迭代中被分配了值,而不是作为每个函数返回的值,那么也许它对我来说更有意义,如果是在循环中声明的块范围局部变量在每次迭代时被重新声明(如使用新内存);但即便如此,并非如此,但上述内容仍然按预期工作。这让我想到了我的问题:i
i
for
i
i
funcs
i
- 如何在 JavaScript 中管理内存中的闭包?我对 JS 和闭包相当陌生,并且习惯于具有堆栈和堆的语言。
- 定义闭包的行为是否会导致将内存从堆栈复制到堆,以保存外部局部变量以供该闭包使用?这是因为闭包似乎无视仅存储在堆栈上的局部变量,因为它仍然可以访问它们。
- 是否在内存中(每次在不同的内存位置)中为声明它们的外部循环的每次迭代重新创建块范围的局部变量?
- 如果可能的话,是到 3.,这是否完全受是否使用闭包的影响(即,在一般情况下,在循环迭代中只在内存中创建一次,但在每次循环迭代中,如果闭包在它的范围内具有该局部块变量)?
- 通过“绑定”,我们指的是内存中变量或常量的不同位置吗?正如本书在对上述解决方案的解释中所说的那样,我问道:“因为 和 是块作用域,所以循环的每次迭代都定义了一个独立于所有其他迭代的作用域的作用域,并且这些作用域中的每一个都有自己独立的绑定。
let
const
i
问题 1-2 是相关的,问题 3-4 也是如此;并且都与上面的例子有关。欢迎任何其他想法来解释为什么上面的第二种方法(与)成功地解决了解决方案。let i
答:
从技术上讲,我不是在回答你们中的任何一个问题,但我可以解释一下你们想知道“这如何解决问题”的部分:
根据定义,在 for 循环初始化中使用 let
声明的变量被视为在循环“body”中声明的变量。这是 for 循环的特殊行为。let
另一方面,声明的变量与定义 for 循环的范围相同(即在循环“body”之外)。var
例子
因此,如果你在for循环中声明外部,你将再次得到错误的值(正如你所期望的那样):let i
10
function constfuncs(){
let funcs = [];
let i;
for( i = 0; i < 10; i++ ){
funcs[ i ] = function(){
return i;
};
}
return funcs;
}
另一方面,即使你保留了内部循环初始化,但声明了一个带有内部循环体的新变量,你也会得到所需的值:var
let
5
function constfuncs(){
let funcs = [];
for( var i = 0; i < 10; i++ ){
let value = i; // <-- new variable in every iteration
funcs[ i ] = () => value;
}
return funcs;
}
言论
较新的声明是专门为这种行为设计的,因为人们往往会对 的行为感到困惑。let
var
(我假设你想知道:IMO让
有太多的特殊情况。当你
不去想它时,让行为更直观,但如果你想理解它,它会更复杂。但它仍然解决了 var
的重要缺陷。
评论
let
和 block 范围的解释