範圍鍊在 JavaScript 一堆奇奇怪怪的觀念中算是簡單好懂的,簡單來說就是:
『函示內沒有對應的變數、常數或是函示時,他會往外層尋找』
這邊一樣用程式碼來說明。
function playGame(){
const gaming = 'PS5'
console.log(`我要用 ${gaming} 玩遊戲`)
}
playGame() //我要用 PS5 玩遊戲
上面這段 gaming
在函示內,因此能正確顯示,這段好像廢話,不過接下來我們試者把 gaming
移到外層全域看看結果如何:
const gaming = 'PS5'
function playGame(){
console.log(`我要用 ${gaming} 玩遊戲`)
}
playGame() //我要用 PS5 玩遊戲
結果是一樣的,上面 playGame()
函式中因為本身沒有 gaming
常數,因此他會到外層也是全域 (window) ,來獲得 gaming
常數的資料。
接下來便是在函示內外都新增兩個 gaming
來看看結果如何
const gaming = 'PS5'
function playGame(){
const gaming = 'Switch'
console.log(`我要用 ${gaming} 玩遊戲`)
}
playGame() //我要用 Switch 玩遊戲
所以結果就是當函示內有相關資料時,變不會往外層尋找,而是直接使用函示內資料。
(因為 const
、 let
提升的狀況不一樣,這邊範例先改為使用 var
, const
、 let
會到介紹他們的章節再來提及)
JavaScript 實際編譯並執行程式碼時,會分為兩種階段:
等創造階段執行完畢,才會進入執行階段,創造階段有幾個重點:
而執行階段的重點則是:
XX()
。這邊使用上面程式碼,並稍微調一下順序就可驗證上面提到的創造階段。
console.log(gaming) // undefined
var gaming = 'PS5'
playGame() //我要用 PS5 玩遊戲
function playGame(){
console.log(`我要用 ${gaming} 玩遊戲`)
}
console.log(gaming)
這段會因為變數被提升,但尚未賦值因此是 cosnole 會回傳 undefined
playGame()
則是 function playGame()
函示會因為提升效果,而先比被呼叫函示的 playGame()
建立,因此 console 可以正確顯示。
因為提升這個 特性 會讓上面的程式碼,在編譯順序變為:
// 創造階段
//函示優先提升
function playGame(){
console.log(`我要用 ${gaming} 玩遊戲`)
}
var gaming
// 執行階段
console.log(gaming) // undefined
gaming = 'PS5'
playGame() //我要用 PS5 玩遊戲
最後要說的是,Hoisting 只是概念,只是幫助我們理解,程式碼在編譯時會這麼做,但他仍只是一種概念, 這點 MDN 文件也有說明到:
提升(Hoisting)是在 ECMAScript® 2015 Language Specification 裡面找不到的專有名詞。它是一種釐清 JaveScript 在執行階段內文如何運行的思路(尤其是在創建和執行階段)。