var、let、const這些變數宣告各有什麼區別及特點,本文將從三個方面闡述他們差別:
- 變數提升機制
- 塊級作用域繫結
- 塊級繫結的最佳實踐
一、var變數提升機制及特點
在函式作用域或全域性作用域中通過var宣告的變數,都會被當成在當前作用域頂部宣告的變數,這就是變數提升機制,下面用一個函式例子說明:
function getAge(name){
if(name){
var age = '28';
return age;
}
// 這裡也能訪問到if語句裡的age變數,不過因為變數未初始化,結果為undefined
console.log(age);
}
複製程式碼
其實js在預編譯階段,會把程式碼修改成下面這樣子:
function getAge(name){
var age;
if(name){
age = '28';
return age;
}
console.log(age); // 結果也為undefined
}
複製程式碼
從上面的示例中可以看到,變數age的宣告被提升至函式頂部,而初始化操作依舊在if語句中完成。 不過還有一點要注意的是,除了var有提升機制,函式也有,請看下面示例:
console.log(age) // ƒ age() {console.log(123);}
age(); // 28
var age = '28'
function age(){
console.log('28');
}
age() // Uncaught TypeError: age is not a function
複製程式碼
上述示例說明:函式提升與變數提升同時存在時,函式提升優先順序高於變數提升並且,並且不會被變數宣告覆蓋,但是當變數賦值之後age函式被覆蓋。
注:還有一個要注意的點,當var被用於全域性作用域時,它會建立一個全域性物件(瀏覽器環境中的window物件)的屬性,所以使用var在全域性作用域宣告的時候要特別注意,全域性物件中是否存在這屬性,防止被覆蓋
var age = '40';
console.log(age); // 40
console.log(window.age) // 40
var RegExp = "hello,world";
// 本來window全域性物件中有js內建的RegExp函式,結果被var宣告的RegExp變數給覆蓋了。
console.log(window.RegExp); // hello,world
複製程式碼
二、塊級作用域繫結
1、let宣告
在上面我們提到var宣告會被提升至作用域頂部,由此可能會引發一些奇怪的BUG,正因為如此, ES6 引入了塊級宣告(let、const),塊級宣告也就是讓所宣告的變數在指定塊級作用域外無法被訪問,讓變數的生命週期更加可控。還是以上面的例子說明:
function getAge(name){
if(name){
let age = '28';
return age;
}
// 如果使用let塊級宣告,這裡會報錯:
// Uncaught ReferenceError: age is not defined
console.log(age);
}
複製程式碼
上述例子說明age變數只有在if程式碼塊裡可以訪問,其它地方無法訪問該變數,這說明let會在一個函式內部或在一個程式碼塊(由一對花括號包裹)內部會建立一個塊級作用域。 下面這個例子也很好的說明了這個問題:
for(var i = 0;i<10;i++){}
console.log(i); // 10
複製程式碼
因為var宣告的變數被提升到全域性作用域頂部,迴圈體外也可以訪問,也導致了變數i在迴圈的每次迭代中都被共享了,所以得到的結果是:10, 但使用let就可以解決這個問題:
for(let i = 0;i<10;i++){}
console.log(i); // Uncaught ReferenceError: i is not defined
複製程式碼
使用let宣告的話,i變數只在迴圈體內可以訪問,其它地方都訪問不到。 同時let也禁止重複宣告:
var age="40";
var age = "50";
// 如果var宣告,後面的宣告會覆蓋前面的宣告,所以這裡列印結果結果是:50
console.log(age);
let name = "張三";
let name = "李四";
// 使用let重複宣告,會出現報錯:
// Uncaught SyntaxError: Identifier 'name' has already been declared
console.log(name);
複製程式碼
let和var宣告還有一點不同的是,在全域性作用域中let宣告不會像var一樣會在全域性window物件中建立一個屬性。
var age = '40';
console.log(window.age === age); // true
let name = '張三';
console.log(window.name === name); // false
複製程式碼
且let並不會像var宣告被提升到作用域頂部
console.log(name); // undefined
var name = '張三';
console.log(age); // 報錯:Uncaught TypeError: age is not a function。
let age = '40';
複製程式碼
因為var宣告會被提升至作用域頂部,所以此時訪問name是存在的,但是name此時並未被賦值,所以得到undefined,但是如果使用let或const來宣告變數,因為它們並不具備提升機制,所以在宣告前訪問會報錯,也稱暫時性死區。
2、const宣告
const的作用很多時候和let是一樣的,但通過const宣告的變數為基礎資料型別時,它是不可變的,當它宣告的變數為引用型別時,可以修改它的屬性,如果要確保引用型別值的屬性值不可變,請使用Object.freeze()凍結
let age = '40';
age = '50';
console.log(age); // 50
const name = '張三';
name = '李四';
console.log(name); // 報錯:Uncaught TypeError: Assignment to constant variable.
const person = {
name:'張三'
}
person.name = '李四';
console.log(person.name); // 李四
複製程式碼
三、塊級繫結的最佳實踐
在ES6中一般情況下需要使用變數宣告的地方都應該使用let或const替代var,在預設情況下建議使用 const,因為一般變數初始化之後都不應該被改變,有助於防止某些型別的錯誤,但變數值確實需要被更改的情況下才使用let。