day16:
这段代码我们应该都见过,想要的效果是我们每隔一秒钟分别打印出来1,2,3,4,5。然而,效果却是打印了5,5,5,5,5。但是我们把声明i的声明语句改成let就可以这是为什么呢?
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, i*1000);
}
原因是因为,var声明的变量会被变量提升
,上面一段代码相当于
var i;
for (i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, i*1000);
}
此时i就是全局对象
中的一个属性,即是window
下的,因此我们每次修改执行完i的值之后,在异步任务
还未执行到的时候for循环已经执行完成了,此时i的值已经被修改成了5,因此每次输出的都是5.
那么为什么使用let
就可以解决呢?
因为let
声明的变量会存在一个块级作用域
的概念,使用let
声明迭代变量时,js引擎会在后台为每一个迭代循环声明一个新的迭代变量,因此每次使用的i算是不同的。
如何不使用let来解决var变量作用域相同的问题呢?
使用闭包
。
产生闭包的条件:
- 内部函数引用外部的变量
- 有内部函数
- 有外部函数
不会用闭包咋办?
今天看到一句话,觉得刷新了我对闭包的认知。可以将闭包理解成一个创造函数对象的工厂,不会写闭包的按照此套路真是屡试不爽,哈哈哈
举个例子:
for (var i = 0; i < 5; i++) {
//2.a的结果时一个函数
let a = creater();
//3.执行i,此时i的值还未改变,此时另一个函数作用域中引用了i变量的值,因此i并不会就此销毁而是会被延用到a函数这个作用域中
a(i);
}
//1.creater相当于就是一个工厂函数,用来延长i的作用域
function creater() {
//4.此时参数的i就是a中的i。在创建函数时会创建一个作用域链,此时参数中的i指向的是a中的i,而a中的i又是全局中的i,在函数创建过程中会预装载全局变量对象,并保存到内部的scope中。此时就创建了一个新的空间,因此全局变量的修改就不会影响到i的改变。
return function (i) {
setTimeout(() => {
console.log(i);
}, i*1000);
};
}
分析过程看上面代码注释。
何以见得全局变量的修改就不会影响到i的改变?
for (var i = 0; i < 5; i++) {
let a = creater();
a(i);
}
console.log(i + " start");
i = 10;
function creater() {
return function (i) {
setTimeout(() => {
console.log(i);
}, i * 1000);
};
}
console.log(i + " end");
打印i时,会发现i的值已经改为了10,发现start之前未5,end时是10.说明此时修改的i就是全局变量中的i。
还有一种常见的修改思路:使用IIFE。原理相同不多做分析。
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(() => {
console.log(i);
}, 1000);
})(i);
creater(i);
}
网友评论