let和const命令 — 学习ES6(一)

1.let命令

基本用法

ES6新增了一个let命令,用于声明变量,与var命令用法相似,可是用let声明的变量只在let命令所在的代码块有效。javascript

{
        let name = "abc";
        var age = 18
    }
    console.log(name);
    console.log(age);

 

上面的代码在代码块中,分别用let和var定义了两个变量,在代码块外,能够打印出用var声明的age,可是打印不出用let声明的name。这代表,用let声明的变量只在其所在的代码块中有效。java

for循环如今就很适合使用let命令了。闭包

{
        let arr = [];
        for (let i = 0, len = arr.length; i < len; i++) {
        }
    }

另外,使用let命令能够解决以前的闭包问题。函数

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

上面代码用var声明了变量i后,是在全局范围内都有效的。因此每一次循环,新的i都会覆盖以前的旧值,致使最后输出的是最后一个i的值。spa

在ES6之前,解决方式一般是采用闭包的方式。code

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

如今有了let命令,能够再也不使用闭包方式解决该问题,直接经过let声明变量i就能够达到该效果。orm

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

上面的代码用let声明了i变量,当前的i只在本轮循环有效,因此每一次循环的i都是一个新的变量,最后输出的是6。对象

不存在变量提高

let命令声明的变量不会像用var声明的同样,发生“变量提高”的现象。因此,变量必定要在声明后使用。ip

console.log(name); // abc
    var name = 'abc';

    console.log(name); // ReferenceError: can't access lexical declaration `name' before initialization
    let name = 'abc';

暂时性死区(temporal dead zone, TDZ)

只要块做用于中存在let命令声明的变量,该变量就“绑定”在这个块级做用域中,再也不受外部声明的变量影响。作用域

var name = 'abc';
    {
        name = 'jack';
        let name;
    }
    // ReferenceError: can't access lexical declaration `name' before initialization

上面的代码先声明了name,又在块级做用域中用let声明了name,致使后者绑定了这个做用域,因此在let声明name前,对name赋值会报错。

不容许重复声明

let不容许在相同的做用域内重复声明一个变量。

function test() {
        let a = 10;
        let a = 20; // SyntaxError: redeclaration of let a
    }
    function test1() {
        let a = 10;
        var a = 30; // SyntaxError: redeclaration of let a
    }
    function test2(arg) {
        let arg; // SyntaxError: redeclaration of formal parameter arg
    }
    function test3(arg) {
        {
            let arg; // ok
        }
    }

上面代码中,前三个函数都报错了,由于在同一个做用域下存在对let声明的变量进行了重复用声明,而test3函数没有报错,由于不在同一个做用域中。

2.const命令

const用来声明常量。一旦声明,其值就不能够再改变。

const PI = 3.14;
    PI = 3; // TypeError: invalid assignment to const `PI'

const与let同样,只在声明所在的做用域有效,也一样存在TDZ,只能在声明后使用,也不能够重复声明。

对于对象类型的变量,其声明不指向对象的数据,而是指向对象所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址下的数据不变。

const user = {};
    user.name = 'jack';
    console.log(user.name); // jack

    user = {}; // TypeError: invalid assignment to const `user'

上面的代码中,常量user保存着一个对象地址,该对象自己是可变的,能够添加name属性,可是地址不可变,将user从新复制给一个地址会报错。

若是想使const声明的对象数据也不可变,可使用Object.freeze方法冻结对象。

const user = Object.freeze({
        name: 'jack'
    });

    console.log(user.name); // jack
    user.name = 'mark';
    console.log(user.name); // jack

上面的代码中,常量user所指向的对象被冻结,修改name属性无效,可是若是name属性指向的不是字符串,而是一个对象,该处理方式则无效。

let role = {name: 'admin'};
    const user = Object.freeze({
        name: 'jack',
        role: role
    });

    console.log(user.name); // jack
    console.log(user.role.name); // admin
    user.name = 'mark';
    user.role.name = 'qa';
    console.log(user.name); // jack
    console.log(user.role.name); // qa

能够看到,user的role属性是个对象,而role的name属性仍然能够修改,想要解决这种状况,须要将对象的属性也冻结。

let role = {name: 'admin'};
    const user = {
        name: 'jack',
        role: role
    };

    let constantize = (obj) => {
        Object.freeze(obj);
        Object.keys(obj).forEach((key, value) => {
            if (typeof obj[key] === 'object') {
                constantize(obj[key]);
            }
        });
    };

    constantize(user);

    console.log(user.name); // jack
    console.log(user.role.name); // admin
    user.name = "mark";
    user.role.name = "qa";
    console.log(user.name); // jack
    console.log(user.role.name); // admin

上面代码中,constantize是一个将对象完全冻结的函数,调用该函数后,则user对象就完全冻结了。

相关文章
相关标签/搜索