ES6 塊級繫結

weixin_34146805發表於2018-05-08
var 宣告與變數提升

使用 var 關鍵字宣告的變數,無論其實際宣告位置在何處,都會被視為宣告於所在函式的頂部,如果宣告不在任意函式內,則視為在全域性作用域的頂部。

function getValue(condition) {
    if (condition) {
        var value = "blue";
        // 其他程式碼
        return value;
    } else {
        // value 在此處可訪問,值為 undefined return null;
    }
    // value 在此處可訪問,值為 undefined
}

如果你不太熟悉 JS ,或許會認為僅當 condition 的值為 true 時,變數 value 才會被建立。但實際上,value 無論如何都會被建立。 JS 引擎在後臺對 getValue 函式進行了調整, 就像這樣:

function getValue(condition) {
    var value;
    if (condition) { 
        value = "blue";
        // 其他程式碼
        return value;
    } else {
        return null;
    }
}

value 變數的宣告被提升到了頂部,而初始化工作則保留在原處。這意味著在 else 分支內 value 變數也是可訪問的,此處它的值會是 undefined ,因為它並沒有被初始化。

塊級宣告

塊級宣告也就是讓所宣告的變數在指定塊的作用域外無法被訪問(在一個函式內部 或 在一個程式碼塊內部)。

let 宣告

let 宣告的語法與 var 的語法一致。你基本上可以用 let 來代替 var 進行變數宣告,但會將變數的作用域限制在當前程式碼塊中(其他細微差別會在稍後討論)。由於 let 宣告並不會被提升到當前程式碼塊的頂部,因此你需要手動將 let 宣告放置到頂部,以便讓變數在整個程式碼塊內部可用。

function getValue(condition) {
    if (condition) {
        let value = "blue";
        // 其他程式碼
        return value;
    } else {
        // value 在此處不可用
        return null;
    }
    // value 在此處不可用
}
禁止重複宣告

如果一個識別符號已經在程式碼塊內部被定義,那麼在此程式碼塊內使用同一個識別符號進行 let 宣告就會導致丟擲錯誤。

var count = 30;
// 語法錯誤
let count = 40;

在巢狀的作用域內使用 let 宣告一個同名的新變數,則不會丟擲錯誤。

var count = 30;
// 不會丟擲錯誤
if (condition) { 
    let count = 40;
    // 其他程式碼
}
常量宣告

ES6 中裡也可以使用 const 語法進行宣告。使用 const 宣告的變數會被認為是常量( constant ),意味著它們的值在被設定完成後就不能再被改變。正因為如此,所有的 const 變數都需要在宣告時進行初始化。試圖對之前用 const 宣告的常量進行賦值會丟擲錯誤。

// 有效的常量
const maxItems = 30;
// 語法錯誤:未進行初始化
const name;
// 丟擲錯誤
const maxItem = 50;

常量宣告與 let 宣告一樣,都是塊級宣告。這意味著常量在宣告它們的語句塊外部是無法訪問的,宣告不會被提升,並且在同一個作用域內不能重複定義。

使用 const 宣告物件比較特殊,const 宣告會阻止對於變數繫結與變數自身值的修改,這意味著 const 宣告並不會阻止對變數成員的修改。

const person = {
    name: "Nicholas"
};
// 工作正常
person.name = "Greg";
// 丟擲錯誤
person = {
    name: "Greg"
}
暫時性死區

當 JS 引擎檢視接下來的程式碼塊並發現變數宣告時,它會在面對 var 的情況下將宣告提升到函式或全域性作用域的頂部,而面對 let 或 const 時會將宣告放在暫時性死區內。任何在暫時性死區內訪問變數的企圖都會導致“執行時”錯誤(runtime error)。只有執行到變數的宣告語句時,該變數才會從暫時性死區內被移除並可以安全使用。

迴圈中的塊級繫結
for (var i = 0; i < 10; i++) { 
    process(i);
}
// i 在此處仍然可被訪問
for (let i = 0; i < 10; i++) {
    process(i);
}
// i 在此處不可訪問,丟擲錯誤
console.log(i);

因為 var 宣告導致了變數提升

迴圈內的常量宣告

在常規的 for 迴圈中,你可以在初始化時使用 const ,但迴圈會在你試圖改變該變數的值時丟擲錯誤。因為該語句試圖更改常量的值。因此,在迴圈中你只能使用 const 來宣告一個不會被更改的變數。

var funcs = [];
// 在一次迭代後丟擲錯誤
for (const i = 0; i < 10; i++) { 
    funcs.push(function() {
        console.log(i);
    });
}

const 變數在 for-in 或 for-of 迴圈中使用時,與 let 變數效果相同。因此下面程式碼不會導致出錯,因為迴圈為每次迭代建立了一個新的變數繫結,而不是試圖去修改已繫結的變數的值。

var funcs = [], object = {
    a: true, b: true, c: true
};

// 不會導致錯誤
for (const key in object) { 
    funcs.push(function() {
        console.log(key);
    });
}
funcs.forEach(function(func) {
    func(); // 依次輸出 "a"、 "b"、 "c"
});
全域性塊級繫結
  • 當在全域性作用域上使用 var 時,它會建立一個新的全域性變數,併成為全域性物件(在瀏覽器中是 window )的一個屬性。這意味著使用 var 可能會無意覆蓋一個已有的全域性屬性。
  • 若你在全域性作用域上使用 let 或 const ,雖然在全域性作用域上會建立新的繫結,但不會有任何屬性被新增到全域性物件上。這也就意味著你不能使用 let 或 const 來覆蓋一個全域性變數,你只能將其遮蔽。
  • 當你不想在全域性物件上建立屬性時,這種特性會讓 let 與 const 在全域性作用域中更安全。
  • 想讓程式碼能從全域性物件中被訪問,你仍然需要使用 var 。在瀏覽器中跨越幀或視窗去訪問程式碼時,這種做法非常普遍。
塊級繫結最佳實踐

在預設情況下使用 const ,而只在你知道變數值需要被更改的情況下才使用 let 。這在程式碼中能確保基本層次的不可變性,有助於防止某些型別的錯誤。

相關文章