深入理解javascript系列(四):變數物件(VO)1

Panthon發表於2018-06-12

在系列(三)中,我們提到了“執行上下文的生命週期”,現在我們來回顧一下:

當一個函式被呼叫時,一個新的執行上下文就會被建立。一個執行上下文的生命週期大致可以分為兩個階段:建立階段和執行階段。

建立階段

在這個階段,執行上下文會分別建立變數物件,確認作用域鏈,以及確定this的指向。

執行階段

建立階段之後,就開始執行程式碼,這個時候會完成變數賦值、函式引用、以及執行其它可執行的程式碼。

我們在系列(二)中也曾提到過變數物件(Variable Object),我們在javascript程式碼中宣告的所有變數都儲存在變數物件中,除此之外,變數物件中還有可能包含以下內容:

1、函式的所有引數。

2、當前上下文中的所有函式宣告(通過function宣告的函式)。

3、當前上下文中的所有變數宣告(通過var宣告的變數)。

說了這麼多次變數物件,那到底什麼才是變數物件了?希望通過本次系列的學習,我能理解變數物件這個概念。

4.1  建立過程

變數物件的建立,依次經歷了以下幾個過程。

1. 在Chrome瀏覽器中,變數物件會首先獲得函式的引數變數及其值;在FireFox瀏覽器中,是直接將引數物件argument儲存在變數物件中。

2. 依次獲取當前上下文中所有宣告的函式,也就是使用function關鍵字宣告的函式。在變數物件中會以函式名建立一個屬性,屬性值為指向該函式所在的記憶體地址引用。如果函式名的屬性已經存在,那麼該屬性的值會被新的引用覆蓋。

3.依次獲取當前上下文中的變數宣告,就是使用var關鍵字宣告的變數。每找到一個變數宣告,就在變數物件中就以變數名建立一個屬性,屬性值為undefined,是undefined的哦。如果該變數名的屬性已經存在,為了防止同名函式被修改為undefined,則會直接跳過,原屬性值不會被修改。

注意:ES6支援新的變數宣告方式let/const,規則與ar完全不同,他們是在上下文的執行階段開始執行的,避免了“變數提升”帶來的一系列問題,因此這裡暫時先不介紹。

知道了上面的規則後,我們來思考一個問題,當我們執行以下程式碼時,具體的執行過程是怎麼樣的呢?

var a = 30;複製程式碼

首先,上下文的建立階段會先確認變數物件,變數物件會獲取帶var關鍵字變數,他會以變數名a建立一個屬性,屬性值為undefined。因此第一步是:

var a = undefined;複製程式碼

上下文的建立階段完畢後,開始進入執行階段,在執行階段需要完成變數賦值的工作,因此第二步是:

a = 30;複製程式碼

需要注意的是,這兩步分別是執行上下文的建立階段和執行階段完成的。因此var a = undefined 這一步其實是提前到了比較早的地方去執行了(我們把這一過程叫做變數提升(Hoisting))。下面通過一個簡單的例子來證明。

深入理解javascript系列(四):變數物件(VO)1

結合之前的理解,這個例子實際的執行順序應該是:

//這個執行上下文 變數物件的建立階段
var a = undefined;

//執行階段
a = 30;複製程式碼

我們在4.1的建立過程的規則中,可以看出,在變數物件的建立過程中,函式宣告的執行優先順序會高於變數宣告,而且同名函式會覆蓋函式與變數,但是同名的變數並不會覆蓋函式。

但是在上下的執行階段,同名的函式會被變數重新賦值。

如果您覺得上面的話,不是那麼好理解,我們通過一個例項來感受一下

深入理解javascript系列(四):變數物件(VO)1

請仔細看這段程式碼.

感受完了上面這張圖,我們來看下程式碼的執行順序:

//本執行上下文的變數物件的建立階段
function fn() { console.log('fn') };
function fn() { console.log('new fn') };
function a() { console.log('a is func') };
var a = undefined;
var fn = undefined;

//執行階段
a  = 20;
console.log(a); //20;
fn(); //'new fn';
fn = 'i am not a func';
console.log(fn);

複製程式碼

根據輸出結果可以證明,在建立階段,後建立的函式fn會覆蓋前面建立的函式fn,但是變數fn並沒有在建立階段覆蓋函式fn(注意我說的是建立階段)。而在執行階段,a與fn的重新賦值導致他們發生了變化。

深入理解javascript系列(四):變數物件(VO)1

請注意這張圖,他為什麼會報錯?

或許您能一眼看出來,因為fn重新賦值覆蓋後,fn就不是一個函式的引用地址了。fn()肯定會報錯。

那麼問題就在這裡,為什麼變數物件的建立階段。變數fn = undefined 不會覆蓋之前的已宣告的函式fn了?如果您知道,希望您能在評論區下您的答案。

記住:執行上下文的兩個階段、變數物件建立的3條規則。

這些都是我以往的學習筆記。如果您看到此筆記,希望您能指出我的錯誤。有這麼一個群,裡面的小夥伴互相監督,堅持每天輸出自己的學習心得,不輸出就出局。希望您能加入,我們一起終身學習。歡迎新增我的個人微訊號:Pan1005919589


相關文章