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