原文出處 原創作者: abruzzi
原文圖文並茂,很好的說明了原型鏈的原理,在這里感謝原文作者把文章寫的那么通俗易懂。
原型對象
每個javascript對象都有一個原型對象,這個對象在不同的解釋器下的實現不同。比如在firefox下,每個對象都有一個隱藏的__proto__屬性,這個屬性就是“原型對象”的引用。
原型鏈
由於原型對象本身也是對象,根據上邊的定義,它也有自己的原型,而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個就是原型鏈,JavaScritp引擎在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找,如果找到,直接返回值,如果整個鏈都遍歷且沒有找到屬性,則返回undefined.原型鏈一般實現為一個鏈表,這樣就可以按照一定的順序來查找。
示例1
var base = {
name : "base",
getInfo : function(){
return this.name;
}
}
var ext1 = {
id : 0,
__proto__ : base
}
var ext2 = {
id : 9,
__proto__ : base
}
print(ext1.id);
print(ext1.getInfo());
print(ext2.id);
print(ext2.getInfo());
結果
0 base 9 base
圖1

可以看到,當執行ext1.id時,引擎在ext1對象本身中就找到了id屬性,因此返回其值0,當執行ext1.getInfo時,ext1對象中沒有找到,因此在其原型對象base中查找,找到之后,執行這個函數,得到輸出”base”。
我們將上例中的ext1對象稍加修改,為ext1對象加上name屬性:
示例2
var base = {
name : "base",
getInfo : function(){
return this.name;
}
}
var ext1 = {
id : 0,
name : "ext1",
__proto__ : base
}
print(ext1.id);
print(ext1.getInfo());
結果
0 ext1
這個運行效果同樣驗證了原型鏈的運行機制:從對象本身出發,沿着__proto__查找,直到找到屬性名稱相同的值(沒有找到,則返回undefined)。
我們對上例再做一點修改,來更好的演示原型鏈的工作方式:
示例3
var base = {
name : "base",
getInfo : function(){
return this.id + ":" + this.name;
}
}
var ext1 = {
id : 0,
__proto__ : base
}
print(ext1.getInfo());
結果
0:base
應該注意的是,getInfo函數中的this表示原始的對象,而並非原型對象。上例中的id屬性來自於ext1對象,而name來自於base對象。這個特性的機制在10.3小節再做討論。如果對象沒有顯式的聲明自己的”__proto__”屬性,這個值默認的設置為Object.prototype,而Object.prototype的”__proto__”屬性的值為”null”,標志着原型鏈的終結。
構造器
我們在來討論一下構造器,除了上邊提到的直接操作對象的__proto__屬性的指向以外,JavaScript還支持構造器形式的對象創建。構造器會自動的為新創建的對象設置原型對象,此時的原型對象通過構造器的prototype屬性來引用。
我們以例子來說明,將Task函數作為構造器,然后創建兩個實例task1, task2:
示例4
function Task(id){
this.id = id;
}
Task.prototype.status = "STOPPED";
Task.prototype.execute = function(args){
return "execute task_"+this.id+"["+this.status+"]:"+args;
}
var task1 = new Task(1);
var task2 = new Task(2);
task1.status = "ACTIVE";
task2.status = "STARTING";
print(task1.execute("task1"));
print(task2.execute("task2"));
結果
execute task_1[ACTIVE]:task1 execute task_2[STARTING]:task2
構造器會自動為task1,task2兩個對象設置原型對象Task.prototype,這個對象被Task(在此最為構造器)的prototype屬性引用,參看下圖中的箭頭指向。
圖2

由於Task本身仍舊是函數,因此其”__proto__”屬性為Function.prototype, 而內建的函數原型對象的”__proto__”屬性則為Object.prototype對象。最后Obejct.prototype的”__proto__”值為null。
