新的声明方式

新的声明方式

之所以会提出新的变量声明方式,在我看来一是因为之前仅有的var声明方式过于单一,无法满足对变量的多样化的声明需求,二就是var声明的变量在绑定作用域时过于粗暴。
探讨三个问题:一个是变量提升机制,一个是全局作用域的绑定,还有一个是如何声明局部变量和常量。


问题一:变量提升机制

看一个经典的例子:

function test(flag) {
    if(flag) {
        var value = 1;
    } else {
        return null;
    }
}

我们的本意是希望在flag为false时不声明变量value,但实际情况是value的声明会提升到test函数的顶部,而flag只能控制是否对value赋值。相当于如下所示:

function test(flag) {
    var value;    // value被提升到函数顶部
    if(flag) {
        value = 1;
    } else {
        return null;
    }
}

这就是变量提升机制。


问题二:全局作用域的绑定

我们在全局作用域下执行如下代码:

var a = 1;
function b() {};
window.a;    // 1
window.b;    // ƒ b() {}

我们会发现,全局变量a和函数b自动挂在window下。这种特性会导致属性误覆盖。那么怎么解决这个问题呢?ES5常用的方式就是闭包。其实闭包最大的用处就是处理作用域的问题,这都是因为JS的作用域比较晦涩难懂有比较混乱导致的。


问题三:声明局部变量和常量

在ES5中,如果我们想要声明一个局部变量,最常见的就是把这个局部作为一个函数,在函数内部声明的变量自然就是这个函数的局部变量。如下所示:

function test() {
    var a = 1;
}
console.log(a); // Uncaught ReferenceError: a is not defined

那么如果想要声明一个常量呢?直接使用var是不行的,这也是我上面提到的不够多样的缺点。如果你想要一个变量被赋值后无法更改,可以借助对象,将这个变量声明为对象的一个属性,然后借助Object.defineProperty方法,禁止更新这个属性的值。还有一种方式就是利用闭包,将这个变量放在一个闭包里,然后只对外暴露set和get方法。这两种方式都是比较曲折的方法。


以上三个问题都是为什么ES6要新增新的声明方式的原因之一。
ES6新增了let和const两种声明方式,解决了我上面提到的问题。这里说两个新声明方式比较强大的两个特性:

特性一:临时死区实现更清晰的块作用域

我们把问题一中的变量声明方式如果替换为let或者const会怎么样?

function test(flag) {
    if(flag) {
        let value = 1;
    } else {
        return null;
    }
    return value;
}
test(true);    // Uncaught ReferenceError: value is not defined

使用let,value就不会存在变量提升机制,而是放入了一个叫做临时死区(TDZ)中,如果我们在value声明前调用value,就会报错。所以上述代码中的value,只有在flag为true,并且只有在if条件内才能调用。在test中也无法调用value。改成const也是一样的。


特性二:常量的便捷声明

喜大普奔,我们再也不需要拐弯抹角的去声明一个类似常量的变量。而是直接通过const,就可以声明一个常量。这里有一个需要注意的点,const在声明一个对象类型的值时,“不可更改性”体现在我们不能改变这个常量的指向,也就是说从头到尾只能指向一个对象,如下所示:

const a = {};
a = {};    // Uncaught TypeError: Assignment to constant variable.

但是允许修改这个对象的内容。这是比较特殊的一点。之所以会有这种特点,是和这个常量是存储值还是存储地址相关的。

你可能感兴趣的:(新的声明方式)