--- title: 'JS 核心 2 - 提升 (hoisting)' tags: JS 核心, Javascript, 提升, hoisting description: 2021/02/03 --- JS 核心 -- 提升 (hoisting) === ## 提升 (hoisting) ### 先來說明執行環境是如何解析程式碼的 * <span class="green">創造環境:var a; </span>記憶體會先把 a 記起來,此時若去查找 a 會出現 undefined。 * <span class="green">執行(賦值): a = 1;</span> 此時才會把 1 這個值賦予給 a。  ### 函式陳述式在創造階段就會優先載入,可以運行 使用**函式陳述式**在宣告變數時,在創造階段就會把整個函式給載入進來。 這樣做的優點是:可以在程式碼宣告該函式之前使用它。  :bulb: **範例 (函式陳述式)** : 函式在創造階段時就已經載入,可以正常運行。像這種執行函式先而宣告函式在後,稱為函式提升。 ``` callName(); function callName() { console.log('呼叫小明'); // 呼叫小明 } ``` 拆解 : 函式陳述式在創造階段,記憶體空間已包含函式完整內容了。 ```typescript= //創造階段 function callName() { console.log('呼叫小明'); } //執行 callName(); ``` :bulb: **範例 (函式表達式)** callName( ); 可以在表達式之後呼叫 ``` var callName = function(){ console.log('呼叫小明'); } callName(); // 呼叫小明 ``` 若 callName( ); 在表達式之前呼叫,會跳錯 ``` console.log(callName); // undefined callName(); // callName is not a function var callName = function(){ console.log('呼叫小明'); } ``` 拆解 ```typescript= // 創造階段 var callName; // 先準備一個記憶體空間,但沒有值,屬於 undefined // 執行 callName = function(){ // 執行時,才把 function() 賦予到此變數上 console.log('呼叫小明'); // callName 尚在未賦值階段,所以會跳錯 } callName(); ``` :bulb: **範例 (創造階段,函式優先)** ``` // 陳述式 與 表達式 function callName() { console.log('呼叫小明 1'); } var callName = function () { console.log('呼叫小明 2'); } callName(); // 呼叫小明 2 ``` 拆解 1. 函式callName被往前移,在創造階段時函式callName先被分配到記憶體中的一個位置,且函式的陳述式已被載入。 2. 接著才是宣告變數callName。 3. 進入創造階段,callName的值被新的函式陳述式覆蓋掉,因此結果會是呼叫小明2。 ```typescript= // 創造階段 function callName() { console.log('呼叫小明 1'); } var callName; // 執行,賦予值 callName = function () { console.log('呼叫小明 2'); } // 此時變數 callName 被函式覆蓋過去 callName(); // 呼叫小明 2 ``` 試著把 (8行) callName( ); 往前移 ```typescript= // 創造階段 function callName() { console.log('呼叫小明 1'); } var callName; // 執行,賦予值 callName(); // 呼叫小明 1 (因為變數重複宣告是沒有用的) callName = function () { console.log('呼叫小明 2'); } ``` ### 變數 vs 函式的拉升 變數與函式的拉升的不同之處在於,變數的拉升只有宣告部份,而函式的拉升是整個函式,因此函式在宣告前是可以執行的。 :bulb: **範例** ``` callName(); // undefined function callName() { console.log(Ming); } var Ming = '小明'; ``` 拆解 ``` // 創造階段 function callName() { console.log(Ming); } var Ming; // 執行,賦予值 callName(); // 目前此函式所取的值是全域變數的Ming,此時Ming尚未被賦予值,所以為undefined Ming = '小明'; ``` ### 若同時有多個函式同名,則後面的會覆寫前面的宣告 :bulb: **範例** ``` function callName() { console.log('小明'); } callName(); function callName() { console.log('杰倫'); } callName(); ``` 拆解 : 兩個函式名稱都相同,後面會覆蓋前面的 ; 執行時只會取到 "杰倫" ``` // 創造階段 function callName() { console.log('小明'); } function callName() { console.log('杰倫'); } // 執行,賦予值 callName(); // 杰倫 callName(); // 杰倫 ``` ### 若函式和變數同名,則函式會優先 範例如下,同名函式 foo 和變數 foo,由於函式優先,因此 foo() 得到 1 而非 undefined() 的結果 TypeError。 ``` foo(); // 1 var foo; function foo() { console.log(1); } foo = 2; ``` 拆解 ``` // 創造階段 function foo() { console.log(1); } // 執行,賦予值 foo(); // 1 foo = 2; ``` :bulb: **範例** ``` whosName(); function whosName() { if (name) { name = '杰倫'; } } var name = '小明'; console.log(name); // 小明 ``` 拆解 ``` // 創造階段 function whosName() { // whosName() 因函式優先被提升到最上方 if (name) { name = '杰倫'; } } var name; // 宣告一個全域變數name,此時尚未被賦於值(顯示undifined) // 執行,賦予值 whosName(); // whosName();在進入if判斷式之前,並沒有宣告變數name,因此向外尋找。 // 此時全域變數name還未被賦予值,if判斷結果為false,函式執行中斷並跳出。 name = '小明'; // name 被重新賦予值 小明 console.log(name); // 答案為小明 ``` ## :memo: 學習回顧 :::info * 變數與函式陳述式,在進入執行階段前,其實就已經完成宣告。這種<span class="red">「將變數宣告與函式宣告的動作,提升到程式碼最頂端」的行為,就是 Hoisting(提升)。</span> * 拉升是逐範疇的,在函式內宣告變數,不會被拉升到全域範疇而成為全域變數。 * 函式陳述式在創造階段就會優先載入,可以運行。 * 函式表達式不會被提升,因此若在函式表達式之前呼叫,它就會出現錯誤訊息。 * 變數與函式的拉升的不同之處在於,變數的拉升只有宣告部份,而函式的拉升是整個函式,因此函式在宣告前是可被執行的。 * <span class="red">若函式和變數同名,則函式會優先;</span>若同時有多個函式同名,則後面的會覆寫前面的宣告。 ::: ## :+1: 相關參考文件 :::info [提升 Hoisting](https://medium.com/@yining1204/javascript-%E6%A0%B8%E5%BF%83%E7%AF%87-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-chap-11-%E6%8F%90%E5%8D%87-hoisting-3a7731c16c0) ::: <style> .red { color: red; } .green { color: green; } </style>
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up