循坏中的块级作用域绑定

循坏中的块级作用域绑定

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一样修改已有绑定,而是创建一个新的绑定。


 Previous
全局块作用域 全局块作用域
var 被用于全局作用域时,他会创建一个新的全局变量作为全局对象(浏览器环境中的window对象)的属性。这意味着var可能会无意中覆盖一个已经存在的全局属性。 var RegExp = "hello"; console.log(windo
2019-03-11
Next 
块级声明 块级声明
块级声明块级声明:用于在指定块的作用域之外无法访问的变量。块级作用域(词法作用域)存在于 函数内部 块中{这里之间区域} let声明 let可以把变量限制在当前作用域中 同一作用域let禁止重复定义已经存在的标识符 变量value被l
2019-03-10
  TOC