【JS】從ECMA學(xué)習(xí)let、const晾蜘、var變量聲明與聲明提升那些事

let邻眷、const、var是JS中的聲明關(guān)鍵字

let和const

我們熟知的let和const的特性剔交,常見的就有以下四點(diǎn):
1.let和const聲明的變量在未初始化之前不可以被使用肆饶。(暫時(shí)性死區(qū)TDZ)
2.let和const聲明的變量,在同一個(gè)執(zhí)行上下文中不可以被重復(fù)聲明省容。
3.let可以只聲明抖拴,后面再賦值,未賦值的話初始化值為undefined。
4.const一經(jīng)聲明必須馬上初始化阿宅。

var

1.可以在聲明之前訪問
2.可以在同一個(gè)執(zhí)行上下文中聲明重復(fù)的變量候衍。

但是如果問我,let和const聲明的變量為什么會(huì)這樣洒放,為什么跟var聲明的變量不一樣蛉鹿,一時(shí)間我又說不出個(gè)所以然。所以就去研究了一下ECMA的文檔往湿。

Let and Const Declarations定義:let和const聲明的變量會(huì)掛載到執(zhí)行上下文的詞法環(huán)境當(dāng)中去妖异,這些變量會(huì)在包含它們的環(huán)境記錄項(xiàng)初始化的時(shí)候被創(chuàng)建。但是在變量的詞法綁定執(zhí)行之前他們是無法被訪問的领追。通過詞法綁定定義的變量他膳,如果包含初始化語句,那么詞法綁定執(zhí)行的時(shí)候绒窑,賦值操作也會(huì)執(zhí)行棕孙,而不是在變量創(chuàng)建的時(shí)候賦值。
let語句些膨,允許變量在詞法綁定的時(shí)候不同時(shí)做初始化操作蟀俊,它會(huì)在詞法綁定的時(shí)候初始化為undefined。

從這個(gè)定義當(dāng)中订雾,我們可以粗略提取出我們了解過的let和const的特性肢预。
但是每個(gè)特性具體是怎么實(shí)現(xiàn)的,還要根據(jù)其聲明的具體實(shí)現(xiàn)來看洼哎。

Let and Const Declarations Syntax

從let和const聲明的靜態(tài)語義語法來看烫映,它分為兩大階段:

  • LexicalDeclaration詞法聲明
    錯(cuò)誤檢查階段:
    在詞法聲明階段會(huì)檢查let不能作為變量關(guān)鍵字,以及綁定列表中不允許出現(xiàn)重復(fù)的標(biāo)識(shí)符谱净。(這就是特性2)
    標(biāo)識(shí)符綁定階段:
    把標(biāo)識(shí)符名稱添加到綁定的標(biāo)識(shí)符列表中(BindingList)
  • LexicalBinding詞法綁定
    錯(cuò)誤檢查階段:
    詞法綁定階段會(huì)去判斷const聲明變量是否帶有初始化窑邦。(特性4 get)
    標(biāo)識(shí)符初始化階段:
    執(zhí)行聲明的時(shí)候自帶初始化器的標(biāo)識(shí)符的初始化。(也可以理解為把執(zhí)行賦值操作)沒有初始化的let聲明變量賦值為undefined(特性3 get)
    所以我們可以看到let聲明過程中壕探,它的標(biāo)識(shí)符變量創(chuàng)建和初始化與賦值是分開的

但是到目前為止冈钦,let和const暫時(shí)性死區(qū)這個(gè)特性其實(shí)理解起來還是沒有那么直觀。

為了有更清楚的對(duì)比李请,我們先看var變量聲明的過程中發(fā)生了什么瞧筛。

VariableStatementvar聲明的變量會(huì)掛載到執(zhí)行上下文的變量環(huán)境當(dāng)中。當(dāng)變量包含的環(huán)境記錄項(xiàng)在創(chuàng)建的時(shí)候即會(huì)被實(shí)例化和初始化為undefined导盅。
當(dāng)var聲明的變量较幌,如果帶有賦值語句,賦值操作會(huì)在代碼執(zhí)行的時(shí)候完成白翻,而不會(huì)在變量一聲明創(chuàng)建的時(shí)候就執(zhí)行乍炉。

所以看完var和let的聲明語義之后绢片,我們可以總結(jié)出來:
變量聲明其實(shí)分為三個(gè)步驟:

1.變量標(biāo)識(shí)符的創(chuàng)建
2.變量標(biāo)識(shí)符的初始化
3.變量標(biāo)識(shí)符的賦值

var的處理方式是,標(biāo)識(shí)符創(chuàng)建的時(shí)候岛琼,不管你有沒有賦值語句底循,它先把變量初始化為undefined。
如果var語句后面跟了賦值語句槐瑞,在創(chuàng)建完標(biāo)識(shí)符之后熙涤,代碼執(zhí)行階段再把變量標(biāo)識(shí)符的值更新成賦值的內(nèi)容。
可以說var聲明的變量困檩,不管
例如var a = '123';這個(gè)語句可以拆解成var a; a='123';祠挫,在聲明解析變量標(biāo)識(shí)符綁定階段,JS只去解析了var a聲明悼沿,因?yàn)関ar聲明的特性等舔,此時(shí)a在變量環(huán)境中被創(chuàng)建,并且直接初始化為undefined了显沈。
但是只有當(dāng)JS到了執(zhí)行階段软瞎,才會(huì)去執(zhí)行a='123';此時(shí)變量環(huán)境中的a取值才會(huì)被更新為'123';
這就是為什么以下代碼能執(zhí)行逢唤,并且順序不同打印的結(jié)果不同拉讯。

console.log(a);//undefined
var a = '123';
var  b;
console.log(b);//undefined
console.log(a);//123
b = 2;
console.log(b);//2

這個(gè)的執(zhí)行可以抽象成這樣:

var a;//變量標(biāo)識(shí)符聲明創(chuàng)建階段,此時(shí)a沒賦值不能訪問
var b;//變量標(biāo)識(shí)符聲明創(chuàng)建階段鳖藕,此時(shí)b沒賦值不能訪問
a = undefined;//變量標(biāo)識(shí)符聲明創(chuàng)建階段魔慷,默認(rèn)賦值可以訪問了
b = undefined;
console.log(a);
a = '123';
console.log(b);
console.log(a);
b = 2;
console.log(b);

那么再說回到let,let聲明的過程也是有創(chuàng)建著恩、初始化院尔、賦值三部曲。
但是問題是喉誊,它和var的區(qū)別就在于邀摆,let標(biāo)識(shí)符創(chuàng)建的時(shí)候就只創(chuàng)建了變量,標(biāo)識(shí)符名稱綁定了伍茄,但是初始化是等到賦值階段再初始化栋盹,也就是說如果let聲明的變量帶了賦值內(nèi)容,就不在初始化了敷矫,沒有才會(huì)初始化為undefined例获。
所以當(dāng)執(zhí)行的時(shí)候,let不能在它聲明之前使用曹仗。

console.log(p);//Uncaught ReferenceError: p is not defined
let p;
{
  console.log(x) // Uncaught ReferenceError: Cannot access 'x' before initialization
  var c = 2;
  let x = 1
}
console.log(c);//2
console.log(x);//Uncaught ReferenceError: x is not defined

上面這個(gè)例子當(dāng)中榨汤,{}塊語句產(chǎn)生了新的塊作用域。
let聲明的變量是綁定到{}塊作用域里面的怎茫,所以在塊作用域之外要訪問x會(huì)報(bào)錯(cuò)收壕,但是 var聲明的變量是掛載到當(dāng)前的函數(shù)作用域里面的,所以可以訪問。
我用了兩個(gè)月的時(shí)間才理解 let

Advanced JavaScript ES6 — Temporal Dead Zone, Default Parameters And Let vs Var — Deep dive!
The Difference Between Function and Block Scope in JavaScript
JavaScript ReferenceError – Can’t access lexical declaration`variable’ before initialization

總結(jié):變量提升和暫時(shí)性死區(qū)(TDZ)


  • 變量提升:代碼順序上蜜宪,變量調(diào)用在前旬渠,聲明在后,可以調(diào)用該對(duì)象端壳。(var)
  • 暫時(shí)性死區(qū)(TDZ):變量在初始化之前不可引用告丢,否則會(huì)報(bào)錯(cuò)。(let损谦,const)

ES6原文:let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
let和const聲明的變量會(huì)掛載到執(zhí)行上下文的詞法環(huán)境當(dāng)中岖免。當(dāng)變量所在的詞法環(huán)境被實(shí)例化的時(shí)候,變量就被創(chuàng)建了照捡,但是在變量的詞法綁定(LexicalBinding)賦值之前颅湘,它并不能被訪問。
A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer's AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created.
帶有初始化器(Initializer)的詞法綁定(LexicalBinding)所定義的變量栗精,在LexicalBinding賦值的時(shí)候闯参,初始化器才會(huì)把值賦給這個(gè)變量,而不是在詞法綁定創(chuàng)建的時(shí)候賦值悲立。
If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.
如果let聲明的變量鹿寨,其詞法綁定沒有初始化,該變量在詞法綁定賦值的時(shí)候會(huì)被初始化為undefined薪夕。

function test(){
  console.log(a);
  let a;
}
test();//報(bào)錯(cuò):Cannot access 'a' before initialization

function test2(){
  let b;
  console.log(b);
}
test();//undefined

console.log(c);// Uncaught ReferenceError: c is not defined
let c;

console.log(b);//
var b;
console.log(b);
b = 1;

在執(zhí)行上下文創(chuàng)建的時(shí)候脚草,letconst定義的變量的值是沒有初始化的,但是var定義的變量的值會(huì)被直接初始化為 undefined
在執(zhí)行 fn 時(shí)原献,會(huì)有以下過程(不完全):

進(jìn)入 fn馏慨,為 fn 創(chuàng)建一個(gè)環(huán)境。
找到 fn 中所有用 var 聲明的變量姑隅,在這個(gè)環(huán)境中「創(chuàng)建」這些變量(即 x 和 y)写隶。
將這些變量「初始化」為 undefined。
開始執(zhí)行代碼
x = 1 將 x 變量「賦值」為 1
y = 2 將 y 變量「賦值」為 2

所以讲仰,var聲明的變量在一開始就掛載到了詞法環(huán)境當(dāng)中慕趴,并且對(duì)應(yīng)的標(biāo)識(shí)符默認(rèn)被賦值為undefined,也就是說var聲明的對(duì)象叮盘,一開始就完成了完整的初始化秩贰。

而let聲明的變量,雖然一開始標(biāo)識(shí)符也掛載到詞法環(huán)境當(dāng)中了柔吼,但是標(biāo)識(shí)符沒有有賦值毒费,還處在未初始化的狀態(tài),所以愈魏,let在初始化前是不能被訪問的觅玻,從代碼順序上也就是在聲明之前不能被訪問想际。

變量提升Hoisting
let是否存在提升

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市溪厘,隨后出現(xiàn)的幾起案子胡本,更是在濱河造成了極大的恐慌,老刑警劉巖畸悬,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侧甫,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹋宦,警方通過查閱死者的電腦和手機(jī)披粟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冷冗,“玉大人守屉,你說我怎么就攤上這事≥镎蓿” “怎么了拇泛?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長思灌。 經(jīng)常有香客問我俺叭,道長,這世上最難降的妖魔是什么习瑰? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任绪颖,我火速辦了婚禮,結(jié)果婚禮上甜奄,老公的妹妹穿的比我還像新娘。我一直安慰自己窃款,他們只是感情好课兄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晨继,像睡著了一般烟阐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上紊扬,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天蜒茄,我揣著相機(jī)與錄音,去河邊找鬼餐屎。 笑死檀葛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腹缩。 我是一名探鬼主播屿聋,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼空扎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了润讥?” 一聲冷哼從身側(cè)響起转锈,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎楚殿,沒想到半個(gè)月后撮慨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脆粥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年甫煞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冠绢。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抚吠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弟胀,到底是詐尸還是另有隱情楷力,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布孵户,位于F島的核電站萧朝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夏哭。R本人自食惡果不足惜检柬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竖配。 院中可真熱鬧何址,春花似錦、人聲如沸进胯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胁镐。三九已至偎血,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盯漂,已是汗流浹背颇玷。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留就缆,地道東北人帖渠。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像违崇,于是被迫代替她去往敵國和親阿弃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诊霹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容