V8引擎實現標準ECMA-262(三)

推薦英文原址 ECMA-262

3.構造函數javascript

構造函數除了經過指定的模式建立對象之外,還有另一個好處——它可以自動設置新建立對象的原型對象,這個原型對象存儲在構造函數的Prototype屬性中。html

例如,咱們使用構造函數來建立對象b和c,以下java

[javascript]  view plain copy
  1. // 構造函數  
  2. function Foo(y) {  
  3.   // 經過指定模式建立對象,對象建立完後,就擁有了y屬性  
  4.   this.y = y;  
  5. }  
  6.    
  7. //Foo.prototype存儲了新建立對象的原型的引用,咱們可使用它來定義共享的屬性或方法   
  8. //以下這句,使全部新建立的對象有用共享的屬性 "x"  
  9. Foo.prototype.x = 10;  
  10.    
  11. // 全部新建立的對象有用共享的方法 "calculate"  
  12. Foo.prototype.calculate = function (z) {  
  13.   return this.x + this.y + z;  
  14. };  
  15.    
  16. // 使用Foo構造函數建立對象b和c,注意使用new調用構造函數建立對象  
  17. var b = new Foo(20);  
  18. var c = new Foo(30);  
  19.    
  20. // 調用繼承的方法,該方法爲兩者共享的,只有一份拷貝  
  21. b.calculate(30); // 60  
  22. c.calculate(40); // 80  
  23.    
  24.   
  25. console.log(  
  26.  //對象b和c原型相同  
  27.   b.__proto__ === Foo.prototype, // true  
  28.   c.__proto__ === Foo.prototype, // true  
  29.    
  30.  // Foo.prototype 會自動建立一個特別的屬性constructor指向構造函數Foo  
  31.  //對象b和c能夠經過代理找到此構造函數  
  32.   b.constructor === Foo, // true  
  33.   c.constructor === Foo, // true  
  34.   Foo.prototype.constructor === Foo // true  
  35.    
  36.   b.calculate === b.__proto__.calculate, // true  
  37.   b.__proto__.calculate === Foo.prototype.calculate // true  
  38.    
  39. );  
以上對象的關係以下圖所示,


此圖看起來很是複雜,我會着重來分析的,容我翻譯完本節回頭再寫^_^python

這張圖展現了每個對象都有一個對應的原型。構造函數Foo自身也有原型,該原型由Foo的屬性_ _proto_ _所指向,如圖Function.prototype,而Function.prototype自己又有原型,即Object.prototype。所以,Foo.prototype只是Foo的顯式屬性,它被對象b和c引用。ecmascript

正常狀況下,若是不考慮類的概念,咱們能夠把構造函數和原型統稱爲「類」。事實上,python的動態類就是利用屬性/方法手段來實現的。從這點上來看,python的類語法只是ECMAScript代理繼承的一種濃縮(syntactic sugar一詞翻譯ide

這個主題的詳細完整解釋放在了ES3 系列第7章,共有兩個部分:面向對象理論和ECMAScript實現。函數

如今,若是你有基本的面向對象的觀點,咱們來看看ECMAScript中運行時程序執行的實現。咱們也叫作程序執行上下文棧,其中的每一個抽象元素都由對象表示,實際上,ECMAScript時刻都透露着面向對象的思想。ui


4.執行上下文棧this

ECMAScript有三種類型的代碼:全局代碼、函數代碼和eval代碼。每種代碼在它的執行上下文中進行。全局上下文的實例只有一個,而函數和eval上下文卻有多個實例。每次函數或者eval調用都會進入相應的函數或者eval的執行上下文中,計算函數或者eval的代碼類型。(後面一句至關拗口,說白了就是每次函數調用前將一些狀態壓入棧中保存,從而在函數代碼的執行時,處在當前的函數執行的上下文內。懂得基本的彙編函數調用對理解這個大有益處。)spa

請注意,每一個函數能夠產生無數個上下文,由於每次函數被調用(即便函數被本身調用,即嵌套函數)都會產生一個新的上下文,並擁有當前上下文的狀態。

[javascript]  view plain copy
  1. function foo(bar) {}  
  2.   
  3. //調用同一個函數三次,會產生三個的上下文,每一個上下文擁有不一樣的狀態,例如參數bar值不同(不少狀況下不只僅與此)  
  4. foo(10);  
  5. foo(20);  
  6. foo(30);  
一個上下文激活另外一個上下文,那麼前者就叫作調用者,後者叫做被調用者。被調用者可能同一時間又是調用者(例如一個函數被全局上下文調用,該函數又調用內部函數)。當調用者調用被調用者,那麼調用者會暫停滋生的執行,並把控制流傳給被調用者。被調用者被壓入棧,變成一個運行(激活)的執行上下文,當被調用上下文結束,將會返回控制流給調用者,調用者再繼續執行直到結束(期間可能又激活其餘上下文)。被調用者返回return結果或者由於異常退出。一個非捕獲異常可能致使從多個上下文退出(出棧)。

全部的ECMAScript程序運行時經過執行上下文(EC)棧來展示,棧最頂部的上下文是激活(正在運行着)的上下文,以下爲一個執行上下文棧,

當程序開始時,先進入全局執行上下文(Global EC),它是棧中最底部也是第一個元素。全局代碼進行一些初始化,建立必要的對象和函數 。在全局上下文執行的時候,它會激活其它上下文(函數調用),並將新元素壓入棧。初始化之後,則等待一些事件來觸發函數,從而進入新的上下文中。下圖展示了函數上下文的變化狀況,棧從上下文EC1進入和退出全局上下文的過程,

如咱們所說,每一個棧中的執行上下文都是一個對象,讓咱們來看看它的結構以及上下文須要哪些狀態來運行代碼。

5.執行上下文

執行上下文抽來來講就是一個簡單對象。每個執行上下文都有一些屬性的集合(上下文狀態),用來跟蹤相關代碼的執行過程。下圖即爲上下文的結構圖

[javascript]  view plain copy
  1. <span style="font-family: 'Microsoft YaHei'; white-space: normal; background-color: rgb(255, 255, 255); ">除了這三種必要屬性外,執行上下文在實現上能夠有其它的狀態。咱們來看看這些重要屬性的細節。</span>  

變量對象

變量對象是跟上下文有關的數據的範圍,這個對象存儲了在上下文中定義的變量、函數聲明

注意,函數表達式(對比函數聲明)不包括在變量對象之中

變量對象是一個抽象的概念,在不一樣的上下文類型中,會使用不一樣的對象。例如,在全局上下文中,變量對象就是全局對象自己(這就是爲何咱們可以經過全局對象的屬性名來引用全局變量),以下:

[javascript]  view plain copy
  1. var foo = 10;  
  2.    
  3. function bar() {} // 函數聲明, FD  
  4. (function baz() {}); //函數表達式, FE  
  5.    
  6. console.log(  
  7.   this.foo == foo, // true  
  8.   window.bar == bar // true  
  9. );  
  10.    
  11. console.log(baz); // 引用錯誤, "baz" 未定義  
全局上下文變量對象(VO)會有下列屬性

再一次看到,函數baz做爲一個函數表達式沒有包括在變量對象中。這就是爲何在外部訪問函數自身時產生引用錯誤。

注意,相對於其餘語言(如C++),在ECMAScript只有函數會建立一個新的範圍。變量和在函數內定義的內部函數在外部不可見,不會污染全局對象變量。

使用eval也會進入一個新的eval執行上下文,而後,eval使用全局變量對象或者調用eval的變量對象(例如調用eval的某個函數)

函數和其變量對象是什麼樣的?在函數上下文中,變量對象表現爲一個激活對象(activation object)

激活對象

當一個函數被調用時,一個特別的激活對象就會被建立。形式參數和一個特別的argument對象(它是對形參的一種映射,能夠經過索引尋找指定的形參)將會用來對它賦值,而後激活對象就能夠做爲一個函數的變量。例如,一個函數的變量對象不只存儲了函數的變量和函數聲明,它也存儲了形式參數和argument對象,它就叫作激活對象。例以下面這個例子:

[javascript]  view plain copy
  1. function foo(x, y) {  
  2.   var z = 30;  
  3.   function bar() {} // FD  
  4.   (function baz() {}); // FE  
  5. }  
  6.    
  7. foo(10, 20);  
咱們有了函數上下文的另外一個對象。


                      

函數表達式baz仍然不包括在變量/激活對象中。

咱們繼續進行下一節。如咱們所知,在ECMAScript中咱們使用內部函數,這些內部函數可能引用父函數或者全局函數的變量,就像上文提到的原型鏈,咱們給做用域對象起名爲做用域鏈。

做用域鏈

咱們做用域鏈是一個對象列表,它用來尋找出如今上下文代碼中的特定符號。

相關文章
相關標籤/搜索