1.变量的值
先前在写代码的时候碰到一个问题,使用闭包来执行函数,会造成变量不能正常调用。
用一个例子来说明:
for(var i = 0; i<3; i++){
function(){
console.log(i);
}
}
执行的结果并不是期望中的0,1,2;而是三个3。
后来从《我用了两个月的时间才理解 let》中找到了问题所在。
这里先放上地址:
https://zhuanlan.zhihu.com/p/28140450
根据文中所述,使用let创建变量,可以使得该变量在编译阶段生成多个隐藏的作用域中的变量,用来承载变量的临时值。比如上面这个例子可以使用这样的写法:
for(let i = 0; i<3; i++){
function(){
console.log(i);
}
}
仅仅改变一个变量创建的方式,就可以达到预期的效果。
这里先不讨论let具体的作用方式,而是探究为何这个闭包不能起到预期的效果。
在JS中,一个对象有创建、初始化和赋值三个阶段。所以,对于变量 i 而言,它在这段代码中经历了如下的过程:
1. 进入编译环境中
2. 创建变量i
3. 首先将i初始化为undefined
4. 执行代码期间给i赋值
在这个过程中,可以发现在执行代码之前要进行变量的创建和初始化,执行代码后进行赋值。
而对于function,则是在执行之前就对声明中的变量执行创建、初始化和赋值操作,内存中存储的实际上是这个function执行过后才能得到的结果,供其他对象调用。自然,这里的 i 也就在每一次调用的时候都变成了3,即执行过后的值。
而let能够巧妙避开这里面的弊端,得益于它每一次执行都使用一个隐藏的作用域中的变量来记录变量的当前值,然后在真正执行的时候,依次指向这些隐藏的变量,而不是原本的那个变量,从而得到预期的结果。
2.跨作用域调用变量
这里主要讨论的是this指向的问题。在闭包中,跨作用域的变量是可以直接操作的,但是this比较特殊。
this的本质是一个指针,指向当前对象的作用域。然而在闭包中,this指向的并不是原本对象的作用域,而是闭包的作用域。也就是说,this是会动态变化的,在不同的作用域中有不同的指向,因而会造成跨作用域时失效。
一个简单的解决方式是使用一个临时变量,在对象作用域中(这个方法来自于《Learning TypeScript》一书)
let _this = this;
它解决了在闭包中找不到对象作用域的问题,以便闭包能对外部的变量也进行操作。
最后再摘录一下《我用了两个月的时间才理解 let》中对于变量提升的总结:
1. let 的「创建」过程被提升了,但是初始化没有提升。
2. var 的「创建」和「初始化」都被提升了。
3. function 的「创建」「初始化」和「赋值」都被提升了。
4. const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。都被提升了。