本篇會談到
let
const
let
宣告可變動的變數,使用 let 代替 var
var | let | |
---|---|---|
作用域 | var在function上 | let 在block上 |
window 全域物件 | 會出現在 window 中 | let 不會在 window 上 |
宣告 | 重複宣告不會跳錯 | let 不可重複宣告 |
hoisting | var 在宣告前取值時,會出現hoisting(提升),得到 undefined | let 有 hoisting 但因為有暫時性死區(TDZ)出現ReferenceError錯誤 |
let/const 宣告的變數也是會有提升(hoist)的作用。提升是 JS 語言中對於變數宣告的基本特性,只是因為 TDZ 的作用,並不會像使用 var 來宣告變數,只是會得到undefined而已,現在則是會直接拋出ReferenceError錯誤,而且很明顯的這是一個在執行期間才會出現的錯誤。
區域變數
:在區塊內等被 { }
包起來的地方宣告的變數(例如 if、else、for、while)
let 屬於 block 作用域 這讓每個宣告的屬性都獨立存在於 block 內,這也是為何 let 比 var 穩定很多的原因
let 在 大括號
裏
{}
大括號屬於 block
{
let a = 1;
}
console.log(a);
// a is not defined
// let 的作用域只存在 block 內,不像 var 還能在外層取到值
let 在 function
裏
function
也是 block 所以離開函式無法取值
function fn() {
let a = 1;
}
console.log(a);
// a is not defined
function fn() {
let a = 1;
console.log(a);
}
fn();
// 1
常見考題 for
範例
setTimeout 屬於非同步行為,但因為宣告 let 的作用域會在 大括號中,所以不受到非同步影響。 所以 let 會穩定許多
for (let i = 0; i < 10; i++) {
console.log(i);
// 第一步驟: 輸出 0~9
setTimeout(() => {
console.log(i);
// 第三步驟:輸出 0~9
// setTimeout 屬於非同步行為
// JS 默認在所有事件結束後才會執行非同步行為
// !但是 let 的作用域會存在在大括號內,所以不會受到非同步影響
}, 0);
}
console.log(i);
// 第二步驟: 錯誤:i is not defined
// for 迴圈裡使用 var 時 setTimeout 都是輸出 10
// 但 let 卻可以正常輸出 0~9
// 原因就是 let 屬於 block 作用域 這讓每個 i 都獨立存在於 block 內
// 這也是為何 let 比 var 穩定很多的原因
let 宣告的變數不會出現在 window 裡面
var a = 0;
let b = 1;
console.log(window)
// 上方輸出後打開 window 這個全域物件會看到 a 但不會看到 b
let 不能對已經進行宣告的變數,再一次宣告,但可以用賦予的方式改變值
let a = 0;
let a = 1;
console.log(a)
// Identifier 'a' has already been declared
沒有辦法再宣告 let 之前,去取得值。
const 也一樣。
// 變數的部分
console.log(a)
let a = 0;
// Cannot access 'a' before initializatio
// 你不能在初始化之前獲取他 需在宣告的下一行才執行操作
理解ES6中的暫時死區(TDZ)
當程式的控制流程在新的作用域(module, function 或 block 作用域)進行實體化時,在此作用域中的用 let/const 宣告的變數會先在作用域中被建立出來,但因此時還未進行詞法綁定,也就是對宣告語句進行求值運算,所以是不能被存取的,存取就會拋出錯誤。所以在這執行流程一進入作用域建立變數,到變數開始可被存取之間的一段時間,就稱之為 TDZ(暫時死區)。
const
const b = 0;
b = 1;
console.log(b);
// Uncaught SyntaxError: Identifier 'b' has already been declared
// 因為用 const 宣告的是常數 常數是不能被重新賦值的
const
不能再一次指定值。假設常數的內容(值)是個物件,那麼此物件的內容(物件的參數)是可以更改的。
因為 物件傳值
的特性,新的值賦予到變數上,並不會更動記憶體位置。
const a = {
name: 'white'
}
a.name = 'dark';
console.log(a);
// {name:'dark'}
const family = ['dad', 'mom', '小明'];
family.forEach((item, key) => {
if(item === '小明') {
family.splice(key, 1)
}
});
console.log(family);
// ['爸', '媽']
// 屬於修改陣列中的其中一個
// 所以也是傳參考 可以用 const
物件傳參考
特性:物件
、陣列
、函式
,並不會直接寫值,會建立一個記憶體空間,所以更改位置不可以使用 const
const a = {
name: 'white'
}
a = {
name: 'dark'
}
// TypeError
參考:
JS