變量提升
在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)容:

由此可見(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ì)被變量賦值之后覆蓋。