2012年2月26日

JavaScript prototype chain


 


在js裡雖然沒有類, 但是有一個在類編程裡常用的的概念, Constructor構造器, 或者叫構造函數. 語法上Constructor跟普通的js函數沒有區別, 但是Constructor前面加上new關鍵字就可以產生新的對象.
那麼」new Constructor()」這樣調用時到底做了什麼事情呢?
1. js運行環境首先創建一個空對象
2. 把this變量指向這個對象
3. 把__proto__指向這個構造器的prototype屬性
4. 通過this把屬性和方法加在這個對象上
5. 最後會把this指向的對象return出來(當然你也可以顯示的return別的對象,這是情況跟我討論的有點不一樣)
這裡有2個概念比較重要, 第一是Constructor的prototype屬性, 它是Constructor的一個保持共享屬性和方法的對象. 二通過該Constructor生成的對象的__proto__屬性, 它指向了那個prototype對象. 對象的Constructor可以通過該對象的constructor屬性獲得.
我們定義一個簡單的Constructor Demo(), 然後通過他生成一個aDemo對象, 在Chrome的console裡把aDemo打出來看到的結果是這樣的,這是我們能很清楚的看出這種關係.
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){} } var aDemo = new DemoA(); console.log(aDemo); 
jsproto1
換一種寫法,
function DemoB(){ this.title = "this is a demo constructor"; } DemoB.prototype.f2 = function () {}; var bDemo = new DemoB(); console.log(bDemo); 
jsproto2
其實跟前面沒多大區別, 不過這一次f2()不在創建出來的對象上, 而是在對象的__proto__指向的構造器DemoB上. 這樣的好處是當使用DemoB創建很多對象時, 比較節省內存.
js的prototype搜索鏈 prototype Chain
js的運行環境是這樣使用構造器的prototype的. 當我們對第一個對象調用其方法時, js運行環境首先看, 這個對象本身有沒有這個方法, 如果有就直接調用. 如果沒找到, 就查找這個對象的__proto__屬性指向的對象(就是前面說的該對象的構造器的prototype屬性), 如果有該方法就調用, 沒有繼續查找__proto__屬性指向對象的__proto__屬性指向的對象, 以此類推. 直到最後, 找到js所有的對象都會指向默認指向的Object(算是js的根對象吧). 所以如果我們給Object上增加個方法, 所以的js對象都能使用.
由於js有對對象函數的prototype chain搜索的特性, 我們可以利用這點來模擬類似其他語言裡類(class)的效果. 如:
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){} } function DemoB(){ this.title = "this is b demo constructor"; this.f2 = function(){} } DemoB.prototype = new DemoA(); var bDemo = new DemoB(); console.log(bDemo); 
jsproto3
這時我們通過bDemo對象, 不但能調用bDemo上的DemoB的f2()方法, 也能__proto__同對對prototype的搜索, 在bDemo上直接調用DemoA的f1()方法.
function inherit(Child, Parent) { Child.prototype = new Parent(); } 
由於這種寫法在語法上太囉嗦, 不容易理解, 我可以定義一個這樣的函數.
這樣我們前面的例子可以寫成這樣, 效果是一樣的:
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){} } function DemoB(){ this.title = "this is b demo constructor"; this.f2 = function(){} } inherit(DemoB, DemoA); var bDemo = new DemoB(); console.log(bDemo); 

沒有留言:

ShareThis