1
  1. let命令
    用来声明变量,它的用法类似var,但是let命令声明的变量只在所在的代码块中有效。

    {
        var a = 1;
        let b = 2;
    }
    
    console.log(a); // 1
    console.log(b); // b is not defined b未定义

    这就说明let定义的变量只在对应的代码块中有效。
    同样的下面是个for循环

    for(let i = 0; i < 5; i++){
        // ...
    }
    console.log(i);// i is not defined i未定义

    i只在循环体内有效。
    阮一峰老师的文章中有一个例子我们看一下并且分析一下

    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6

    老师给我们的分析是这样子的:

    上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
    如果还不理解的话我们可以再看一个例子:
    var a = [];
    for (let i = 0; i < 10; i++) {
      let val = i;
      a[i] = function () {
        console.log(val);
      };
    }
    a[6](); // 6

    这时候你就会理解原因了,就相当于for循环中的执行体,每次的执行都位于一个单独的代码块中。这个val是我们显示定义的一个副本,当我们执行a[6]的时候,他会找到a[6]保存的函数的执行环境下去找这个val,数组a中的每个元素都位于一个单独的代码块中互不影响。那么我们可以得出一个结论:js引擎会为我们for循环中每次循环的代码块保存一个副本。
    Nicholas C. Zakas《深入理解ES6》中也有相关解释。
    不存在变量提升

    console.log(a);// undefined
    console.log(b);// b is not defined
    var a = 1;
    let b = 2;

    暂时性死区
    只要块级作用域中存在let命令,那么let声明的变量就会绑定这个作用域不受外部影响。

    var temp = 'hello';
    if(true){
        console.log(temp);// temp is not defined
        let temp;
    }
    原因就是let绑定了块级作用域并且let定义的变量不会提升,就是这个地盘跟我同名的都得死。
    隐蔽的死区
    function test(x = y, y = 2) {
        return [x, y];
    }
    test();// 报错

    原因是:当执行test()的时候会先将y的值赋给x作为初始值,但此时y不存在,所以报错。

    function test(x = 2, y = x) {
        return [x, y];
    }
    test();// [2, 2]

    这样就没问题了。

    总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

    不允许重复声明
    let不允许在同一作用域声明两个相同的变量。

    let a;
    var a;//报错  a已经被声明了
    function fun(params) {
        let params;
    }
    func();//报错  params已经被声明了
    function test(params) {
        {//内部代码块
            let params;
        }
    }
    test();//正常执行  执行函数内部的代码块被其中的那个变量占用了而已

    块级作用域
    先看一下es5,es5只有全局作用域和函数作用域。

    var name = 'Jason';
    function test() {
        console.log(name);
        if(false) {
            var name = 'Nico';
        }
    }
    ==>相当于
    var name = 'Jason';
    function test() {
        var name;//变量提升
        console.log(name);
        if(false) {
            name = 'Nico';
        }
    }
    所以执行test()会打印undefined

    还有就是for循环的时候i的定义被提前

    for(var i = 0; i < 3; i++) {
        console.log(i);
    }
    console.log(i);
    依次打印0 1 2 3

    原因是i定义被提前,如下:

    var i;
    for(i = 0; i < 3; i++) {
        console.log(i);
    }
    console.log(i);

    es6出现了块级作用域

    function test() {
        let n = 0;
        if(true) {
            let n = 1;
        }
        console.log(n);
    }
    test();// 0

    外层的块级作用域不受内层的控制。

    {
        {
            let a = 1;
        }
        console.log(a);//报错 a未被定义
    }

    外层作用域无法访问内层作用域定义的变量

    {
        let a = 0;
        {
            let a = 1;
        }
    }

    内层作用域可以命名外层已经命名的变量。
    值得提醒的是函数在块级作用域中的声明

    function test() {
        console.log('outside');
    }
    (function() {
        if(false) {
            function test() {
                console.log('inside');
            }
        }
        console.log(test());
    })();

    ES5会打印inside字符串,ES6却会报test is not a function的错。
    是不是很诧异,为什么ES6没有打印outside呢?

    原因是:ES6中允许块级作用域中声明函数,但是函数声明会类似于var提升到全局作用域或者函数作用域头部,同时函数声明会提升到块级作用域头部,所以上面的代码就相当于
    function test() {
        console.log('outside');
    }
    (function() {
        var test = undefined;
        if(false) {
            function test() {
                console.log('inside');
            }
        }
        console.log(test());
    })();
    所以才会报test is not a function的错误。

    所以尽量避免块级作用域中声明函数,如果有必要的话可以使用函数表达式。

    function test() {
        console.log('outside');
    }
    (function() {
        if(false) {
            let test = function() {
                console.log('inside');
            }
        }
        console.log(test());// outside
    })();
  2. const命令
    用于定义常量,一旦定义必须立刻初始化不然报错,定义后无法改变值,改变也会报错。

    const USER_NAME;//报错
    const USER_ID = '410100';
    USER_ID = 2;//报错

    const的作用域与let命令相同:只在声明所在的块级作用域内有效
    const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
    const声明的常量,也与let一样不可重复声明。
    const其实保证的是变量存储的内存地址不得改动,对于简单的数据类型(数值,布尔,字符串),值就存在内存地址指向的位置,就等同于常量。但是对于数组、对象保存的就是一个指针,只能保证这个指针不被改变。对于内部的数据结构的变化是无法控制的。

    const ARR = [];
    ARR.push(1);// 可以执行
    ARR.length = 0;// 可以执行
    ARR = [];//报错
    const OBJ = {};
    OBJ.name = 'JASON';// 可以执行
    OBJ = {};//报错

    ES6 声明变量的六种方法:var function let const import class

    顶层对象:浏览器中指window Node中指global
    ES5的时候全局变量的赋值与顶层变量的赋值是同一件事。
    ES6改变了这一点除了varfunction还保持原来的方式,新的声明变量的方法不再与顶层变量挂钩。


Jason
90 声望3 粉丝