Skip to content

Commit 8c3d27a

Browse files
committed
Update explanation about capturing variables
1 parent f9640a9 commit 8c3d27a

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

docs/languages/javascript/function.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,4 +505,31 @@ counters[2](); // 4
505505

506506
这样的代码不仅正确了,可读性也很好。
507507

508+
!!! note "为什么是对的?"
509+
510+
事实上这里正确并不是因为 `let` 关键字带来了什么特殊处理,而是因为作用域不再是函数作用域。
511+
考虑下面的代码,虽然使用了 `let` 关键字,但作用域仍是函数作用域:
512+
513+
```javascript
514+
function getCounters() {
515+
let arr = [];
516+
let i = 0; // 注意在这里声明的 i 是函数作用域
517+
for (i = 0; i < 3; ++i) {
518+
arr.push(function () { console.log(i * i); });
519+
}
520+
return arr;
521+
}
522+
523+
let counters = getCounters();
524+
counters[0](); // 9
525+
counters[1](); // 9
526+
counters[2](); // 9
527+
```
528+
529+
可以看到,结果仍然是错误的。
530+
531+
那么为什么作用域是局部作用域时就对了呢?
532+
原因在于在闭包捕获变量时,对局部作用域的变量会**复制**,而对函数作用域的变量会**引用**。
533+
因此,通过在 `for` 语句中使用 `let` 声明变量,我们声明了局部变量,从而获得了期望的复制捕获行为。
534+
508535
此外需要注意的是,JavaScript 在任何情况下都不会帮你复制**对象**(上面被捕获的 `int` 是基本值而非对象),如果你的闭包捕获一个对象的值,无论这个对象的作用域如何,都会产生类似问题。事实上,这里并非基本值被特殊地对待,而是对象类型的变量存储的值是到实际对象的引用,因此在捕获时复制的是引用(浅拷贝)而非实际对象(深拷贝)。除非你明确地想要保存一些值,否则在构造闭包时请注意捕获实际的值,而非引用。

0 commit comments

Comments
 (0)