前言都不知道要打什麼,就直接了當吧,今天要來簡單聊聊 Hoisting
、Scope Chain
。
本文開始
我們先來看這段程式碼
console.log(a);
var a = 123;
JS 是同步執行,也代表程式碼會由上而下一行一行執行,那上面的範例中,我們在宣告 a 之前就試圖印出它,這樣應該會顯示 a is not defined 吧!
但人參就是這樣,總是會有一些意料之外的事發生
執行結果是 undefined(有宣告,但沒有值),而不是 not defined(沒有宣告)。
這個結果在 function
也是一樣,可以在宣告式上面呼叫(執行)它。
該如何解釋這個現象呢? 這是今天要講的主題之一 hoisting
。
JS 在執行時大致分為兩個階段
this
window
)在第一個環節中,所宣告的變數、函數就會被放到記憶體,所以才沒有出現 not defined 的錯誤。
流程轉為程式碼可以理解成這樣:
var a;
console.log(a);
a = 123;
在我們定義變數的過程中,可以分成宣告
和給值
的兩個過程,宣告
的變數會在逐行執行程式前,先被執行並儲存在記憶體(hoist),給值
的內容則是在 hoist 之後,逐行執行程式時,才會被執行。
所以程式一開始執行的時候,就已經把 var a
的宣告
存在記憶體中了,但是還沒把值賦予給 a
這個變數,所以 a
在印出時 undefined 的結果。
記得昨天講的 ES6 新增的兩個變數宣告方式嗎,let
& const
。
它們提供了更嚴謹的撰寫方式,其中有一個特性就是禁止在宣告前使用
。
延續昨天的變數作用範圍,接下來要提提 Scope Chain
。
用 var 所宣告的變數,作用範圍是在當時所在環境(函數內),而不使用 var 直接指定值而建立的變數,則是全域物件(window)上的一個屬性,也就全域範圍。
在 JS 中有稱作 Scope Chain
(範圍鏈)的特性,JS 在使用變數時,會遵循著 Scope Chain
一層一層往外找,若函數內找不到,則往外找。
這個很好理解,func
內沒有 myVar
這個變數,所以向外一層去找看看有沒有這個變數,而它也找到了。
var myVar = "outer";
function func(){
console.log(myVar); // outer
myVar = "inner";
}
func();
console.log(myVar); // inner
那這個呢,func
中的 myVar
會印出什麼?
function func(){
console.log(myVar); // ?
}
function fund(){
var myVar = "inner";
func();
}
fund();
var myVar = "outer";
答案是 func
裡面印出的 myVar
是 outer 而不是 inner!
這邊要補充一下,JS 屬於同步執行,所以執行 function 時,會依執行順序把 function 丟到 stack 中,而每個 function 在執行時,都會建立屬於它自己的執行環境,也就是環境中的變數是獨立存在該環境中,不會互相汙染。
看看這段程式碼,Q1
、Q2
=?
var myVar = "outer";
function func(){
var myVar = "inner";
console.log(myVar); // Q1?
}
func();
console.log(myVar); // Q2?
聰明的你應該答對了,Q1
: innerQ2
: outer
儘管在 func
外面已經宣告 myVar
,而在 func
又宣告了 myVar
,這兩個變數看起來是一樣的,但其實不然。func
在執行時會產生新的執行環境,而其宣告的 var myVar = "inner"
只存在於這個新產生的執行環境,和外部(全域)的 var myVar = "outer"
八竿子打不著。
回到最開始的程式碼
var myVar = "outer";
function func(){
console.log(myVar); // outer
myVar = "inner";
}
func();
console.log(myVar); // inner
雖然 myVar
是屬於外部(全域)的變數,它並不存在(活動)於 func
內,func
中之所以能夠印出它的值,是因為 Scope Chain
的特性,當內層的環境找不到這個變數,就會往外去找。
理解之後看看這段程式碼,請問 Q3
會印出什麼呢?
function func(){
var myVar = "inner";
}
func();
console.log(myVar); // Q3=?
答案是 myVar is not defined
!
因為 Scope Chain
的特性是由內往外找,並不會也不能往內找。所以無法使用函數 func
內的變數 myVar
。
注意:內層函式都可以存取外部函式的變數;但外部不能存取內部
多層函數也是遵循一樣的規則,Q4
=?
var myVar = "global";
function outer(){
var myVar = "outer";
function inner(){
console.log(myVar); // Q4=?
}
inner();
}
outer();
Ans:Q4
= outer
function inner 的執行環境找不到 myVar,所以向外找到了 outer 中的 myVar,因為找到了,所以不會再往外去找到 myVar = "global"
。
那今日的分享就到這邊,明天見
文中有個地方不對
function func(){
console.log(myVar); // ?
}
function fund(){
var myVar = "inner";
func();
}
fund();
var myVar = "outer";
執行結果應該是undefined而不是outer,因為程式是先執行fund()後才對myVar給值