JavaScript 中的“提升”是怎么回事?
在執(zhí)行JavaScript代碼之前,js引擎首先會(huì)對(duì)其進(jìn)行解析和編譯。在編譯階段,變量和函數(shù)聲明被放入內(nèi)存,這被稱(chēng)為提升(hoisting)。
需要注意的是,聲明只是被提升,而不是被初始化,這意味著如果一個(gè)變量在使用后被聲明并初始化,則不會(huì)初始化它的值。
function
使用 function 聲明時(shí),可以在定義之前調(diào)用這個(gè)函數(shù),它會(huì)按預(yù)期正常工作。例如:
- hello(); // 輸出 'Hello world!'
- function hello() {
- console.log('Hello world!');
- }
- hello(); // 輸出 'Hello world!'
在上面的例子中, function 聲明被提升到其作用域的頂部,并且由于函數(shù)聲明的性質(zhì),在聲明之前就可用。不過(guò)這是函數(shù)提升行為的唯一案例。
var
另一方面,var 聲明的行為有所不同,在其初始化之前進(jìn)行訪問(wèn)會(huì)返回 undefined。例如:
- console.log(x); // 輸出 'undefined'
- f(); // 拋出異常:'Uncaught TypeError: f is not a function'
- var x = 1;
- var f = () => 'Hi!';
- console.log(x); // 輸出 '1'
- f(); // 返回 'Hi!'
正如你在這個(gè)例子中所看到的,var 聲明被提升到它作用域的頂部,但是變量的值只有在初始化代碼被執(zhí)行時(shí)才會(huì)初始化,所以在初始化這行代碼之前是 undefined。
const 和 let
const 和 let 聲明被提升,但它們沒(méi)有被初始化為 undefined。而是會(huì)給你一個(gè)錯(cuò)誤,這也是 class 聲明的行為方式。例如:
- console.log(y); // 拋出異常: 'Uncaught ReferenceError: Cannot access "y" before initialization'
- g(); // 拋出異常: 'Uncaught ReferenceError: Cannot access "g" before initialization'
- let y = 2;
- const g = () => 'Hey!';
- console.log(y); // 輸出 '2'
- f(); // 返回 'Hey!'
一般情況下,在初始化之前訪問(wèn)用 var 聲明的變量會(huì)默默地失敗,對(duì) const 或 let做同樣的事情會(huì)導(dǎo)致一個(gè)清晰的、易于調(diào)試的錯(cuò)誤。
總結(jié)
- 在使用變量、函數(shù)、對(duì)象和類(lèi)之前,務(wù)必要先定義它們。ESLint 可能可以幫你解決這個(gè)問(wèn)題。
- 在項(xiàng)目中要盡可能的使用 const 和 let,而不是 var 。這樣可以減少很多麻煩。
- 如果可能的話(huà),只使用箭頭函數(shù)或 function 聲明。一致性有助于減少混亂。