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