js 是非常灵活的语言,写起来真是*
不过现在有了typescript,写起来舒服多了。
问题
在说js闭包,一定会牵涉到作用域。而一般在区别 var 跟 let 时就会举 for 循环的例子,但是这里只说 作用域,而不说闭包,那么其实还是看不懂,至于觉得很无厘头。
在阮一峰的 一节,举了这么一个例子。
var a = [];for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 10
跟
var a = [];for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 6
然后我就不清楚了,为什么使用var全局变量后, 就输出10, 变成块级作用域let后就正常输出了。
动手
不知道到底怎么回事,只好调试去看变量到底是什么样?在两个例子中都稍微增加了点东西
var a = [];var b;var c;for (var i = 0; i < 10; i++) { b = i; c = i; a[i] = function () { console.log(i); console.log(c); };}
这里,循环里面有3个变量,内部函数中引用两个。然后我们循环三次后,看看a[0], a[1]
我们发现a[0], a[1]首先是个函数对象,在scopes 中有 Closure 这个东西,这就是闭包了。
这里闭包中只有 i跟c,并没有b, 因为b没有在内部函数中被使用,因此没有被scopes 记录下来。而且请注意,i跟c的值都是当前变量i的值。 这也是闭包的属性的,能够记录下内部函数引用外部的值。因为 i, c, b 都是全局变量,所以循环也就是不断值覆盖,闭包并不会记录在循环时的值,只会记录 闭包变量。注: 我这里是循环了3次,所以 闭包变量都是3,如果循环完了则是另外的值,你能正确说出它们的值么?
接着我们来看let 的改编
var a = [];let b;let c;for (let i = 0; i < 10; i++) { b = i; c = i; a[i] = function oo() { console.log(i); console.log(c); };}a[6](); // 6
同样,这里依旧i,b,c三个变量,内部函数中引用两个。然后我们循环三次后,看看a[0], a[1]
在上图,我们可以看到scopes 增加了个新东西 Block, 这是函数记录了 块作用域。
看着这个图,我们就可以这么理解: let声明的变量i 不是全局变量,每次循环都是作用域关闭然后重新再重建,但是在内部函数又引用了 这个块级作用域变量, 所以内部函数会记录这个值。而变量c 虽然也是 let声明,为什么不是被记录到 Block 呢,这是因为 变量c 虽然是let声明,但是是在for循环外面, 对于这个文件来说,变量c就是全局变量,所以被记录到 closure 中
over
看完以上分析,不知道有没有加深你对let,const 的理解, let声明的变量是块级作用域,const声明的是全局变量,但也要看用在哪儿。 闭包时记录的 除了闭包变量还有块级作用域变量
最后来看看这个输出什么:
var a = [];let b;let c;for (let i = 0; i < 10; i++) { b = i; c = i; var n = i; let m = i; a[i] = function oo() { console.log(i); console.log(c); console.log(n); console.log(m); };}a[6]();