摘要:第二個(gè)賦值聲明會(huì)被留在原地等待執(zhí)行階段。這個(gè)過(guò)程就叫作提升。還有一點(diǎn),函數(shù)聲明會(huì)被提升,但是函數(shù)表達(dá)式不會(huì)被提升。
到目前為止,大家應(yīng)該很熟悉作用域的概念了,以及根據(jù)聲明的位置和方式將變量分配給作用域的相關(guān)原理了。函數(shù)作用域和塊作用域的行為是一樣的,可以總結(jié)為:任何聲明在某個(gè)作用域內(nèi)的變量,都將屬于這個(gè)作用域。
但是作用域同其中的變量聲明出現(xiàn)的位置有某種微妙的關(guān)系,而這個(gè)細(xì)節(jié)就是我們這節(jié)要探討的內(nèi)容。
1. 聲明提升先看代碼:
a = 2; var a; console.log(a);
大家認(rèn)為這里會(huì)輸出什么?
有一些人認(rèn)為是 undefined ,因?yàn)?var a; 是在 a = 2; 之后,所以會(huì)覺(jué)得 undefined 覆蓋了 a 的值。但是,真正的結(jié)果是 2 。
再看一段代碼:
console.log(a); var a = 2;
鑒于上一個(gè)例子,有些人會(huì)認(rèn)為這里會(huì)輸出 2 ,也有人認(rèn)為由于 a 在使用前并沒(méi)有聲明,所以這里會(huì)報(bào)錯(cuò)。但是,這里的結(jié)果是 undefined 。
之前討論編譯器的時(shí)候,我們知道 JS 引擎會(huì)在解釋代碼之前首先對(duì)其進(jìn)行編譯。編譯階段的第一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來(lái)。
因此,正確的思路是,包括變量和函數(shù)在內(nèi)的所有聲明都會(huì)在任何代碼執(zhí)行前首先被處理。
當(dāng)你看到 var a = 2; 時(shí),JavaScript 實(shí)際上會(huì)將其看成兩個(gè)聲明:var a; 和 a = 2; 。第一個(gè)定義聲明是在編譯階段進(jìn)行的。第二個(gè)賦值聲明會(huì)被留在原地等待執(zhí)行階段。
所以,在第一個(gè)例子中,代碼的等價(jià)形式是這樣的:
var a; a = 2; console.log(a);
第二個(gè)例子中,代碼的等價(jià)形式是這樣的:
var a; console.log(a); a = 2;
這個(gè)過(guò)程就好像是變量和函數(shù)聲明從它們的代碼中出現(xiàn)的位置被“移動(dòng)”到了最上面。這個(gè)過(guò)程就叫作“提升”。
注意,只有聲明本身會(huì)被提升,而賦值操作和其他運(yùn)行邏輯都會(huì)停留在原地,想象一下,如果提升會(huì)改變代碼的執(zhí)行順序,那么會(huì)造成非常嚴(yán)重的破壞。
還有一點(diǎn),函數(shù)聲明會(huì)被提升,但是函數(shù)表達(dá)式不會(huì)被提升。
foo(); // 報(bào)錯(cuò),TypeError: foo is not a function,因?yàn)檫@里 foo 是 undefined,并不是一個(gè)函數(shù) var foo = function foo() { // something else }
這段程序中的變量標(biāo)識(shí)符 foo 被提升并分配給所在的作用域(在這里是全局作用域),因此 foo() 不會(huì)導(dǎo)致 ReferenceError 。但是,foo 此時(shí)并沒(méi)有賦值(如果它是一個(gè)函數(shù)聲明而不是函數(shù)表達(dá)式,那么就會(huì)被賦值)。foo() 由于對(duì) undefined 值進(jìn)行函數(shù)調(diào)用而導(dǎo)致非法操作,所以會(huì)拋出 TypeError 異常。
同時(shí),即使是具名函數(shù)表達(dá)式,名稱標(biāo)識(shí)符在賦值之前也無(wú)法在所在作用域中使用:
foo(); bar(); var foo = function bar () { // something else };
這段代碼經(jīng)過(guò)提升后,實(shí)際上等價(jià)于:
var foo; foo(); bar(); foo = function () { var bar = ...self... // something else };2. 函數(shù)優(yōu)先
函數(shù)聲明和變量聲明都會(huì)被提升。但是一個(gè)值得注意的細(xì)節(jié)是,函數(shù)聲明會(huì)首先被提升,然后才是變量。
考慮如下代碼:
foo(); // 1 var foo; function foo () { console.log(1); } foo = function () { console.log(2); };
這里會(huì)輸出 1 而不是 2 。這段代碼其實(shí)等價(jià)于:
function foo () { console.log(1); } foo(); // 1 foo = function () { console.log(2); };
var foo; 盡管出現(xiàn)在 function foo() {...} 聲明之前,但是它是重復(fù)聲明,所以會(huì)被編譯器忽略,因?yàn)楹瘮?shù)聲明會(huì)被提升到變量聲明之前。
注意,盡管重復(fù)的 var 聲明會(huì)被忽略,但重復(fù)的函數(shù)聲明卻會(huì)覆蓋前一個(gè)同名函數(shù)。
foo(); // 3 function foo () { console.log(1); } var foo = function () { console.log(2); }; foo(); // 2 function foo () { cosole.log(3); }
這個(gè)例子充分說(shuō)明了在同一個(gè)作用域中進(jìn)行重復(fù)定義是非常糟糕的,而且經(jīng)常會(huì)導(dǎo)致各種奇怪的問(wèn)題。上面那個(gè)例子,等價(jià)于:
function foo () { cosole.log(3); } foo(); // 3 foo = function () { console.log(2); }; foo(); // 2
還有一些人會(huì)犯如下錯(cuò)誤:
foo(); // 2 var a = true; if (a) { function foo () { console.log(1); } } else { function foo () { console.log(2); } }
因?yàn)?if 并沒(méi)有塊作用域,所以這里的函數(shù)聲明會(huì)提升到其作用域最前邊,而后一個(gè) function 聲明會(huì)覆蓋前一個(gè),所以這里結(jié)果是 2 。這里代碼等價(jià)如下:
function foo () { console.log(2); } var a; foo(); // 2 a = true; if (a) { } else { }3. 總結(jié)
我們習(xí)慣將 var a = 2; 看作一個(gè)聲明,而實(shí)際上 JavaScript 引擎并不這么認(rèn)為。它將 var a; 和 a = 2; 當(dāng)作兩個(gè)多帶帶的聲明,第一個(gè)是編譯階段的任務(wù),而第二個(gè)則是執(zhí)行階段的任務(wù)。
這意味著無(wú)論作用域中的聲明出現(xiàn)在什么地方,都將在代碼本身被執(zhí)行前首先被處理(預(yù)編譯)。可以將這個(gè)過(guò)程想象成所有的聲明(變量和函數(shù))都會(huì)被“移動(dòng)”到各自的作用域的最頂端,這個(gè)過(guò)程叫作提升。
歡迎關(guān)注我的公眾號(hào)文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81659.html
摘要:我們繼續(xù),這次來(lái)聊聊類。,編寫代碼角色基類判斷角色是否死亡升級(jí)受到傷害攻擊普通攻擊攻擊了造成了點(diǎn)傷害攻擊,有概率是用必殺攻擊必殺攻擊使用必殺攻擊了造成了點(diǎn)傷害游戲世界權(quán)利的游戲初始化英雄怪物集合,模擬簡(jiǎn)單的游戲關(guān)卡。 OK, 我們繼續(xù),這次來(lái)聊聊類。 內(nèi)有 Jon Snow大戰(zhàn)異鬼, ? 熟悉后端的朋友們對(duì)類肯定都不陌生,如下面一段PHP的代碼: class Human { pr...
摘要:延伸閱讀學(xué)習(xí)與實(shí)踐資料索引與前端工程化實(shí)踐前端每周清單半年盤點(diǎn)之篇前端每周清單半年盤點(diǎn)之與篇前端每周清單半年盤點(diǎn)之篇 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(hào)(ID:frontshow),及時(shí)獲取前端每周清單;本文則是對(duì)于半年來(lái)發(fā)布的前端每周清單...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)。因?yàn)槲倚睦砗芮宄业哪繕?biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來(lái)的學(xué)習(xí)計(jì)劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...
摘要:斯坦福宣布使用作為計(jì)算機(jī)課程的首選語(yǔ)言近日,某位有年教學(xué)經(jīng)驗(yàn)的斯坦福教授決定放棄,而使用作為計(jì)算機(jī)入門課程的教學(xué)語(yǔ)言。斯坦福官方站點(diǎn)將它們新的課程描述為是最流行的構(gòu)建交互式的開發(fā)語(yǔ)言,本課程會(huì)用講解中的實(shí)例。 前端每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優(yōu)化服務(wù)端渲染,優(yōu)秀React界面框架合集 為InfoQ中文站特供稿件,首發(fā)地址為...
閱讀 787·2021-11-12 10:36
閱讀 3363·2021-09-08 10:44
閱讀 2739·2019-08-30 11:08
閱讀 1393·2019-08-29 16:12
閱讀 2668·2019-08-29 12:24
閱讀 889·2019-08-26 10:14
閱讀 676·2019-08-23 18:32
閱讀 1160·2019-08-23 17:52