let和const

264 阅读4分钟

ES6 新增了let命令,所声明的变量,只在let命令所在的代码块内有效。

关于let的要点

1.使用let的for循环的特别之处:

设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。举例!

        for (let i = 0; i < 3; i++) {
            let i = "abc"
            console.log(i);
        }
        //结果打印三次abc

函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let 重复声明同一个变量)。

2. 不存在变量提升

var不一样它所声明的变量一定要在声明后使用,否则报错。

3. 暂时性死区

如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。举例!

        var tmp = 123;
        if (true) {
            tmp = "abc" //ReferenceError
            let tmp
        }

包括这种情况:

        let x = x //ReferenceError

因为js是先取值再赋值,先RHS查询取得x的值然后LHS查询赋值给变量x,然而x的值并没有被声明,所以报错+

4.不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。举例!

        function fn(){
            let a = 10
            var a = 20 //Uncaught SyntaxError: Identifier 'a' has already been declared
        }

因此,不能在函数内部重新声明参数。

关于块作用域

es5只有全局作用域和函数作用域,没有块级作用域,就会出现这样的情况:

       var tmp = new Date()
       function fn(){
           console.log(tmp);
           if(false){
               var tmp = "hello world"
           }
       }
       fn() // undefined

这是由于变量提升导致的,if语句内的var tmp被提升到了函数fn中第一行

        function fn() {
            var tmp
            console.log(tmp); // undefined
            if (false) {
                tmp = "hello world"
            }
        }

let实际上为 JavaScript 新增了块级作用域。

        function fn() {
            let a = 5;
            if (true) {
                let a = 10
            }
            console.log(a);
        }
        fn() // 5

两个let创建的两个块作用域,两个a都是独立的互不干扰,原先匿名函数的的作用也被取代了

        //匿名函数写法
        (function () {
            var a = 10
        }())
        //块级作用域写法
        {
            let a = 10
        }

块级作用域与函数声明

ES5规定函数只能在全局作用域和函数作用域之中声明,不能在块级作用域声明。但ES6允许在块级作用域之中声明函数,函数声明语句的行为类似于let,在块级作用域之外不可引用。关于这一点,不同的支持环境会导致不同的结果,应该避免在块级作用域内声明函数。

顺便一提,ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

关于const命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。

        const PI=3.1415
        PI=3 // Uncaught TypeError: Assignment to constant variable

这也意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。举例!

        const PI; // Uncaught SyntaxError: Missing initializer in const declaration

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

        {const PI=3.1415}
        console.log(PI); //Uncaught ReferenceError: PI is not defined

同样的,const命令声明的常量也是不进行提升,同样存在暂时性死区,只能在声明的位置后面使用,也与let一样不可重复声明。

const本质

const实际上保证的,并不是变量的值不得改动,而是变量指向的内存地址不得改动。因此对于复杂类型的数据,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。举例!

        const foo = {}
        foo.name = "哈哈" // 哈哈
        console.log(foo.name);
        foo = {} // Uncaught TypeError: Assignment to constant variable.

foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,但对象本身是可变的,所以依然可以为其添加新属性。

顺便一提:let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。(在浏览器中顶层对象就是window)

本文参考自《ES6入门标准》一书