es6塊級綁定之let and const全理解

變量聲明一直是js工做中最微妙的一部分,它不像C語言同樣,變量老是在被它建立的時候聲明,js語言能夠容許你在你須要聲明變量的時候進行聲明。

  • var let const 之變量聲明

  • var 聲明與變量提高。

當咱們使用var關鍵字進行變量聲明的時候,不管變量聲明的位置在哪裏,都會被是爲聲明於所在的函數的頂部(若是不在函數內的話,則視爲在全局做用域的頂部)這就是所謂的變量提高(hoisting)

var提高以下:es6

function getValue(condition) {
if (condition) {
var value = "blue";
// 其餘代碼
return value;
} else {
// value 在此處可訪問,值爲 undefined
return null;
}
// value 在此處可訪問,值爲 undefined
}
  • 塊級聲明let

塊級聲明也就是讓所聲明的變量在指定的做用域外沒法被訪問到,塊級做用域在以下狀況下被建立

  1. 在一個函數內部,
  2. 在一個代碼塊(由一對花括號包裹)內部

let聲明的語法和var聲明一致,因爲let聲明不會將變量提高到函數頂部,所以咱們須要手動將let聲明放置到頂部,以便讓變量在整個代碼塊內部可用。

以下所示:瀏覽器

function getValue(condition) {
if (condition) {
let value = "blue";
// 其餘代碼
return value;
} else {
// value 在此處不可用
return null;
}
// value 在此處不可用
}

禁止重複標識

若是一個標識符已經在代碼內部被定義,重複進行let聲明會報錯安全

var a = 30;
//報錯
let a = 30;

a變量被聲明瞭兩次:一次使用 var ,另外一次使用 let 。由於 let 不能在同一做用域內重複聲明一個已有標識符,此處的 let 聲明就會拋出錯誤。另外一方面,在嵌套的做用域內使用 let 聲明一個同名的新變量,則不會拋出錯誤,如下代碼對此進行了演示:

var count = 30;
// 不會拋出錯誤
if (condition) {
let count = 40;
// 其餘代碼
}

這段代碼中不會拋錯,關鍵點在於let在同一級代碼塊中重複聲明會報錯

const常量聲明

在es6中可使用const語法進行聲明。使用const聲明的變量會被認爲是常量(constant),意味着他們的值在被設置完成後既不能再被改變。正由於如此,全部的const的變量都須要在聲明時進行初始化,

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

maxItems 變量被初始化了,所以它的 const 聲明能正常起效。而 name 變量沒有被初始化,致使在試圖運行這段代碼時拋出了錯誤。const聲明會組織對變量綁定和對自生值的修改,這意味着const聲明並不會組織對變量成員的修改。例如:

const person = {
name: "Nicholas"
};
// 工做正常
person.name = "Greg";
// 拋出錯誤
person = {
name: "Greg"
};

const聲明和let聲明的對比

  1. 首先他們都是塊級聲明,這就意味着常量在聲明它們的語句塊外是沒法被訪問的,而且聲明也不會被提高,示例以下:

if (condition) {
const maxItems = 5;
// 其餘代碼
}
// maxItems 在此處沒法訪問
  1. 它們在統一級做用域中重複聲明時會致使拋出錯誤

暫時性死區

當咱們使用let或者const 進行聲明的時候,在到達聲明處以前都是沒法訪問的,若是咱們試圖訪問會致使一個引用錯誤。出項這個問題是由於暫時性死區

當JS 引擎檢視接下來的代碼塊並發現變量聲明時,它會在面對 var 的狀況下將聲明提高到函數或全局做用域的頂部,而面對 let 或 const 時會將聲明放在暫時性死區內。任何在暫時性死區內訪問變量的企圖都會致使「運行時」錯誤(runtime error)。只有執行到變量的聲明語句時,該變量纔會從暫時性死區內被移除並能夠安全使用。

循環中的塊級綁定

for (var i = 0; i < 10; i++) {
process(items[i]);
}
// i 在此處仍然可被訪問
console.log(i); // 10

輸出的結果並非預期的值而是10;是由於var聲明致使的變量的提高。聰明的你確定會想到使用塊級綁定來進行變量聲明併發

for (let i = 0; i < 10; i++) {
process(items[i]);
}

console.log(i);

i在此處是否是會正常輸出呢,其實不會,在這個例子中會致使報錯,爲何呢?由於i在此處不可訪問。本例中的變量 i 僅在 for 循環內部可用,一旦循環結束,該變量在任意位置都不可訪問。函數

咱們在來看看一下代碼code

var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // 輸出數值 "10" 十次
});

你本來可能預期這段代碼會輸出 0 到 9 的數值,但它卻在同一行將數值 10 輸出了十次。這是
由於變量 i 在循環的每次迭代中都被共享了,意味着循環內建立的那些函數都擁有對於同一
變量的引用。在循環結束後,變量 i 的值會是 10 ,所以當 console.log(i) 被調用時,
每次都打印出 10 。對象

爲了修正這個問題,開發者在循環內使用當即調用函數表達式(IIFEs),以便在每次迭代中
強制建立變量的一個新副本,示例以下:作用域

var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // 從 0 到 9 依次輸出
});

循環內的 let 聲明

let 聲明經過有效模仿上例中 IIFE 的做用而簡化了循環。在每次迭代中,都會建立一個新的
同名變量並對其進行初始化。這意味着你能夠徹底省略 IIFE 而得到預期的結果,就像這樣開發

var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 從 0 到 9 依次輸出
})

咱們是否會想到這個問題:爲何一樣的代碼使用let聲明會致使不同的結果呢?
在循環中let聲明每次都建立了一個新的i變量,所以在循環內部建立的函數得到了各自的i副本,而每一個i副本的值都會在每次的循環迭代聲明變量的時候肯定了get

var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // 依次輸出 "a"、 "b"、 "c"
});

本例中的 for-in 循環體現出了與 for 循環相同的行爲。每次循環,一個新的 key 變量綁
定就被建立,所以每一個函數都可以擁有它自身的 key 變量副本,結果每一個函數都輸出了一個
不一樣的值。而若是使用 var 來聲明 key ,則全部函數都只會輸出 "c" 。
let 聲明在循環內部的行爲是在規範中特別定義的,而與不提高變
量聲明的特徵沒有必然聯繫。事實上,在早期 let 的實現中並無這種行爲,它是後來
才添加的。

循環內的常量聲明

雖然es6沒有明確的規範咱們不能在for循環中使用const聲明,然而它會根據循環方式的不一樣而有不一樣的行爲,咱們能夠在初始化時使用const,可是當循環試圖改變變量的值的時候會拋出錯誤,例如:

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

在此代碼中, i 被聲明爲一個常量。循環的第一次迭代成功執行,此時 i 的值爲 0 。在
i++ 執行時,一個錯誤會被拋出,由於該語句試圖更改常量的值。所以,在循環中你只能使
用 const 來聲明一個不會被更改的變量
而另外一方面, 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"
});

這段代碼與「循環內的 let 聲明」小節的第二個例子幾乎徹底同樣,惟一的區別是 key 的值在
循環內不能被更改。 const 可以在 for-in 與 for-of 循環內工做,是由於循環爲每次迭
代建立了一個新的變量綁定,而不是試圖去修改已綁定的變量的值(就像使用了 for 而不是
for-in 的上個例子那樣)。

全局塊級綁定

let 與 const 不一樣於 var 的另外一個方面是在全局做用域上的表現。當在全局做用域上使
用 var 時,它會建立一個新的全局變量,併成爲全局對象(在瀏覽器中是 window )的一
個屬性。

總結

let和const塊級做用域的引入,可以使咱們減小不少無意的錯誤,它們的一個反作用,是不能在變量聲明位置以前訪問它們

塊級綁定當前的最佳實踐就是:在默認狀況下使用 const ,而只在你知道變量值須要被更改的狀況下才使用 let 。這在代碼中能確保基本層次的不可變性,有助於防止某些類型的錯誤。

相關文章
相關標籤/搜索