前言都不知道要打什麼,就直接了當吧,今天要來簡單聊聊 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給值