Js 原型對象與原型鏈(轉)


原文出處 原創作者: 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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM