首頁  >  文章  >  web前端  >  JavaScript作用域鏈其二:函數的生命週期

JavaScript作用域鏈其二:函數的生命週期

黄舟
黄舟原創
2016-12-20 16:21:281291瀏覽

函數的的生命週期分為創建和激活階段(調用時),讓我們詳細研究它。

函數創建

眾所周知,在進入上下文時函數聲明放到變數/活動(VO/AO)物件中。讓我們看看在全域上下文中的變數和函數宣告(這裡變數物件是全域物件自身,我們還記得,對吧?)

var x = 10;
  
function foo() {
  var y = 20;
  alert(x + y);
}
  
foo(); // 30

在函數啟動時,我們得到正確的(預期的)結果-30 。但是,有一個很重要的特點。

此前,我們僅談到有關當前上下文的變數物件。這裡,我們看到變數「y」在函數「foo」中定義(意味著它在foo上下文的AO中),但是變數「x」並未在「foo」上下文中定義,相應地,它也不會加入到“foo”的AO中。乍一看,變數“x”相對於函數“foo”根本就不存在;但正如我們在下面看到的——也僅僅是“一瞥”,我們發現,“foo”上下文的活動物件中僅包含一個屬性——“y”。

fooContext.AO = {
  y: undefined // undefined – 进入上下文的时候是20 – at activation
};

函數「foo」如何存取到變數「x」?理論上函數應該能存取一個更高一層上下文的變數物件。實際上它正是這樣,這種機制是透過函數內部的[[scope]]屬性來實現的。

[[scope]]是所有父變數物件的層級鏈,處於目前函數上下文之上,在函數建立時存於其中。

注意這重要的一點——[[scope]]在函數創建時被儲存——靜態(不變的),永遠永遠,直到函數銷毀。即:函數可以永不調用,但[[scope]]屬性已經寫入,並儲存在函數物件中。

另外一個需要考慮的是-與作用域鏈對比,[[scope]]是函數的一個屬性而不是上下文。考慮到上面的例子,函數「foo」的[[scope]]如下:

foo.[[Scope]] = [
  globalContext.VO // === Global
];

舉例來說,我們用通常的ECMAScript 陣列展現作用域和[[scope]]。

繼續,我們知道在函數呼叫時進入上下文,這時候活動物件被創建,this和作用域(作用域鏈)被確定。讓我們詳細考慮這一刻。

函數啟動

正如在定義中說到的,進入上下文創建AO/VO之後,上下文的Scope屬性(變數查找的一個作用域鏈)作如下定義:

Scope = AO|VO + [[Scope]]

上面程式碼的意思是:活動對象是作用域數組的第一個對象,即加入到作用域的前端。

Scope = [AO].concat([[Scope]]);

這個特點對於標示符解析的處理來說很重要。標示符解析是一個處理過程,用來決定一個變數(或函數宣告)屬於哪個變數物件。

這個演算法的回傳值中,我們總是有一個引用型,它的base元件是對應的變數物件(或若未找到則為null),屬性名稱元件是向上尋找的標示符的名稱。引用類型的詳細資訊在後面已討論。

標識符解析過程包含與變數名稱對應屬性的查找,即作用域中變數物件的連續查找,從最深的上下文開始,繞過作用域鏈直到最上層。

這樣一來,在向上查找中,一個上下文中的局部變數較之於父作用域的變數擁有較高的優先權。萬一兩個變數有相同的名稱但來自不同的作用域,那麼第一個被發現的是在最深作用域中。

我們用一個稍微複雜的例子來描述上面講到的這些。

var x = 10;
  
function foo() {
  var y = 20;
  
  function bar() {
    var z = 30;
    alert(x +  y + z);
  }
  
  bar();
}
  
foo(); // 60

對此,我們有如下的變數/活動對象,函數的的[[scope]]屬性以及上下文的作用域鏈:

全局上下文的變數對像是:

globalContext.VO === Global = {
  x: 10
  foo: <reference to function>
};

在「foo」創建時, 「foo」的[[scope]]屬性是:

foo.[[Scope]] = [
  globalContext.VO
];

在「foo」活化時(進入上下文),「foo」上下文的活動物件是:

fooContext.AO = {
  y: 20,
  bar: <reference to function>
};

「foo」上下文的作用域鏈為:

fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:
  
fooContext.Scope = [
  fooContext.AO,
  globalContext.VO
];

內部函數「bar」建立時,其[[scope]]為:

bar.[[Scope]] = [
  fooContext.AO,
  globalContext.VO
];

在「bar」啟動時,「bar」上下文的活動物件為:

barContext.AO = {
  z: 30
};

「bar」上下文的作用域鏈為:

barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.:
  
barContext.Scope = [
  barContext.AO,
  fooContext.AO,
  globalContext.VO
];

對「x」、「y」、「z」的識別碼解析如下:

- "x"
-- barContext.AO // not found
-- fooContext.AO // not found
-- globalContext.VO // found - 10
 
- "y"
-- barContext.AO // not found
-- fooContext.AO // found - 20
 
- "z"
-- barContext.AO // found - 30

 以上就是JavaScript作用域鏈其二:函數的生命週期的內容,更多相關內容請關注PHP中文網(www .php.cn)! 


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn