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對象就完全凍結了。

相關文章
相關標籤/搜索