塊級繫結
var變數及變數提升
當使用var宣告變數時,js引擎會先將宣告語句提升至當前作用域最頂部(如果在函式內部,就是函式作用域的最頂部;如果是全域性作用域,就提升到全域性作用域最頂部)
function getValue(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
// value exists here with a value of undefined
return null;
}
// value exists here with a value of undefined
}
複製程式碼
javascript 引擎處理後的結果
function getValue(condition) {
var value;
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}
複製程式碼
延伸
關於編譯器對函式、變數提升的問題:
- var和函式宣告會在程式碼執行前,被提升到作用域頂部
- 函式表示式的方式,只會把變數提升
- let不會被提升,做到了即用即宣告的目的,不會因為變數提升導致不可預估的異常
// 函式宣告提升 printName('hello world') var printName = function(name){ console.log('name:',name) //name: hello world } // 函式表示式不會被提升 printName('hello world') // Uncaught TypeError: printName is not a function var printName = function(name){ console.log('name:',name) } /// 等效於 var printName //變數提升 printName('hello world') // Uncaught TypeError: printName is not a function printName = function(name){ console.log('name:',name) } 複製程式碼
塊級宣告
塊極作用域(又叫詞法作用域)被建立的情況:
- 在函式內部
- 在程式碼塊{}內部
塊極變數不存在變數提升的情況,塊極變數只能在作用域內訪問,外部訪問不了
let宣告
- 在塊極作用域內使用,和var宣告的變數使用方法基本一致
const宣告
- 常量宣告必須在宣告時完成初始化
- 常量宣告會阻止對變數本身的修改,但是對於const宣告的物件,對其變數的某一成員屬性修改是不報錯的,但是不建議
const person = {
name: "Nicholas"
};
// 工作正常
person.name = "Greg";
// 丟擲錯誤
person = {
name: "Greg"
};
複製程式碼
注意點:
1. 同級作用內不能重複宣告
var count = 30;
// Syntax error
let count = 40;
複製程式碼
2. 暫時性死區
使用 let 或 const 宣告的變數,在當前同級作用域內在達到宣告處之前都是無法訪問的
當 JS 引擎檢視接下來的程式碼塊並發現變數宣告時,它會在面對 var 的情況下將宣告提升到函式或全域性作用域的頂部,而面對 let 或 const 時會將宣告放在暫時性死區內。任何在暫時性死區內訪問變數的企圖都會導致“執行時”錯誤(runtime error)。只有執行到變數的宣告語句時,該變數才會從暫時性死區內被移除並可以安全使用。
if (true) {
// 在同級塊極作用域內,不能在塊極變數宣告之前使用它
console.log(typeof value); //Uncaught ReferenceError: value is not defined
let value = "blue";
}
複製程式碼
// 在外層訪問則無所謂了,不會報語法錯誤
console.log(typeof value); // "undefined"
if (true) {
let value = "blue";
}
複製程式碼
3. 在迴圈中使用let塊極變數繫結,可以實現區域性作用域繫結的效果(for-in\for-of同樣適用)
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // 輸出數值 "10" 十次
});
複製程式碼
var被提升至最頂部,在迴圈中i變數是共享的同一個變數,所以在迴圈內建立的函式都擁有對同一i變數的引用,因此當最後執行函式時,列印的都是最後的i結果
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 從 0 到 9 依次輸出
})
複製程式碼
在每次迴圈中,在區域性作用域中都會建立一個新的區域性塊極變數i,在每一次迭代中都是在自己的區域性作用域中,因此i變數不會被共享,當函式執行時,也就是列印對應作用域中的i變數的值
4. 全域性塊極繫結
- 在全域性作用域,var宣告的變數會掛載到window物件上
- 在全域性作用域,let宣告的變數不會掛載到window物件上,當訪問同名變數時,會優先取區域性塊極變數(遮蔽全域性變數,除非使用window.變數名訪問)
變數宣告最佳實踐
- 預設情況下,優先使用const
- 在確定宣告的變數會更改時,使用let