3.構造函數javascript
構造函數除了經過指定的模式建立對象之外,還有另一個好處——它可以自動設置新建立對象的原型對象,這個原型對象存儲在構造函數的Prototype屬性中。html
例如,咱們使用構造函數來建立對象b和c,以下java
(此圖看起來很是複雜,我會着重來分析的,容我翻譯完本節回頭再寫^_^)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
請注意,每一個函數能夠產生無數個上下文,由於每次函數被調用(即便函數被本身調用,即嵌套函數)都會產生一個新的上下文,並擁有當前上下文的狀態。
全部的ECMAScript程序運行時經過執行上下文(EC)棧來展示,棧最頂部的上下文是激活(正在運行着)的上下文,以下爲一個執行上下文棧,
當程序開始時,先進入全局執行上下文(Global EC),它是棧中最底部也是第一個元素。全局代碼進行一些初始化,建立必要的對象和函數 。在全局上下文執行的時候,它會激活其它上下文(函數調用),並將新元素壓入棧。初始化之後,則等待一些事件來觸發函數,從而進入新的上下文中。下圖展示了函數上下文的變化狀況,棧從上下文EC1進入和退出全局上下文的過程,
如咱們所說,每一個棧中的執行上下文都是一個對象,讓咱們來看看它的結構以及上下文須要哪些狀態來運行代碼。
5.執行上下文
執行上下文抽來來講就是一個簡單對象。每個執行上下文都有一些屬性的集合(上下文狀態),用來跟蹤相關代碼的執行過程。下圖即爲上下文的結構圖
變量對象
變量對象是跟上下文有關的數據的範圍,這個對象存儲了在上下文中定義的變量、函數聲明
注意,函數表達式(對比函數聲明)不包括在變量對象之中
變量對象是一個抽象的概念,在不一樣的上下文類型中,會使用不一樣的對象。例如,在全局上下文中,變量對象就是全局對象自己(這就是爲何咱們可以經過全局對象的屬性名來引用全局變量),以下:
再一次看到,函數baz做爲一個函數表達式沒有包括在變量對象中。這就是爲何在外部訪問函數自身時產生引用錯誤。
注意,相對於其餘語言(如C++),在ECMAScript只有函數會建立一個新的範圍。變量和在函數內定義的內部函數在外部不可見,不會污染全局對象變量。
使用eval也會進入一個新的eval執行上下文,而後,eval使用全局變量對象或者調用eval的變量對象(例如調用eval的某個函數)
函數和其變量對象是什麼樣的?在函數上下文中,變量對象表現爲一個激活對象(activation object)
激活對象
當一個函數被調用時,一個特別的激活對象就會被建立。形式參數和一個特別的argument對象(它是對形參的一種映射,能夠經過索引尋找指定的形參)將會用來對它賦值,而後激活對象就能夠做爲一個函數的變量。例如,一個函數的變量對象不只存儲了函數的變量和函數聲明,它也存儲了形式參數和argument對象,它就叫作激活對象。例以下面這個例子:
函數表達式baz仍然不包括在變量/激活對象中。
咱們繼續進行下一節。如咱們所知,在ECMAScript中咱們使用內部函數,這些內部函數可能引用父函數或者全局函數的變量,就像上文提到的原型鏈,咱們給做用域對象起名爲做用域鏈。
做用域鏈
咱們做用域鏈是一個對象列表,它用來尋找出如今上下文代碼中的特定符號。