變量提升
變量提升是 JavaScript
在運行時的一種機(jī)制。
在代碼被執(zhí)行前,JavaScript 會做一些準(zhǔn)備工作,其中會準(zhǔn)備一個執(zhí)行上下文,也就是代碼的執(zhí)行時的環(huán)境,如 綁定this
、準(zhǔn)備變量等。
變量提升這一特性就是在準(zhǔn)備執(zhí)行上下文時進(jìn)行的,這一特性也是和執(zhí)行上下文相關(guān)的最常在面試中出現(xiàn)的內(nèi)容。
1. 表現(xiàn)
console.log(number); // 輸出:undefined
var number = 1;
console.log(number); // 輸出:1
這段代碼先輸出了 undefined
再輸出了 1
。
說明 number
變量在第一行輸出之前就已經(jīng)存在了,只不過沒有被賦值,因為如果變量不存在,訪問變量會拋出異常:ReferenceError: 變量 is not defined
。
可是在第一次使用 number
之前并沒有聲明過變量,卻可以被訪問到,出現(xiàn)這個表現(xiàn)就是因為變量提升的特性。
在生成執(zhí)行上下文階段,會把變量都收集起來,事先進(jìn)行聲明,需要注意的是,這個特性只會聲明變量,而不會初始化,即變量的值都會是 undefined
。
根據(jù)這個規(guī)則,上面這段代碼在執(zhí)行時可以理解成是這樣的:
var number;
console.log(number);
number = 1;
console.log(number);
需要注意的是,如果沒有使用 var
關(guān)鍵字聲明變量,是不會進(jìn)行提升處理的。
console.log(number);
number = 1;
console.log(number);
這樣就會拋出變量不存在的異常。
2. 函數(shù)提升
函數(shù)也會提升,函數(shù)的提升會把整個函數(shù)放到最前。
fn('咕咕咕');
function fn(str) {
console.log(str);
}
這段代碼可以被正常執(zhí)行,函數(shù)也能被正常調(diào)用,因為在生成執(zhí)行上下文階段,整個函數(shù)會提升到最前面。
這個規(guī)則只對函數(shù)聲明的方式聲明的函數(shù)有效,如果使用的是函數(shù)表達(dá)式,那就會走變量提升的規(guī)則。
console.log(fn); // 輸出:undefined
fn('咕咕咕'); // 拋出異常 TypeError: fn is not a function
var fn = function(str) {
console.log(str);
};
可以看到 fn
能被訪問到,已經(jīng)聲明了,但不能作為函數(shù)調(diào)用,這說明 fn
走了變量提升的機(jī)制。
在執(zhí)行上下文生成的階段,函數(shù)會比變量更早的進(jìn)行提升,也就是說函數(shù)相比變量,更加靠前。
函數(shù)在調(diào)用時也會生成函數(shù)級別的執(zhí)行上下文,這也就意味著提升這個特性也會在函數(shù)執(zhí)行前發(fā)生。
3. 小結(jié)
在 ES6 中和提升相關(guān)的內(nèi)容又有些許不同,let 和 const 這兩個新關(guān)鍵字對提升的表現(xiàn)也和 var 不同,具體可以參閱 ES6 中的相關(guān)內(nèi)容。
現(xiàn)在和提升相關(guān)的內(nèi)容更多的出現(xiàn)在面試題里,由于代碼檢查工具的介入,一些由于提升特性造成的 BUG
出現(xiàn)的越來越少。