幫你徹底搞懂JS中的prototype、__proto__與constructor(圖解)
提示:不要排斥,靜下心來,認真讀完,你就搞懂了!(可以先看一下最后的總結部分再回過頭來完整看完)
1. 前言
作為一名前端工程師,必須搞懂JS中的prototype
、__proto__
與constructor
屬性,相信很多初學者對這些屬性存在許多困惑,容易把它們混淆,本文旨在幫助大家理清它們之間的關系并徹底搞懂它們。這里說明一點,__proto__
屬性的兩邊是各由兩個下劃線構成(這里為了方便大家看清,在兩下劃線之間加入了一個空格:_ _proto_ _
),實際上,該屬性在ES標準定義中的名字應該是[[Prototype]]
,具體實現(xiàn)是由瀏覽器代理自己實現(xiàn),谷歌瀏覽器的實現(xiàn)就是將[[Prototype]]
命名為__proto__
,大家清楚這個標準定義與具體實現(xiàn)的區(qū)別即可(名字有所差異,功能是一樣的)。本文基于谷歌瀏覽器(版本 72.0.3626.121)的實驗結果所得。
現(xiàn)在正式開始! 讓我們從如下一個簡單的例子展開討論,并配以相關的圖幫助理解:
function Foo() {...}; let f1 = new Foo();
以上代碼表示創(chuàng)建一個構造函數(shù)Foo()
,并用new
關鍵字實例化該構造函數(shù)得到一個實例化對象f1
。這里稍微補充一下new操作符將函數(shù)作為構造器進行調(diào)用時的過程:函數(shù)被調(diào)用,然后新創(chuàng)建一個對象,并且成了函數(shù)的上下文(也就是此時函數(shù)內(nèi)部的this是指向該新創(chuàng)建的對象,這意味著我們可以在構造器函數(shù)內(nèi)部通過this參數(shù)初始化值),最后返回該新對象的引用。雖然是簡簡單單的兩行代碼,然而它們背后的關系卻是錯綜復雜的,如下圖所示:
看到這圖別怕,讓我們一步步剖析,徹底搞懂它們!
圖的說明:右下角為圖例,紅色箭頭表示__proto__
屬性指向、綠色箭頭表示prototype
屬性的指向、棕色實線箭頭表示本身具有的constructor
屬性的指向,棕色虛線箭頭表示繼承而來的constructor
屬性的指向;藍色方塊表示對象,淺綠色方塊表示函數(shù)(這里為了更好看清,F(xiàn)oo()僅代表是函數(shù),并不是指執(zhí)行函數(shù)Foo后得到的結果,圖中的其他函數(shù)同理)。圖的中間部分即為它們之間的聯(lián)系,圖的最左邊即為例子代碼。
2. _ _ proto _ _ 屬性
首先,我們需要牢記兩點:①__proto__
和constructor
屬性是對象所獨有的;② prototype
屬性是函數(shù)所獨有的。但是由于JS中函數(shù)也是一種對象,所以函數(shù)也擁有__proto__
和constructor
屬性,這點是致使我們產(chǎn)生困惑的很大原因之一。上圖有點復雜,我們把它按照屬性分別拆開,然后進行分析:
第一,這里我們僅留下 __proto__
屬性,它是對象所獨有的,可以看到__proto__
屬性都是由一個對象指向一個對象,即指向它們的原型對象(也可以理解為父對象),那么這個屬性的作用是什么呢?它的作用就是當訪問一個對象的屬性時,如果該對象內(nèi)部不存在這個屬性,那么就會去它的__proto__
屬性所指向的那個對象(可以理解為父對象)里找,如果父對象也不存在這個屬性,則繼續(xù)往父對象的__proto__
屬性所指向的那個對象(可以理解為爺爺對象)里找,如果還沒找到,則繼續(xù)往上找…直到原型鏈頂端null(可以理解為原始人。。。),再往上找就相當于在null上取值,會報錯(可以理解為,再往上就已經(jīng)不是“人”的范疇了,找不到了,到此結束,null
為原型鏈的終點),由以上這種通過__proto__
屬性來連接對象直到null
的一條鏈即為我們所謂的原型鏈。
3. prototype屬性
第二,接下來我們看 prototype
屬性:
prototype
屬性,別忘了一點,就是我們前面提到要牢記的兩點中的第二點,它是函數(shù)所獨有的,它是從一個函數(shù)指向一個對象。它的含義是函數(shù)的原型對象,也就是這個函數(shù)(其實所有函數(shù)都可以作為構造函數(shù))所創(chuàng)建的實例的原型對象,由此可知:f1.__proto__ === Foo.prototype
,它們兩個完全一樣。那prototype
屬性的作用又是什么呢?它的作用就是包含可以由特定類型的所有實例共享的屬性和方法,也就是讓該函數(shù)所實例化的對象們都可以找到公用的屬性和方法。任何函數(shù)在創(chuàng)建的時候,其實會默認同時創(chuàng)建該函數(shù)的prototype對象。
4. constructor屬性
最后,我們來看一下 constructor
屬性:
constructor
屬性也是對象才擁有的,它是從一個對象指向一個函數(shù),含義就是指向該對象的構造函數(shù),每個對象都有構造函數(shù)(本身擁有或繼承而來,繼承而來的要結合__proto__
屬性查看會更清楚點,如下圖所示),從上圖中可以看出Function這個對象比較特殊,它的構造函數(shù)就是它自己(因為Function可以看成是一個函數(shù),也可以是一個對象),所有函數(shù)和對象最終都是由Function構造函數(shù)得來,所以constructor
屬性的終點就是Function這個函數(shù)。
感謝網(wǎng)友的指出,這里解釋一下上段中“每個對象都有構造函數(shù)”這句話。這里的意思是每個對象都可以找到其對應的constructor,因為創(chuàng)建對象的前提是需要有constructor,而這個constructor可能是對象自己本身顯式定義的或者通過__proto__
在原型鏈中找到的。而單從constructor這個屬性來講,只有prototype對象才有。每個函數(shù)在創(chuàng)建的時候,JS會同時創(chuàng)建一個該函數(shù)對應的prototype對象,而函數(shù)創(chuàng)建的對象.__proto__ === 該函數(shù).prototype,該函數(shù).prototype.constructor===該函數(shù)本身
,故通過函數(shù)創(chuàng)建的對象即使自己沒有constructor屬性,它也能通過__proto__
找到對應的constructor,所以任何對象最終都可以找到其構造函數(shù)(null如果當成對象的話,將null除外)。如下:
5. 總結
總結一下:
- 我們需要牢記兩點:①
__proto__
和constructor
屬性是對象所獨有的;②prototype
屬性是函數(shù)所獨有的,因為函數(shù)也是一種對象,所以函數(shù)也擁有__proto__
和constructor
屬性。 __proto__
屬性的作用就是當訪問一個對象的屬性時,如果該對象內(nèi)部不存在這個屬性,那么就會去它的__proto__
屬性所指向的那個對象(父對象)里找,一直找,直到__proto__
屬性的終點null,再往上找就相當于在null上取值,會報錯。通過__proto__
屬性將對象連接起來的這條鏈路即我們所謂的原型鏈。prototype
屬性的作用就是讓該函數(shù)所實例化的對象們都可以找到公用的屬性和方法,即f1.__proto__ === Foo.prototype
。constructor
屬性的含義就是指向該對象的構造函數(shù),所有函數(shù)(此時看成對象了)最終的構造函數(shù)都指向Function。
本文就此結束了,希望對那些對JS中的prototype
、__proto__
與constructor
屬性有困惑的同學有所幫助。
最后,感謝這兩篇博文,本文中的部分內(nèi)容參考自這兩篇博文:
http://www.dbjr.com.cn/article/168334.htm
http://www.dbjr.com.cn/article/89523.htm
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
從數(shù)據(jù)庫讀取數(shù)據(jù)后將其輸出成html標簽的三種方法
需要輸出成html標簽時編譯器卻自動幫我們輸出成字符串,這該怎么辦?下面有個三個解決方法,一一測試便知其效果是如何2014-10-10javascript實現(xiàn)網(wǎng)頁字符定位的方法
這篇文章主要介紹了javascript實現(xiàn)網(wǎng)頁字符定位的方法,實例分析了javascript頁面元素查找與定位的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07微信小程序開發(fā)之從相冊獲取圖片 使用相機拍照 本地圖片上傳
本篇文章主要介紹了微信小程序開發(fā)之從相冊獲取圖片--使用相機拍照,本地圖片上傳的相關資料。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04JavaScript創(chuàng)建閉包的兩種方式的優(yōu)劣與區(qū)別分析
這篇文章主要介紹了JavaScript創(chuàng)建閉包的兩種方式的優(yōu)劣與區(qū)別分析的相關資料,需要的朋友可以參考下2015-06-06