JS 原型以及原型鏈

 

    /**
     * 原型對象
     * 不管什麼時,只要建立一個新函數,就會根據一組特定的規則爲該函數建立一個 prototype 屬性
     * 這個屬性指向該函數的原型對象。默認狀況下,全部原型對象都會自動得到一個 constructor(構造函數)
     * 屬性,這個屬性包含一個指向 prototype 屬性所在函數的指針。當構造函數建立一個新實例後,
     * 該實例的內部包含一個指針[[Prototype]](內部屬性),指向構造函數的原型對象。
     */

    /**
     * 如:
     * 建立一個函數 A:function A(){}
     * function A(){} 會有一個屬性: prototype 屬性
     * function A(){} 的原型對象爲: A.prototype
     * function A(){} 的原型對象會自動得到一個 constructor 屬性: A.prototype.constructor
     * 那麼:
     * function A(){} 中的 prototype 屬性 ---(指向)---> A.prototype (function A(){} 的原型對象)
     * A.prototype.constructor (constructor屬性) ---(指向)---> function A(){} (函數 A)
     */
    function A(){}
    console.log(A.prototype.constructor==A);//true 可見原型對象中的 constructor 指向函數A

    /**
     * 如:
     * 使用 new 關鍵字結合 A() 函數來建立實例 obj
     * 則 obj 這個實例會有一個 [[Prototype]] 內部屬性指向 A 的原型對象 A.prototype
     * Firefox、Safari 和Chrome 在每一個對象上都支持一個屬性 __proto__ ,它就至關於 [[Prototype]]
     */

    var obj=new A();
    console.log(obj.__proto__==A.prototype);//true 可見實例中的 [[Prototype]] 指向函數原型 A.prototype
    
    A.prototype.name="guang";//給A的原型對象增長一個name:"guang"屬性
    A.prototype.sayName=function(){return this.name};//給A的原型對象增長一個sayName方法
    var obj=new A();
    console.log(obj.sayName());//guang

原型

 

    // 上圖展現了A 構造函數、A 的原型屬性以及A 現有的實例 obj之間的關係。
    // 在此,A.prototype 指向了原型對象,而A.prototype.constructor 又指回了A。
    // 原型對象中除了包含constructor 屬性以外,還包括後來添加的其餘屬性。A 的每一個實例—
    // 都包含一個內部屬性,該屬性僅僅指向了 A.prototype原型對象,而構造函數沒有直接的關係。
    // 雖然這 obj實例不包含屬性和方法,但卻可調用 A.sayName(),這是經過查找對象屬性的過程來實現的。

    // 每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具備給定名字的屬性。搜索首先
    // 從對象實例自己開始。若是在實例中找到了具備給定名字的屬性,則返回該屬性的值;若是沒有找到,
    // 則繼續搜索指針指向的原型對象,在原型對象中查找具備給定名字的屬性。若是在原型對象中找到了這
    // 個屬性,則返回該屬性的值。

    // 對於上面的例子而言,在調用A.sayName()的時候,會前後執行兩次搜索
    // 首先,解析器會問:「實例A 有sayName 屬性嗎?」答:「沒有。」而後,它繼續搜索,再
    // 問:「A 的原型有sayName 屬性嗎?」答:「有。」因而,它就讀取那個保存在原型對象中的函數。
    // 而這正是多個對象實例共享原型所保存的屬性和方法的基本原理。

 

 

    /**
     * 原型鏈
     * 每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。
     * 當一個原型對象是另外一個原型對象的實例時,該原型對象將包含一個指向另外一個原型的指針。相應地,另外一個原型中也包含着一個指向
     * 另外一個構造函數的指針。假如另外一個原型又是另外一個類型的實例,那麼上述關係依然成立,如此層層遞進,就構成了實例與原型的鏈條
     */

    function SuperType(){
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function(){
            return this.property;
    };
    function SubType(){
        this.subproperty = false;
    }
    //繼承了SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function (){
        return this.subproperty;
    };
    var instance = new SubType();
    console.log(instance.getSuperValue()); //true
      console.log(instance.constructor == SubType,instance.constructor == SuperType); //false true
//以上代碼定義兩個類型:SuperType、SubType。經過建立 SuperType的實例,原型對象 SubType.prototype 繼承了SuperType的屬性
//本質上是經過重寫原型對象實現繼承。如圖:

 

原型鏈

 

// 在上面的代碼中,並無使用SubType 默認提供的原型,而是給它換了一個新原型;這個新原型
// 就是SuperType 的實例。因而,新原型不只具備做爲一個SuperType 的實例所擁有的所有屬性和方法,
// 並且其內部還有一個指針,指向了SuperType 的原型。
// 最終結果就是這樣的:instance 指向SubType的原型, SubType 的原型又指向SuperType 的原型。
// getSuperValue() 方法仍然還在 SuperType.prototype 中,但property 則位於SubType.prototype 中。
// 這是由於property 是一個實例屬性,而getSuperValue()則是一個原型方法。既然SubType.prototype 如今是SuperType
// 的實例,那麼property 固然就位於該實例中了。此外,要注意instance.constructor 如今指向的
// 是SuperType,這是由於SubType 的原型指向了另外一個對象——>SuperType 的原型,而這個原型對象的constructor 屬性指向的是SuperType。
// 在經過原型鏈實現繼承的狀況下,搜索過程就得以沿着原型鏈繼續向上。就拿上面的例子來講,調用
// instance.getSuperValue()會經歷三個搜索步驟:1>搜索實例;2>搜索SubType.prototype;3>搜索SuperType.prototype,
// 4>最後一步纔會找到該方法。在找不到屬性或方法的狀況下,搜索過程老是要一環一環地前行到原型鏈末端纔會停下來。

 

// 事實上,由於全部引用類型默認都繼承了Object,而這個繼承也是經過原型鏈實現的。全部函數的默認原型都是Object 的實例,
// 所以默認原型都會包含一個內部指針,指向Object.prototype。這也正是全部自定義類型都會繼承toString()、valueOf()等
// 默認方法的根本緣由。對該例子來講完整的原型鏈應該以下:

 

完整的原型鏈

 

 

//內置函數及其原型鏈  查看大圖
// 理解函數的 prototype指向:看函數做爲構造函數時 new出來的實例是那種類型(是函數,仍是對象),這樣容易理解
內置函數及其原型鏈

 

//上述圖中的原型及原型鏈的關係圖中, 將 String 換成 Array、Number、Boolean、及其餘一些內置函數時,依然有相似的關係
//下面是與之相關的一些運行結果:

console.log("XXX");
console.log("String:",String);//String: function String() 內置構造函數
console.log("Function:",Function);//Function: function Function() 內置構造函數
console.log("Object:",Object);//Object: function Object() 內置構造函數
console.log("Array:",Array);//Array: function Array() 內置構造函數
console.log("Number:",Number);//Number: function Number() 內置構造函數
console.log("Boolean:",Boolean);//Boolean: function Boolean() 內置構造函數


console.log("XXX.prototype");
console.log("String.prototype:",String.prototype);//String.prototype: String { "" } 空字符串
console.log("Function.prototype:",Function.prototype);//Function.prototype: function () 空函數
console.log("Object.prototype:",Object.prototype);//Object.prototype: Object { … } 空對象
console.log("Array.prototype:",Array.prototype);//Array.prototype: Array [] 空數組
console.log("Number.prototype:",Number.prototype);//Number.prototype: Number { 0 } 數字零
console.log("Boolean.prototype:",Boolean.prototype);//Boolean.prototype: Boolean { false } 布爾值 false


console.log("XXX.prototype.constructor");
console.log("String.prototype.constructor:",String.prototype.constructor);
//String.prototype.constructor: function String() 內置構造函數
console.log("Function.prototype.constructor:",Function.prototype.constructor);
//Function.prototype.constructor: function Function() 內置構造函數
console.log("Object.prototype.constructor:",Object.prototype.constructor);
//Object.prototype.constructor: function Object() 內置構造函數
console.log("Array.prototype.constructor:",Array.prototype.constructor);
//Array.prototype.constructor: function Array() 內置構造函數
console.log("Number.prototype.constructor:",Number.prototype.constructor);
//Number.prototype.constructor: function Number() 內置構造函數
console.log("Boolean.prototype.constructor:",Boolean.prototype.constructor);
//Boolean.prototype.constructor: function Boolean() 內置構造函數


console.log("XXX.__proto__");
console.log("String.__proto__:",String.__proto__);//String.__proto__: function () 空函數
console.log("Function.__proto__:",Function.__proto__);//Function.__proto__: function () 空函數
console.log("Object.__proto__:",Object.__proto__);//Object.__proto__: function () 空函數
console.log("Array.__proto__:",Array.__proto__);//Array.__proto__: function () 空函數
console.log("Number.__proto__:",Number.__proto__);//Number.__proto__: function () 空函數
console.log("Boolean.__proto__:",Boolean.__proto__);//Boolean.__proto__: function () 空函數


console.log("XXX.prototype.__proto__");
console.log("String.prototype.__proto__:",String.prototype.__proto__);
//String.prototype.__proto__: Object { … } 空對象
console.log("Function.prototype.__proto__:",Function.prototype.__proto__);
//Function.prototype.__proto__: Object { … } 空對象
console.log("Object.prototype.__proto__:",Object.prototype.__proto__);
//Object.prototype.__proto__: null 空對象
console.log("Array.prototype.__proto__:",Array.prototype.__proto__);
//Array.prototype.__proto__: Object { … } 空對象
console.log("Number.prototype.__proto__:",Number.prototype.__proto__);
//Number.prototype.__proto__: Object { … } 空對象
console.log("Boolean.prototype.__proto__:",Boolean.prototype.__proto__);
//Boolean.prototype.__proto__: Object { … } 空對象


console.log("self defined function");
function test(){}
console.log("test:",test);//test: function test() 自定義函數
console.log("test.prototype:",test.prototype);//test.prototype: Object { … } 空對象
console.log("test.prototype.constructor:",test.prototype.constructor);
//test.prototype.constructor: function test() 自定義函數
console.log("test.__proto__:",test.__proto__);//test.__proto__: function () 空函數
console.log("test.prototype.__proto__:",test.prototype.__proto__);
//test.prototype.__proto__: Object { … } 空對象
相關文章
相關標籤/搜索