/** * 原型對象 * 不管什麼時,只要建立一個新函數,就會根據一組特定的規則爲該函數建立一個 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
//以上代碼定義兩個類型: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 { … } 空對象