動態語言的一點小小小小小小小小小特性 - hoist篇

今天我要來講動態語言的故事了,因為javascript最多,所以拿javascript來說明感覺好像蠻好講的,加上為了上色好看所以用了不少gist(羞

好,故事是這樣的,想必很多人寫javascript的時候都會遇到有重複命名的情況

為了簡單說明,先來看以下這段code:

從上面的情況正常會認為var foo = 1function foo覆蓋了,所以var foo = 1應該會是無效的行為,但是實際上執行這段程式會得到下面的結果:

1

從上面的結果,我們可以知道,最後會被執行的是var foo = 1而不是function foo,這是為什麼呢?
參考一下MDN的說明:

Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top.

在interpreter的認知中,會先去掃一次所有的function scope的內容,並且將function都置於程式的最頂端,所以,經過interpreter的洗禮後,剛剛的程式會變成:

function foo因為interpreter的判定,而被移動到最上方,造成下面只要同名的變數都會被拉到var foo的數值裡面,程式會因為這樣造成無法預期的結果。

或許這樣沒什麼感覺,那來一段比較有情境的code吧!

在這段程式碼當中,我們做了三個動作

  1. 宣告我們的分數score
  2. 針對我們宣告的分數做加總,那因為也是分數,所以我們這裡也是用score
  3. 針對我們加總完分數進行印出

那執行到第三步驟的時候你可能會得到類似的訊息

console.log(score());
            ^

TypeError: score is not a function
    at Object.<anonymous> (/Users/michael/Downloads/test.js:12:13)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)
    at tryModuleLoad (module.js:417:12)
    at Function.Module._load (module.js:409:3)
    at Module.runMain (module.js:575:10)
    at run (bootstrap_node.js:352:7)
    at startup (bootstrap_node.js:144:9)
    at bootstrap_node.js:467:3

好了,到這裡瞭解問題在哪了吧?對於程式語言來說,function的命名一定是優先於variable的命名,這裡我們有一個專有名詞「hoist」,他主要是指說將宣告提升到頂端的行為,所以像是下面這種程式也是有效的:

上述的程式明明就是function在下面,但是由於hoist的特性,導致foo()也可以正確的被執行,那有人可能就會問啦:「我程式該如何跟我預期的行為一致?」

目前比較好的解法就是將function與variable移到同一層,使得function與variable有同樣的行為模式,例如:

將function宣告成變數後,到指定行程式才會被正確的執行,可以參考以下討論: http://stackoverflow.com/questions/336859/javascript-function-declaration-syntax-var-fn-function-vs-function-fn

以上介紹到這,開發中能儘量避掉這類的雷就努力避免吧~~