幫你徹底搞懂JS中的prototype、__proto__與constructor(圖解)(轉)

hackLi發表於2019-03-29

本文為CSDN博主碼飛_CC原創文章,未經博主允許不得轉載。 blog.csdn.net/cc188688768…

 作為一名前端工程師,必須搞懂JS中的prototype_proto_constructor屬性,相信很多初學者對這些屬性存在許多困惑,容易把它們混淆,本文旨在幫助大家理清它們之間的關係並徹底搞懂它們。這裡說明一點,__proto__屬性的兩邊是各由兩個下劃線構成(這裡為了方便大家看清,在兩下劃線之間加入了一個空格:_ _ proto _ _),本文基於谷歌瀏覽器(版本 72.0.3626.121)的實驗結果所得。

原型鏈

現在正式開始! 讓我們從如下一個簡單的例子展開討論,並配以相關的圖幫助理解:

function Foo() {...};
let f1 = new Foo();
複製程式碼

以上程式碼表示建立一個建構函式Foo(),並用new關鍵字例項化該建構函式得到一個例項化物件f1。雖然是簡簡單單的兩行程式碼,然而它們背後的關係卻是錯綜複雜的,如下圖所示:

image

看到這圖別怕,讓我們一步步剖析,徹底搞懂它們!
  圖的說明:右下角為圖例,紅色箭頭表示__proto__屬性指向、綠色箭頭表示prototype屬性的指向、棕色實線箭頭表示本身具有的constructor屬性的指向,棕色虛線箭頭表示繼承而來的constructor屬性的指向;藍色方塊表示物件,淺綠色方塊表示函式(這裡為了更好看清,Foo()僅代表是函式,並不是指執行函式Foo後得到的結果,圖中的其他函式同理)。圖的中間部分即為它們之間的聯絡,圖的最左邊即為例子程式碼。
  首先,我們需要牢記兩點:①__proto__constructor屬性是物件所獨有的;② prototype屬性是函式所獨有的。但是由於JS中函式也是一種物件,所以函式也擁有__proto__constructor屬性,這點是致使我們產生困惑的很大原因之一。

__proto__

上圖有點複雜,我們把它按照屬性分別拆開,然後進行分析:   

image

第一,這裡我們僅留下 __proto__ 屬性,它是物件所獨有的,可以看到__proto__屬性都是由一個物件指向一個物件,即指向它們的原型物件(也可以理解為父物件),那麼這個屬性的作用是什麼呢?它的作用就是當訪問一個物件的屬性時,如果該物件內部不存在這個屬性,那麼就會去它的__proto__屬性所指向的那個物件(可以理解為父物件)裡找,如果父物件也不存在這個屬性,則繼續往父物件的__proto__屬性所指向的那個物件(可以理解為爺爺物件)裡找,如果還沒找到,則繼續往上找…直到原型鏈頂端null(可以理解為原始人。。。),此時若還沒找到,則返回undefined(可以理解為,再往上就已經不是“人”的範疇了,找不到了,到此結束),由以上這種通過__proto__屬性來連線物件直到null的一條鏈即為我們所謂的原型鏈

prototype

第二,接下來我們看 prototype 屬性:

image

  prototype屬性,別忘了一點,就是我們前面提到要牢記的兩點中的第二點,它是函式所獨有的,它是從一個函式指向一個物件。它的含義是函式的原型物件,也就是這個函式(其實所有函式都可以作為建構函式)所建立的例項的原型物件,由此可知:f1.__proto__ === Foo.prototype,它們兩個完全一樣。那prototype屬性的作用又是什麼呢?它的作用就是包含可以由特定型別的所有例項共享的屬性和方法,也就是讓該函式所例項化的物件們都可以找到公用的屬性和方法。任何函式在建立的時候,其實會預設同時建立該函式的prototype物件

constructor

最後,我們來看一下 constructor 屬性:

image
  constructor屬性也是物件才擁有的,它是從一個物件指向一個函式,含義就是指向該物件的建構函式指向該物件的建構函式,每個物件都有建構函式(本身擁有或繼承而來,繼承而來的要結合__proto__屬性檢視會更清楚點,如下圖所示),從上圖中可以看出Function這個物件比較特殊,它的建構函式就是它自己(因為Function可以看成是一個函式,也可以是一個物件),所有函式和物件最終都是由Function建構函式得來,所以constructor屬性的終點就是Function這個函式。
image
  感謝網友的指出,這裡解釋一下上段中“每個物件都有建構函式”這句話。這裡的意思是每個物件都可以找到其對應的constructor,因為建立物件的前提是需要有constructor,而這個constructor可能是物件自己本身顯式定義的或者通過__proto__在原型鏈中找到的。而單從constructor這個屬性來講,只有prototype物件才有。每個函式在建立的時候,JS會同時建立一個該函式對應的prototype物件,而函式建立的物件.proto === 該函式.prototype,該函式.prototype.constructor===該函式本身,故通過函式建立的物件即使自己沒有constructor屬性,它也能通過__proto__找到對應的constructor,所以任何物件最終都可以找到其建構函式(null如果當成物件的話,將null除外)。如下:
image

總結

  1. 我們需要牢記兩點:①__proto__constructor屬性是物件所獨有的;② prototype屬性是函式所獨有的,因為函式也是一種物件,所以函式也擁有__proto__constructor屬性。
  2. __proto__屬性的作用就是當訪問一個物件的屬性時,如果該物件內部不存在這個屬性,那麼就會去它的__proto__屬性所指向的那個物件(父物件)裡找,一直找,直到__proto__屬性的終點null,然後返回undefined,通過__proto__屬性將物件連線起來的這條鏈路即我們所謂的原型鏈
  3. prototype屬性的作用,即f1.__proto__ === Foo.prototype
  4. constructor屬性的含義就是指向該物件的建構函式,所有函式(此時看成物件了)最終的建構函式都指向Function。   本文就此結束了,希望對那些對JS中的prototype__proto__constructor屬性有困惑的同學有所幫助。

最後,感謝這兩篇博文,本文中的部分內容參考自這兩篇博文:

www.cnblogs.com/xiaohuochai…

www.cnblogs.com/Narcotic/p/…

相關文章