ES6之塊級作用域
作用域
作用域指變數所作用的範圍,在 Javascript 中有兩種作用域:
- 全域性作用域
- 函式作用域
變數提升
變數提升(Hoisting)被認為是, Javascript 中執行上下文 (特別是建立和執行階段)工作方式的一種認識。具體表現就是所有透過 var 宣告的變數會提升到當前作用域的最前面。
function foo() {
console.log(temp);
}
function bar() {
console.log(temp);
var temp;
}
foo(); // ReferenceError: temp is not defined
bar(); // undefined
可以看到用 var 宣告瞭的並不會報錯。因為其實函式 bar 等同於
function bar() {
var temp;
console.log(temp);
}
大多數類 C 語言語法的語言都擁有塊級作用域。在一個程式碼塊(括在一對花括號中的一組語句)中定義的所有變數在程式碼塊的外部是不可見的。定義在程式碼塊中的變數在程式碼塊被執行結束後會變釋放掉。這是件好事。
糟糕的是,儘管 Javascript 的程式碼貌似支援塊級作用域,但實際上 Javascript 並不支援(就是因為有變數提升)。這個混淆之處可能成為錯誤之源。
所以在 ES6 中規定了 let 和 const 來支援塊級作用域。但是,是不是真的提升就不存在了呢,可以看下面暫時性死區這部分。
let
let 可以理解為『更完美的 var』,使用方法很簡單;
let foo = 3;
使用方法基本和 var 相同,而且宣告的變數只在其塊和子塊中可用,這點也與 var 相同。 二者之間最主要的區別在於 var 宣告的變數的作用域是整個封閉函式。
function foo() {
if(true) {
var temp = 5;
console.log(temp);
}
console.log(temp);
}
function bar() {
if(true) {
let temp = 5;
console.log(temp);
}
console.log(temp);
}
foo(); // 5 和 5
bar(); // 5 和 "ReferenceError: temp is not defined
let 宣告的變數的作用域只是外層塊,而不是整個外層函式。
我們可以利用這個特性來替代立即執行函式(IIFE)。
// IIFE
(function(){
var temp = xxx;
/*
other code
*/}())
// 塊級
{
let temp = xxx;
/*
other code
*/
}
const
const 的用法跟 let 差不多,但是 const 一定要初始化, 不初始化是會報錯的。
const temp = 4;// 沒有初始化報錯
const t; // SyntaxError: Missing initializer in const declaration
const 是塊級作用域,const 跟 let 的語義相似,就是用來宣告常量的,一旦宣告瞭就不能更改。值得注意的是 const 宣告的變數記錄的是指標,不可更改的是指標,如果 const 所宣告的是物件,物件的內容還是可以修改的。
// 重新賦值宣告導致報錯
const PI = 3.14;
PI = 3.1415926; // TypeError: Assignment to constant variable.
// 給物件增加屬性不會導致 obj 的指標變化,所以不會報錯
const obj = { foo: 2 };
obj.bar = 3;
console.log(obj); // {foo: 2, bar: 3}
暫時性死區
使用 let 或 const 宣告的變數,在宣告沒有到達之前,訪問該變數都會導致報錯,就連一直以為安全的 typeof 也不再安全。
// TDZ1
function foo() { // TDZ 開始
console.log(typeof temp);
let temp = 5; // TDZ 結束
}
foo(); // ReferenceError: temp is not defined
報的錯是 ReferenceError,如果使用 var 宣告的話,temp 輸出應該是 undefined,從 let 宣告的變數的塊的第一行,到宣告變數之間的這個區域被稱作暫時性死區(TDZ)。凡是在這個區域使用這些變數都會報錯。
// TDZ2
function bar() {
console.log(typeof temp);
}
bar(); // undefined
看到上面兩個例子仔細思考有沒有覺得想到點什麼?
在函式里沒有用 let 宣告 temp 的時候,temp 是 undefined,講道理在 let 宣告前也應該是 temp,然而 foo 函式卻報了錯,證明了就算是在未到達 let 宣告的地方,但是在用 let 之前已經起到了作用。這是不是說明其實 let 也有提升,只是在 TDZ 使用的時候報錯了,而不是 undefined。
事實上,當 JS 引擎檢視下面的程式碼塊有變數宣告時,對於 var 宣告的變數,會將宣告提升到函式或全域性作用域的頂部,而對 let 或 const 的時候會將宣告放在暫時性死區內。任何在暫時性死區內訪問變數的企圖都會導致“執行時”錯誤(runtime error)。只有執行到變數的宣告語句時,該變數才會從暫時性死區內被移除並可以安全使用。
禁止重複宣告
在同一個塊內,let 和 const 不能宣告相同的識別符號。禁止的情況包括:
- let 或 const 和 let 或 const
- var 和 let 或者 const
- 函式引數與 let 或 const
// let 和 let
let foo = 1;
let foo = 2;
// let 和 const
let foo = 1;
const foo = 1;
// var 與 let
var foo = 1;
let foo = 1;
// 函式引數與 let
function bar(foo) {
let foo = 1;
}
以上情況都是會報 SyntaxError。但是在巢狀的作用域內使用 let 宣告同一變數是被允許的。
var foo = 1;
{
// 不會報錯
let = 2;
// other code
}
同時因為是 let 和 const 是塊級作用域,宣告的變數在當前塊使用完之後就會被釋放,所以就算使用相同的識別符號也不會覆蓋外部作用域的變數, 而 var 是會覆蓋外部作用域的變數的。
function foo() {
var bar = 1;
{
let bar = 2;
}
console.log(bar);
}
function zoo() {
var bar = 1;
{
var bar = 2;
}
console.log(bar);
}
foo(); // 1
zoo(); // 2
最佳實踐
在 ES6 的發展階段,被廣泛認可的變數宣告方式是:預設情況下應當使用 let 而不是 var 。對於多數 JS 開發者來說, let 的行為方式正是 var 本應有的方式,因此直接用 let替代 var 更符合邏輯。在這種情況下,你應當對需要受到保護的變數使用 const。
在預設情況下使用 const ,而只在你知道變數值需要被更改的情況下才使用 let 。這在程式碼中能確保基本層次的不可變性,有助於防止某些型別的錯誤。
思考題
兩個思考題,我會把答案放在評論中。請點選原文連結去看答案
// 思考題 1
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
// 思考題 2
function bar(){
var foo = 1;
if (true) {
let foo = (foo + 2);
}
}
bar();
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2690698/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ES6語法(一)塊級作用域、字串字串
- 重讀《深入理解ES6》 —— 塊級作用域
- JavaScript 塊級作用域JavaScript
- ES6深入學習(一)塊級作用域詳解
- JavaScript塊級作用域宣告函式JavaScript函式
- ES6let命令和塊級作用域和const命令
- 【ES6基礎】let和作用域
- ES6 變數作用域總結變數
- ES6 塊級繫結
- 塊級作用域替代“匿名立即執行函式表示式(匿名IIFE)”函式
- c語言中塊作用域的優先順序高於檔案作用域C語言
- 函式(三)作用域之變數作用域、函式巢狀中區域性函式作用域、預設值引數作用域函式變數巢狀
- 深入學習js之——詞法作用域和動態作用域JS
- es6塊級繫結筆記筆記
- 深入理解 Javascript 之 作用域JavaScript
- JavaScript之變數及作用域JavaScript變數
- 深入學習js之——詞法作用域和動態作用域#2JS
- 作用域與作用域鏈
- 作用域及作用域鏈
- 【深入淺出ES6】塊級變數變數
- JavaScript 作用域 與 作用域鏈JavaScript
- js的作用域、作用域鏈JS
- 11-程式碼塊和變數的作用域變數
- js的作用域和作用域鏈JS
- javascript之作用域與作用域鏈JavaScript
- js的作用域與作用域鏈JS
- 作用域
- 區塊鏈作用在哪些領域得以實現?區塊鏈
- 深入學習js之——作用域鏈#5JS
- JS 總結之函式、作用域鏈JS函式
- 作用域、作用域鏈及閉包(一)
- 原型、原型鏈、作用域、作用域鏈、閉包原型
- 深入理解JavaScript作用域和作用域鏈JavaScript
- for range 作用域
- javaScript 作用域JavaScript
- js作用域JS
- JavaScript作用域JavaScript
- Go 程式碼塊與作用域,變數遮蔽問題詳解Go變數