初識原型鏈——怎麼畫一條完整的原型鏈

關於原型鏈的知識,我剛開始就是看一篇一篇的博客,知識有點零碎,後面看了下《JS高級程序設計》,又大體整合了一下。下面記錄的是,對於一條完整的原型鏈長什麼樣這個問題的思考。javascript

1、疑惑

借用一段在《JS高級程序設計》中的一段代碼:html

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

此段代碼中,構造函數Person,原型對象Person.prototype以及實例對象person1person2的關係以下圖所示:java

按照書上的解釋,很好理解,但很顯然,它並非一條完整的原型鏈,而且我也有疑問:書中給出的示例圖中沒有畫構造函數Person和原型對象Person.prototype的_proto_屬性。segmentfault

首先:_proto_屬性是個對象都有,那麼,在萬物皆對象的JS中,構造函數Person和原型對象Person.prototype_proto_屬性又指向哪裏呢??其次,就這個問題,結合上面給出的代碼,能夠將一條完整的原型鏈用相似上面的圖畫出來嗎??函數

2、再疑惑

以前看了一篇博文,其中分析了_proto_應該指向誰的問題,舉了下面這個例子,可能直接看會有點蒙圈,原文: JS原型鏈簡單圖解 - 最騷的就是你 - 博客園工具

對應的示例代碼以下:this

var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(A.prototype即A的原型對象)
console.log(a.__proto__.__proto__); //Object {}(Object.prototype即Object的原型對象)
console.log(a.__proto__.__proto__.__proto__); //null

其實,對於a._proto_ ==A.prototype以及Object.prototype._proto_==null比較好理解:.net

使用函數表達式方法建立的函數變量A,就是新的函數變量a的構造函數,因此a的原型對象就是其構造函數的prototype所指的對象:A.prototype
而Object.prototype._proto_爲何是null能夠參考:爲何原型鏈的終點是null,而不是Object.prototype?- 餘百鍊的博客 - CSDN博客prototype

然而,爲何A.prototype的原型是Object.prototype,即A.prototype._proto_==Object.prototype怎麼解釋呢??設計

3、解答

其實,原型鏈是指對象的原型鏈,這個鏈上的節點都是一級一級的原型對象,因此原型鏈上的全部節點都是對象!!!所以這就好理解了,A.prototype 是一個對象,而後它是Object的一個實例,因此它的原型是Object.prototype

這裏須要注意的是,a雖然是原型鏈上的起點,它也是對象,可是它的原型並非直接就是Object.prototype,它的原型是其構造函數的prototype所指的對象:A.prototype

其實這個圖描述的還只是局部 ,不利於理解,請看下面的圖:(圖片取自javascript - 爲何原型鏈的終點是null,而不是Object.prototype - SegmentFault 思否



從這張大圖中咱們能夠看出來,

  1. 全部函數對象的原型(即fun._proto_)都是Function.prototype,不管是JS原生的構造函數如Function仍是Object等仍是用戶自定義的構造函數如上圖的Foo;
  2. 全部構造函數對應的原型對象的原型(即fun.prototype._proto_)都是Object.prototype,不管是自定義的仍是原生的(Object除外);
  3. 存儲在函數對象中的屬性有_proto_、prototype(其實也有constructor,通通指向Function,就連Function本身的constructor也是指向本身,也就是說全部函數對象都是Function的實例,包括它本身);
  4. 存儲在原型對象中的屬性有_proto_、constructor;
  5. 存儲在實例對象中的屬性有_proto_(其實也有constructor不過和原型對象中的指向一致)

根據這個圖,能夠更好的理解下面這兩句話:

  1. 因爲_proto_是任何對象都有的屬性,而JS裏萬物都是對象,因此會造成一條_proto_連起來的鏈條,遞歸訪問_proto_必須最終到頭,而且值是null;
  2. 當JS引擎查找對象的屬性時,先查找對象自己是否存在該屬性,若是不存在,會在原型鏈上查找,但不會查找自身的prototype

4、再解答

這樣的話,關係就比較明瞭了。因此,文章開頭的那段代碼的原型鏈應該以下圖所示:




因爲繪圖工具問題,function Function應該還有一個指向本身的constructor沒畫出來。這個圖對應的代碼又貼在了下面,離得近方便看…………

function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

趁着這個機會,能夠討論一下原生構造函數ObjectFunction到底什麼關係?
從上圖中咱們能夠把FunctionObject部分單獨摘出來:




用代碼將上面的原型鏈表示以下:

Function._proto_==Function.prototype==function(){}
Function._proto_._proto_==Object.prototype
Function._proto_._proto_._proto_==Object.prototype._proto_==null

Object._proto_ === Function.prototype==function(){}
Object._proto_._proto_==Object.prototype
Object._proto_._proto_._proto_==null

從上圖中能夠看到構造函數之間的關係以下:

Object.prototype.constructor===Object
Object instanceof Function;//true
Object.constructor===Function

Function.prototype.constructor===Function
Function instanceof Object;//true
Function.constructor===Function

5、小結

  1. 全部函數對象的原型(即fun._proto_)都是Function.prototype不管是JS原生的構造函數如Function仍是Object等仍是用戶自定義的構造函數;
  2. 全部構造函數對應的原型對象的原型(即fun.prototype._proto_)都是Object.prototype,不管是自定義的仍是原生的
  3. 存儲在函數對象中的屬性有_proto_、prototype(其實也有constructor,通通指向Function,就連Function本身的constructor也是指向本身)
  4. 存儲在原型對象中的屬性有_proto_、constructor
  5. 存儲在實例對象中的屬性有_proto_(其實也有constructor不過和原型對象中的指向一致)

Reference:

  1. JS原型鏈簡單圖解 - 最騷的就是你 - 博客園
  2. javascript - 爲何原型鏈的終點是null,而不是Object.prototype - SegmentFault 思否
  3. 爲何原型鏈的終點是null,而不是Object.prototype?- 餘百鍊的博客 - CSDN博客
  4. 《JS高級程序設計》
相關文章
相關標籤/搜索