1. 程式人生 > >初識原型鏈——怎麽畫一條完整的原型鏈

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

n-1 www eof () 原生 節點 ref 方便 ces

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

一、疑惑

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

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的關系如下圖所示:

技術分享圖片

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

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

二、再疑惑

之前看了一篇博文,其中分析了_proto_

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

技術分享圖片

對應的示例代碼如下:

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比較好理解:

使用函數表達式方法創建的函數變量A,就是新的函數變量a的構造函數,所以a的原型對象就是其構造函數的prototype所指的對象:A.prototype
而Object.prototype._proto_為什麽是null可以參考:為什麽原型鏈的終點是null,而不是Object.prototype?- 余百煉的博客 - CSDN博客

然而,為什麽A.prototype的原型是Object.prototype,即A.prototype._proto_==Object.prototype怎麽解釋呢??

三、解答

其實,原型鏈是指對象的原型鏈,這個鏈上的節點都是一級一級的原型對象,所以原型鏈上的所有節點都是對象!!!因此這就好理解了,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

四、再解答

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


技術分享圖片

由於繪圖工具問題,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

五、小結

  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高級程序設計》

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