理解ES6: 块作用域

这是Nicholas Zakas的新作,原文链接:https://github.com/nzakas/understandinges6/blob/master/manuscript/01-Block-Bindings.md


var声明与变量提升现象

这是前ES6时期var的问题,变量会被JS引擎处理成好像它们的声明被放在函数作用域(或者全局作用域)的顶端。


function getValue(condition) {

    if (condition) {
        var value = "blue";

        // other code

        return value;
    } else {

        // value exists here with a value of undefined

        return null;
    }

    // value exists here with a value of undefined
}

经过JS引擎解释后,上面的代码等同于下面的写法:

function getValue(condition) {

    var value;

    if (condition) {
        value = "blue";

        // other code

        return value;
    } else {

        return null;
    }
}

这个现象以前讨论过很多次,我就不完全复述作者原文了。


区块级别的声明


let的声明


区块作用域的存在要符合两个要求:

  • 在函数里;
  • 在一对{}里。


用let关键字有以下几个作用:

  • 只存在于定义区块内;
  • 不会再被提升,见代码:
    function getValue(condition) {
    
        if (condition) {
            let value = "blue";
    
            // other code
    
            return value;
        } else {
    
            // value doesn't exist here
    
            return null;
        }
    
        // value doesn't exist here
    }
  • 不能重复使用同一个变量名;
  • 但在不同区块里可以重复使用相同名声明变量,比如子区块,如:
    var count = 30;
    
    // Does not throw an error
    if (condition) {
    
        let count = 40;
    
        // more code
    }


常量的声明


  • 它和let一样,也是区块作用域级别,并且不会被提升;
  • 必须在声明时赋值;
  • 不可以被修改,对象是特例,下面会说到;
  • 也不能够重复使用相同的变量名,不论该名字是通过var还是let声明的。


常量对象


这个地方有点晦涩,作者解释的方法是,const关键字约束的是一个绑定本身,而不是被绑定的值,见代码:

const person = {
    name: "Nicholas"
};

// works
person.name = "Greg";

// throws an error
person = {
    name: "Greg"
};

这个现象很接近C++里的常量指针,指针指向的地址不能够改变,它指向一个对象后将永远指向它,但是这个对象本身的内容可以被修改。


临时无人区(TDZ)

TDZ这个概念只是在抽象层面存在的一个术语,用于方便解释一些JS引擎的行为,而且它不是ES规范里的内容,在JS引擎遇到var声明时,它的行为和ES3一样,提升变量,在遇到let或者const声明时,则是把变量先放到一个临时无人区,这样没有人能访问它,甚至是typeof,顺便提一下,在ES3里用typeof操作一个未定义变量是安全的,它会返回undefined。直到程序执行到该变量的声明,它的绑定才会被移出无人区,从此能够被访问到。

if (condition) {
    console.log(typeof value);  // ReferenceError!
    let value = "blue";
}

但TDZ有一个奇特的效果:

console.log(typeof value);     // "undefined"

if (condition) {
    let value = "blue";
}

按说,在if区块之前不应该能够访问到value的。


循环里的区块绑定

ES3里,for循环有一个缺陷,如:

for (var i = 0; i < 10; i++) {
    process(items[i]);
}

// i is still accessible here
console.log(i);                     // 10

这也是个讲过很多次的经典问题了,就不多说了。ES6里,这个问题可以用let解决

for (let i = 0; i < 10; i++) {
    process(items[i]);
}

// i is not accessible here - throws an error
console.log(i);

循环里的函数

ES3里的问题如下示例代码:

var funcs = [];

for (var i = 0; i < 10; i++) {
    funcs.push(function() { console.log(i); });
}

funcs.forEach(function(func) {
    func();     // outputs the number "10" ten times
});

这也是个经典的老问题了,我就不复述了。以前的解决办法是用闭包:

var funcs = [];

for (var i = 0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
            console.log(value);
        }
    }(i)));
}

funcs.forEach(function(func) {
    func();     // outputs 0, then 1, then 2, up to 9
});

但是在ES6里,用let就行了。

var funcs = [];

for (let i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}

funcs.forEach(function(func) {
    func();     // outputs 0, then 1, then 2, up to 9
})

这个行为也适用于for...in循环和for...of循环

var funcs = [],
    object = {
        a: true,
        b: true,
        c: true
    };

for (let key in object) {
    funcs.push(function() {
        console.log(key);
    });
}

funcs.forEach(function(func) {
    func();     // outputs "a", then "b", then "c"
});

在普通的for循环里,你不能用const声明循环控制变量i,但是在for...in和for...of循环里,可以用const,效果和let一样:

var funcs = [],
    object = {
        a: true,
        b: true,
        c: true
    };

// doesn't cause an error
for (const key in object) {
    funcs.push(function() {
        console.log(key);
    });
}

funcs.forEach(function(func) {
    func();     // outputs "a", then "b", then "c"
});

全局作用域的绑定

不同于var,let和const在全局作用域里声明变量时,它们不再会在window对象上创建新属性,所以它们不会重写window上面的内建对象,比如Date和Math。


作者的建议

使用const,其次是let,抛弃var。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值