JavaScript 中的變數是松散型別的,可以保存任何型別資料,變數只不過是一個名稱,JavaScript 中,可以宣告變數的關鍵字有var、let和const,
1. var
使用var定義變數,可以保存任何型別的值,若不初始化變數,變數會保存undefined,
1. 函式級作用域
使用var定義的變數會成為包含它的函式的區域變數,
function func() {
var a = 'hi'; // 區域變數
}
func();
console.log(a); // ReferenceError: a is not defined
變數a在函式內部使用var定義,呼叫函式func,會創建這個變數并給它賦值,函式執行結束后,變數就會被銷毀,所以上述代碼最后一行會報錯,顯示變數a未定義,
若在函式內部,不使用var運算子,直接給變數a賦值,那么a就成為了全域變數,可以在函式外部訪問到,在瀏覽器環境下,a成為window物件的屬性,
function func() {
a = 'hi'; // 全域
}
func();
console.log(a); // hi
console.log(window.a); // hi
console.log(window.a === a); // true
2. 變數提升
使用var宣告變數,會自動提升到函式作用域頂部,如下代碼:
function func() {
console.log(a);
var a = 1;
}
func(); // undefined
代碼沒有報錯,輸出了undefined,這是因為變數的宣告被提升到了函式作用域頂部,等價于如下代碼:
function func() {
var a;
console.log(a);
a = 1;
}
func(); // undefined
3. 重復宣告
另外,使用var重復宣告同一個變數也可以:
function func() {
var a = 1;
var a = 2;
var a = 3;
console.log(a);
}
func(); // 3
4. 全域變數掛載到 window
瀏覽器環境中,全域作用域下,使用var宣告的變數,會掛載到window物件上,
var a = 1;
console.log(window.a === a); // true
2. let
let也可以宣告變數,但和var運算子有很大的區別,
1. 塊級作用域
以下代碼會報錯,因為let宣告的作用域,具有塊級作用域,即被{}包裹的部分,
if (true) {
let a = 10;
}
console.log(a); // a is not defined
2. 不可重復宣告
以下代碼,執行到let a = 2就會報錯,因為變數a在當前塊級作用域中已經被宣告過了,不能重復宣告,
if (true) {
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
let a = 3;
}
另外,如果混用var和let宣告變數,也是不允許的,下面的代碼都會報錯:
let a;
var a; // 報錯
var a;
let a; // 報錯
2. 不存在變數提升(暫時性鎖區)
使用let宣告的變數,不能在宣告之前訪問它,
if (true) {
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 1;
}
實際上,JavaScript 也會注意出現在塊后面的let宣告,只不過在此之前不能以任何方式來參考未宣告的變數,在let宣告之前的執行瞬間被稱為暫時性死區,
3. 全域變數不會掛載到 window
和var不同,即使在全域作用域下,使用let宣告的變數也不會掛載到window物件,
var a = 1;
let b = 2;
console.log(window.a === a); // true
console.log(window.b === b); // false
4. 不依賴條件宣告
if (typeof name === 'undefined') {
let name;
}
// name 被限制在 if {} 塊的作用域內
name = 'Matt'; // 全域賦值
try {
console.log(age); // 如果 age 沒有宣告過,則會報錯
} catch (error) {
let age;
}
// age 被限制在 catch {}塊的作用域內
age = 26; // 全域賦值
3. const
const的特點與let基本一致,但const有一些自己的特點,
1. 宣告變數時必須同時初始化
以下宣告報錯,因為宣告的同時沒有初始化變數,
const a; // Missing initializer in const declaration
2. 不能修改宣告后的變數
使用const定義了一個變數后,不能再更改它的值:
const a = 1;
a = 2; // TypeError: Assignment to constant variable
這里有一個誤區,實際上,使用const宣告的變數,不能修改的是記憶體地址!!
具體規則如下:
- 當
const定義的常量為基本資料型別時,不能被修改, - 當
const定義的常量為參考資料型別時,可以通過其屬性進行資料修改,
基本資料型別的值就保存在記憶體地址中,所以const定義的基礎資料型別不可被改變, 而參考資料型別指向的記憶體地址只是一個指標,通過指標來指向實際資料,也就是說,不可被改變的是指標,而不是資料,所以const定義的參考資料型別的常量可以通過屬性來修改其資料,
例如,使用const定義了一個陣列,雖然不能更改資料型別,但可以通過push等方法,修改這個陣列中的資料,
const arr = [];
arr.push(1, 2, 3);
console.log(arr); // [ 1, 2, 3 ]
4. 總結及最佳實踐
var | let | const |
|---|---|---|
| 函式級作用域 | 塊級作用域 | 塊級作用域 |
| 重復宣告 | 不可重復宣告 | 不可重復宣告 |
| 變數提升 | 不存在變數提升 | 不存在變數提升 |
| 值可更改 | 值可更改 | 值不可更改 |
全域變數掛載到window | 全域變數不會掛載到window | 全域變數不會掛載到window |
通常,寫 JavaScript 代碼時,遵循以下原則:
- 不使用
var const優先,let次之
5. 一道面試題
以下代碼運行后會列印什么?
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
答案:6 6 6 6 6
雖然每個for回圈中定時器設定的時間都是0,但由于 JavaScript 是單執行緒 eventLoop機制,setTimeout是異步任務,遇到setTimeout函式時,JavaScript 會將其放入任務佇列中,待同步任務執行完畢后,才執行任務佇列中的異步任務,
又因為setTimeout函式也是一種閉包,往上找它的父級作用域鏈是window,而變數i是用var宣告的,是window上的全域變數,所以此時變數i的值已經變成i = 6了,最后執行setTimeout時,當然會輸出 5 個6了!
使用let解決:
利用 JavaScript 的塊級作用域,就不用這么麻煩了,如果for回圈使用塊級作用域變數關鍵字,回圈就會為每個回圈創建獨立的變數,從而每次列印都會有正確的索引值,
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
只介紹了其中一個解決方法,詳細分析及其他解決方法見JavaScript經典題 —— 解決回圈列印問題,
參考資料:
《JavaScript高級程式設計(第4版)》
📘📘歡迎在我的博客上訪問:
https://lzxjack.top/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/342230.html
標籤:其他
上一篇:一文讀懂什么是VUE
