1

js 是非常灵活的语言,写起来真是*

不过现在有了typescript,写起来舒服多了。

问题

在说js闭包,一定会牵涉到作用域。而一般在区别 var 跟 let 时就会举 for 循环的例子,但是这里只说 作用域,而不说闭包,那么其实还是看不懂,至于觉得很无厘头。

在阮一峰的 let 和 const 命令一节,举了这么一个例子。

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]

clipboard.png

我们发现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]

clipboard.png

在上图,我们可以看到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](); 




mugbya
1.2k 声望41 粉丝

时间永远分岔,通往无数未来