JavaScript中的變量提升/函數(shù)提升

變量提升

在ES6之前,JavaScript沒(méi)有塊級(jí)作用域的說(shuō)法(一對(duì)花括號(hào){}為一個(gè)塊級(jí)作用域),只有全局作用域和函數(shù)作用域。變量提升即將變量聲明提升到它所在作用域的最開(kāi)始的部分。

按照正常的編程邏輯來(lái)說(shuō),變量或者函數(shù),都應(yīng)該先聲明,再調(diào)用。

就像這樣:

var a;
a = 1;
console.log(a)

但是JavaScript再處理程序時(shí),會(huì)將當(dāng)前作用域內(nèi)所有的變量聲明提升到程序的頂部,所以如果程序長(zhǎng)成這樣,也是可以運(yùn)行的。

a = 1;
console.log(a);
var a;

因?yàn)樵趫?zhí)行上面程序之前,變量a的聲明被提升到了程序的頂部。

因此,在JavaScript中,可以先使用變量,再聲明變量。

按照runoob給的定義:

變量提升:函數(shù)聲明和變量聲明總是會(huì)被解釋器悄悄地被"提升"到方法體的最頂部。

只有聲明的變量才會(huì)有變量提升,初始化的不會(huì)有。

這里要復(fù)習(xí)一下變量聲明的基礎(chǔ)知識(shí)。

變量聲明

var a;
a = 1;

變量初始化

var a = 1;

所以以下兩端代碼,是完全不同的兩個(gè)輸出結(jié)果。

var a = 1;
b = 2;
console.log(a + b)
var b;

輸出3,變量a經(jīng)過(guò)了初始化,變量b被提升了,所以輸出1 + 2。

var a = 1;
console.log(a + b)
var b = 2

輸出NaN,變量a經(jīng)過(guò)了初始化,輸出 1 + b,變量b也經(jīng)過(guò)了初始化,所以不存在變量提升,所以在執(zhí)行1+b的時(shí)候,b的值是undefined,1 + undefined = NaN,因此輸出NaN。

關(guān)于ES6中的 let const

let

ES6中的let命令用于替代ES5中的var命令,因?yàn)樽兞刻嵘龝?huì)使整個(gè)代碼看上去很奇怪。

從2018年中旬開(kāi)始,我已經(jīng)在項(xiàng)目中逐漸使用let來(lái)代替var了,并且最新版本的webStorm中,當(dāng)你使用var來(lái)聲明一個(gè)變量的時(shí)候,編輯器都會(huì)報(bào)warnings,并提示你要使用let來(lái)代替var。

按照 阮一峰ES6教程的話(huà)說(shuō),let命令改變了語(yǔ)法的行為,它所聲明的變量一定要在聲明后使用,否則報(bào)錯(cuò)。

來(lái)看一段代碼:

a = 1;
console.log(a)
let a;

報(bào)錯(cuò):Uncaught ReferenceError: Cannot access 'a' before initialization

意思為:在a未初始化之前無(wú)法調(diào)用。

所以我們需要這么寫(xiě):

let a = 1;
console.log(a) // 1

故 let命令不存在變量提升。

題外話(huà): 有心的讀者可以看看這篇文章:http://www.itdecent.cn/p/0f49c88cf169
作者對(duì)let const的變量提升產(chǎn)生了質(zhì)疑。

const

剛才同樣的代碼,用const輸出如下:

a = 1;
console.log(a)
const a;

報(bào)錯(cuò):Uncaught SyntaxError: Missing initializer in const declaration

意思為: 語(yǔ)法錯(cuò)誤,在const聲明時(shí)未進(jìn)行初始化。

按照阮一峰ES6教程的話(huà)說(shuō), const在聲明時(shí)就一定要渲染,

上面的代碼有幾個(gè)錯(cuò)誤,首先const生成的是常量,不能被修改,所以函數(shù)第一行就是錯(cuò)誤的。其次const必須在聲明時(shí)就初始化,所以第三行也是錯(cuò)誤的。

換一下寫(xiě)法

console.log(a)
const a = 1;

報(bào)錯(cuò):Uncaught ReferenceError: Cannot access 'a' before initialization

和let報(bào)錯(cuò)一樣,這說(shuō)明const同樣不存在變量提升。

暫時(shí)性死區(qū)

只要塊級(jí)作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個(gè)區(qū)域,不再受外部的影響。

var tmp = 123;

if (true) {
    tmp = 'abc'; // Uncaught ReferenceError: Cannot access 'tmp' before initialization
    let tmp;
}

上面代碼中,存在全局變量tmp,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量tmp,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域,所以在let聲明變量前,對(duì)tmp賦值會(huì)報(bào)錯(cuò)。

ES6明確規(guī)定,如果區(qū)塊中存在let和const命令,這個(gè)區(qū)塊對(duì)這些命令聲明的變量,從一開(kāi)始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會(huì)報(bào)錯(cuò)。

總之,在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語(yǔ)法上,稱(chēng)為“暫時(shí)性死區(qū)”(temporal dead zone,簡(jiǎn)稱(chēng)TDZ)。

引用:https://www.cnblogs.com/ricoliu/p/6149912.html

函數(shù)提升

js中創(chuàng)建函數(shù)有兩種方式:函數(shù)聲明式和函數(shù)字面量式。只有函數(shù)聲明才存在函數(shù)提升。

a()
var a = function() {
    console.log(1)
}

報(bào)錯(cuò),因?yàn)槭亲置媪渴?,所以不存在函?shù)提升。

a();
function a() {
    console.log(1)
}

輸出1,因?yàn)槭呛瘮?shù)聲明式,所以存在函數(shù)提升。

根據(jù)剛才的文章,這里應(yīng)該非常容易理解。

函數(shù)提升和變量提升的優(yōu)先級(jí)

根據(jù)官方書(shū)籍《你不知道的javascript》(上卷)中寫(xiě)道:
“函數(shù)會(huì)首先被提升,然后才是變量”。

所以我們看看下面的代碼。

console.log(a);    
console.log(a());    
var a = 3;

function a() {
        console.log(10) 
}
console.log(a)   
a = 6;
console.log(a());  

會(huì)輸出以下內(nèi)容:


image.png

由此可見(jiàn),該函數(shù)的執(zhí)行過(guò)程是:
1、 提升函數(shù)a()
2、提升變量a = 6
3、輸出a 因?yàn)閍為函數(shù)聲明未被變量聲明替代,所以輸出了a的代碼
4、輸出a(),因?yàn)閍是個(gè)函數(shù),所以執(zhí)行了a()
5、重新聲明a = 3 , a被覆蓋。
6、輸出a 因?yàn)閍 = 3,所以輸出3。
7、輸出a(),因?yàn)閍已經(jīng)被3覆蓋,已經(jīng)不是一個(gè)函數(shù),所以輸出a is not a function.

由此可見(jiàn)函數(shù)提升要比變量提升的優(yōu)先級(jí)要高一些,且不會(huì)被變量聲明覆蓋,但是會(huì)被變量賦值之后覆蓋。

引用:https://www.cnblogs.com/oxiaojiano/p/7918967.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容