循坏中的块级作用域绑定
for (var i = 0; i < 10; i++) {
}
console.log(i);
console.log(i)由于var声明得到提升,变量i在循坏结束后仍然可以访问。
for (let i = 0; i < 10; i++) {
}
console.log(i);
//ReferenceError: i is not defined
使用let声明,变量i只存在于for循坏中,一旦循坏结束,在其他地方均无法访问到该变量。
循坏中的函数
var func = [];
for (var i = 0; i < 10; i++) {
func.push(function () {
console.log(i);
})
}
func.forEach(function(func) {
func();
})
输出10次10,这里是因为循环里的每次迭代同时共享变量i,循坏内部创建的函数全都保留了对相同变量相同的引用。循坏结束时变量i的值为10,所以每次调用console.log(i)都会输出10
for循环是同步任务,i在不断增加直到等于length时候,循环不再执行,即等于10
而循环内部函数是异步函数,也就是说内部的console.log(i)的执行任务被排在了任务队列的最后,所以for循环本身会先执行10次,但是不执行回调里面的任务,for循环都结束时,回调函数里面的console.log才开始执行第一次,所以每次输出都是10.
为解决这个问题,在循环中使用立即执行函数表达式(IIFE),以强行生成计数器变量的副本。
var func = [];
for (var i = 0; i < 10; i++) {
func.push(function (value) {
return function() {
console.log(value);
}
}(i))
}
func.forEach(function(func) {
func();
})
输出0~9
在循坏内部,IIFE表达式为接受的每一个变量i都创建了一个副本并存储为变量value,这个变量的值就是相应迭代创建的函数所使用的值,因此调用的每个函数都会像从0到9循坏一样得到期望的值。let和const提供的块级作用域让我们无须再这样子折腾。
疑惑
为什么立即执行函数就能得到每个迭代的值?
使用let声明,每次迭代循坏都会创建一个新变量,并以之前迭代中同名变量的值将其迟世红。
循坏中的const声明
普通的for循坏,可以在初始化变量时使用const,但是要更改这个变量的值就会抛出错误
var func = [];
for (const i = 0;i < 10; i++) {
func.push(function() {
console.log(i);
})
}
TypeError: Assignment to constant variable.
类型错误:分配给常量变量。
在循坏的第一个迭代中,i是0,迭代执行成功。执行i++,这条语句试图修改常量,因此抛出错误。
在for-in 或者 for-of循坏中使用const的行为与使用let一致。不会报错。
var func = [];
var object = {
a: true,
b: true,
c: true
}
for (const key in object) {
func.push(function() {
console.log(key);
})
}
func.forEach(function(func) {
func();
})
const 运用在for-in 和for-of循环中,每次迭代不会像前面的for一样修改已有绑定,而是创建一个新的绑定。