__proto__、prototype粱栖、constructor话浇、原型鏈,真沒那么難闹究!教你怎么套用方法直接判斷

前言

首先原型幔崖、原型鏈,算是前端進(jìn)階里面必不可少渣淤,十分重要的一塊了赏寇。由于這塊特別繞,所以面試官很喜歡用這一塊來辨別你的底層知識(shí)掌握的怎么樣价认。用的第三方框架嗅定,庫里面,很多功能模塊化了用踩,但大部分功能都繼承自一個(gè)基類渠退。既然涉及到繼承,那也必不可少得先了解原型鏈脐彩,所以原型鏈確實(shí)重中之重碎乃。

為什么大家對原型,原型鏈子會(huì)感到“懵”跟“繞”

其本質(zhì)是因?yàn)椋?strong>大家都沒理清楚__proto__惠奸、prototype梅誓、constructor三者的聯(lián)系。所以很多人在看這一塊知識(shí)的時(shí)候佛南,剛開頭看可能還能理解证九,看久了就懵了,因?yàn)榇a中充斥著各種x.__proto__.__proto__共虑,x.__proto__.constructor.prototype愧怜,x.prototype.__proto__等等,這當(dāng)然會(huì)懵掉妈拌。所以我們要理解原型拥坛、原型鏈?zhǔn)鞘裁磁畹欢ㄒ雀忝靼祝?code>__proto__、prototype猜惋、constructor這三個(gè)到底是個(gè)什么東西丸氛,再弄明白它們?nèi)齻€(gè)是什么聯(lián)系。

下面我會(huì)用比較通俗的話來解釋著摔,帶著大家更好的理解原型缓窜,原型鏈?zhǔn)鞘裁矗ㄒ驗(yàn)闉榱舜蠹腋玫睦斫猓杂行┑胤娇赡軙?huì)稍微有點(diǎn)不恰當(dāng)谍咆,敬請見諒)禾锤。

為了更好的理解,我們用以下變量作為例子跟話術(shù):

  • People為構(gòu)造函數(shù)
  • person為由People實(shí)例出來的一個(gè)對象
  • Object為構(gòu)造所有對象的頂級基類構(gòu)造函數(shù)
  • Function為構(gòu)造所有函數(shù)的頂級基類構(gòu)造函數(shù)

__proto__

這個(gè)屬性可以通俗的理解成摹察,所有對象都擁有的一個(gè)私有屬性(函數(shù)也是一種特殊的對象恩掷,所以構(gòu)造函數(shù)也會(huì)有這個(gè)屬性)。所以我們會(huì)看到person.__proto__供嚎、People.prototype.__proto__黄娘、People.__proto__Object.__proto__克滴、Function.__proto__等描述逼争。

prototype

這個(gè)屬性可以通俗的理解成,專屬于函數(shù)自身的一個(gè)屬性(可用hasOwnProperty驗(yàn)證)劝赔,所以實(shí)例出來的對象不會(huì)有誓焦,只有函數(shù)、構(gòu)造函數(shù)會(huì)有望忆。我們通常都會(huì)把構(gòu)造函數(shù).prototype看做一個(gè)整體罩阵,它代表的是竿秆,這個(gè)函數(shù)的prototype里启摄,所有的屬性方法等(People.prototype代表People.prototype這個(gè)整體里,所有的屬性與方法)幽钢。所以我們會(huì)看到person.__proto__.prototype歉备、People.prototypeObject.prototype匪燕、Function.prototype等描述蕾羊,但一定不會(huì)看到實(shí)例.prototypeperson.prototype)。

例外:

  • 箭頭函數(shù)沒有prototype帽驯,箭頭函數(shù)也不能拿來做構(gòu)造函數(shù)
  • 使用bind方法創(chuàng)造出來的副本函數(shù)也沒有prototype

這兩個(gè)是例外龟再,大家記得就好,但不影響我們的理解尼变。

constructor

這個(gè)屬性也可以通俗的理解成利凑,所有對象都擁有的一個(gè)屬性浆劲。可以用對象.constructor.name來查看當(dāng)前構(gòu)造函數(shù)的名字是什么(person.constructor.name返回People哀澈,因?yàn)?code>person由People構(gòu)造實(shí)例而來)牌借。所以我們也會(huì)看到person.constructorPeople.prototype.constructor割按、People.constructor等描述膨报。

ok,介紹完這三個(gè)屬性适荣,我們再來看看這三者有什么聯(lián)系现柠。

__proto__prototype束凑、constructor這三者到底是什么聯(lián)系

我們看看下面例子:

// 定義一個(gè)People構(gòu)造函數(shù)
function People () {

}

// 實(shí)例化一個(gè)person對象
const person = new People();

// 打印true --> 說明實(shí)例的__proto__與實(shí)例的構(gòu)造函數(shù)的prototype相等
console.log(person.__proto__ === People.prototype); 

// 打印true --> 說明constructor是構(gòu)造函數(shù)的prototype里“自身”的一個(gè)屬性
console.log(People.prototype.hasOwnProperty('constructor'));

// 打印true --> 說明非頂級構(gòu)造函數(shù)的prototype.constructor指回這個(gè)構(gòu)造函數(shù)本身
console.log(People.prototype.constructor === People);

// 打印true --> 說明實(shí)例的__proto__.constructor 就是 構(gòu)造函數(shù)的prototype.constructor(由第一個(gè)打印可知person.__proto__ = People.prototype)
console.log(person.__proto__.constructor === People.prototype.constructor);

// 打印People --> 說明實(shí)例的constructor指向的就是實(shí)例的構(gòu)造函數(shù)
console.log(person.constructor.name);

// 打印fale --> 說明實(shí)例自身是沒有的constructor屬性的
console.log(person.hasOwnProperty('constructor'));

// 打印true, true --> 說明實(shí)例自身是沒有的constructor屬性的
// 它是繼承自實(shí)例的__proto__.constructor晒旅,即實(shí)例的構(gòu)造函數(shù)的prototype.constructor
console.log(person.constructor === person.__proto__.constructor, person.constructor === People.prototype.constructor);

解析:

  • __proto__prototype是什么聯(lián)系:
    如果有一個(gè)實(shí)例,它是由一個(gè)構(gòu)造函數(shù)實(shí)例而來汪诉,那么這個(gè)實(shí)例的__proto__一定指向這個(gè)構(gòu)造函數(shù)的prototype废恋,即person.__proto__ = People.prototype
  • prototypeconstructor是什么聯(lián)系:
    constructor就是某個(gè)普通構(gòu)造函數(shù)的prototype自身的一個(gè)屬性(用hasOwnProperty可驗(yàn)證),它指向的就是這個(gè)構(gòu)造函數(shù)本身扒寄,即People.prototype.constructor = People
  • __proto__constructor是什么聯(lián)系:
    __proto__constructor的聯(lián)系跟prototypeconstructor的聯(lián)系一樣鱼鼓。因?yàn)橐?code>.__proto__結(jié)尾的,它最后一定指向某個(gè)構(gòu)造函數(shù)的原型對象(People.prototype该编,然后又由于constructor是某個(gè)構(gòu)造函數(shù)的prototype自身的一個(gè)屬性迄本,因此我們可以這么看:person.__proto__.constructor = People.prototype.constructor

ok,看到這里课竣,大家可以先暫停一下嘉赎,整理一下思路。理一理什么是__proto__于樟、prototype公条、constructor;然后再理一理__proto__迂曲、prototype靶橱、constructor這三者之間的聯(lián)系。然后接下來進(jìn)入最讓我們蒙圈的東西——原型鏈路捧。

什么是原型鏈

當(dāng)我們用構(gòu)造函數(shù)People實(shí)例化了一個(gè)對象person后关霸,訪問person的方法或者屬性時(shí),會(huì)先在實(shí)例person自身找有沒有對應(yīng)的方法屬性杰扫。有值的話队寇,則返回值,沒有的話則去person.__proto__People.prototype)里找章姓;有值的話佳遣,則返回值炭序,沒有的話,又會(huì)去People.prototype.__proto__Object.prototype)里找苍日。有值的話惭聂,則返回值;沒有的話相恃,又會(huì)去Object.prototype._proto__里找辜纲,但是Object.prototype.__proto__返回null,原型鏈到頂拦耐,一條條原型鏈搜索完畢耕腾,都沒有,則返回undefined杀糯。

在查找的過程中會(huì)遍歷以上的一條鏈扫俺,這條鏈就是原型鏈。上述的過程可以這么看(這個(gè)過程也是實(shí)現(xiàn)繼承的核心):

pic_1.png

經(jīng)過上述的知識(shí)點(diǎn)固翰,相信大家對原型鏈應(yīng)該有個(gè)基本的認(rèn)識(shí)里吧狼纬,現(xiàn)在我們來總結(jié)一下,看看有沒有什么方法規(guī)律骂际。

方法總結(jié)

在看到一堆類似.__proto__.__proto__.__proto__疗琉、.__proto__.__proto__.prototype.__proto__.prototype.consturtor什么的歉铝,先不要慌盈简。

思想步驟:

  1. 我們直接看最后一個(gè)屬性,看看是以什么結(jié)尾
  2. 然后再一步步反推前面調(diào)用的都是什么對象
  3. 最后再推出它具體返回值的是什么

規(guī)律:

  1. 如果最后以.__proto__結(jié)尾太示,它最后返回的一定是某個(gè)構(gòu)造函數(shù)的prototypeObject.prototype.__proto__除外柠贤,它到頂了,是原型鏈的頂端类缤,返回null

  2. 如果是以.prototype結(jié)尾臼勉,那么它前面一定是個(gè)構(gòu)造函數(shù),因?yàn)橹挥泻瘮?shù)才會(huì)有prototype屬性(因?yàn)橐话阋?code>.prototype結(jié)尾返回的都是這個(gè)構(gòu)造函數(shù)的prototype所有的方法與屬性呀非,所以題目很少會(huì)以.prototype結(jié)尾)

  3. 如果是以.constructor結(jié)尾坚俗,先弄清楚前面是什么

    • 如果前面是實(shí)例镜盯,那它直接返回創(chuàng)造實(shí)例的那個(gè)構(gòu)造函數(shù)岸裙;
    • 如果前面直接是頂級基類構(gòu)造函數(shù)Function.constructor)或者直接是普通構(gòu)造函數(shù)People.constructor),它會(huì)直接指向構(gòu)造所有函數(shù)的頂級基類構(gòu)造函數(shù)Function(所有構(gòu)造函數(shù)都是函數(shù)速缆,都由頂級構(gòu)造函數(shù)Function而來降允,所以constructor當(dāng)然指向它;
    • 如果前面是非頂級構(gòu)造函數(shù)(普通函數(shù))的原型對象People.prototype.constructor)艺糜,因?yàn)閷?shí)例的constructor是繼承自普通構(gòu)造函數(shù).prototype.constructor剧董,所以普通構(gòu)造函數(shù).prototype.constructor必須指回它自己幢尚,(普通構(gòu)造函數(shù).prototype.constructor = 普通構(gòu)造函數(shù))。針對這點(diǎn)翅楼,我們看看它是怎么繼承來的尉剩。

    constructor整個(gè)繼承的流程是:在實(shí)例person本身查找,找不到去person.__proto__People.prototype)找毅臊,發(fā)現(xiàn)有People.prototype.constructor理茎,并且People.prototype.constructor = People返回它,所以person.constructor = People管嬉。
    流程如圖所示:

    [圖片上傳失敗...(image-eff596-1674636118495)]

ok皂林,經(jīng)過上面總結(jié)出的思想步驟跟規(guī)律,我們來試試:

// 定義一個(gè)People構(gòu)造函數(shù)
function People() {

}

// 實(shí)例化一個(gè)person對象
const person = new People();

// 第一題
console.log(People.__proto__);

// 第二題
console.log(People.constructor);

// 第三題
console.log(person.prototype);

// 第四題
console.log(person.__proto__.__proto__);

// 第五題
console.log(People.__proto__.prototype);

// 第六題
console.log(person.__proto__.__proto__.constructor);

// 第七題
console.log(Object.__proto__);
  • 我們以第一道題為例蚯撩,解析一下:

    1. 先看是以什么結(jié)尾础倍。以.__proto__
    2. ok,心里有個(gè)大概了胎挎,根據(jù)規(guī)律總結(jié)第一點(diǎn)沟启,它肯定返回某個(gè)構(gòu)造函數(shù)的prototype
    3. 再反推一下前面調(diào)用的都是什么對象。前面是People犹菇,People是什么美浦?是構(gòu)造函數(shù),函數(shù)都有一個(gè)頂級基類構(gòu)造函數(shù)项栏,那就是Function浦辨,所以People.__proto__返回的就是Function.prototype
  • 我們以第二道題為例沼沈,解析一下:

    1. 先看是以什么結(jié)尾流酬。以.constructor
    2. 調(diào)用對象直接是普通構(gòu)造函數(shù),根據(jù)規(guī)律總結(jié)第三點(diǎn)的第二小點(diǎn)列另,直接得出Function
  • 我們再以第六道題為例芽腾,解析一下:

    1. 先看是以什么結(jié)尾。以.constructor
    2. 再反推一下前面是調(diào)用的都是什么對象页衙。先看person.__proto__摊滔,返回的是People.prototype,那這題就變成了People.prototype.__proto__.constructor店乐。再繼續(xù)看艰躺,People.prototype.__proto__返回的是什么,Object.prototype眨八,那這題實(shí)際就是Object.prototype.constructor腺兴。根據(jù)規(guī)律總結(jié)第三點(diǎn)的第三小點(diǎn),那它返回的就是Object本身廉侧。

大家一定要注意页响,Object.__proto__Function.__proto__篓足,ObjectFunction都是頂級構(gòu)造函數(shù),所以Object.__proto__闰蚕、Function.__proto__返回的都是Function.prototype

牛刀小試

根據(jù)上面對__proto__栈拖、prototypeconstructor的特點(diǎn)總結(jié)没陡,還有方法總結(jié)辱魁,我們可以拿下面這道題來試試,如果大家都可以正確無誤的答出來诗鸭,那大家對原型應(yīng)該就了解的差不多了

function Person(name) {
    this.name = name
}
var p2 = new Person('king');

console.log(p2.__proto__); // Person.prototype

console.log(p2.__proto__.__proto__); // Object.prototype

console.log(p2.__proto__.__proto__.__proto__); // null

console.log(p2.__proto__.__proto__.__proto__.__proto__); // 報(bào)錯(cuò)

console.log(p2.constructor); // Person

console.log(p2.prototype); // undefined

console.log(Person.constructor); // Function

console.log(Person.prototype); // 輸出Person.prototype這個(gè)對象里所有的方法和屬性

console.log(Person.prototype.constructor); // Person

console.log(Person.prototype.__proto__); // Obejct.prototype

console.log(Person.__proto__); // Fuction.prototype

console.log(Function.prototype.__proto__); // Obeject.prototype

console.log(Function.__proto__); // Function.prototype

console.log(Object.__proto__); // Function.prototype

console.log(Object.prototype.__proto__); // null

最后

原型染簇、原型鏈本來就挺繞的,所以大家先了解__proto__强岸、prototype吐葵、constructor是什么踊兜,再明白它們之間的是什么聯(lián)系忧便,循環(huán)漸進(jìn)牺汤。等理解以后,多畫幾遍原型鏈圖加深理解妓盲。OK杂拨,最后祭出一張?jiān)玩湀D:

紅色鏈表示的就是實(shí)例person原型鏈

pic_3.png

寫著寫著,發(fā)現(xiàn)又寫了一大堆悯衬,希望能夠幫助到大家弹沽。如果覺得覺得寫得好的,有幫助到的筋粗,歡迎大家點(diǎn)贊策橘,也歡迎大家評論交流。

既然明白了什么是原型鏈娜亿,那還不趕緊趁熱打鐵丽已,進(jìn)階看看什么是JS繼承吧!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末买决,一起剝皮案震驚了整個(gè)濱河市沛婴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌督赤,老刑警劉巖嘁灯,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異够挂,居然都是意外死亡旁仿,警方通過查閱死者的電腦和手機(jī)藕夫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門孽糖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枯冈,“玉大人,你說我怎么就攤上這事办悟〕咀啵” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵病蛉,是天一觀的道長炫加。 經(jīng)常有香客問我,道長铺然,這世上最難降的妖魔是什么俗孝? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮魄健,結(jié)果婚禮上赋铝,老公的妹妹穿的比我還像新娘。我一直安慰自己沽瘦,他們只是感情好革骨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著析恋,像睡著了一般良哲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上助隧,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天筑凫,我揣著相機(jī)與錄音,去河邊找鬼并村。 笑死漏健,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的橘霎。 我是一名探鬼主播蔫浆,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姐叁!你這毒婦竟也來了瓦盛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤外潜,失蹤者是張志新(化名)和其女友劉穎原环,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體处窥,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘱吗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谒麦。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俄讹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绕德,到底是詐尸還是另有隱情患膛,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布耻蛇,位于F島的核電站踪蹬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏臣咖。R本人自食惡果不足惜跃捣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夺蛇。 院中可真熱鬧枝缔,春花似錦、人聲如沸蚊惯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽截型。三九已至趴荸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宦焦,已是汗流浹背发钝。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留波闹,地道東北人酝豪。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像精堕,于是被迫代替她去往敵國和親孵淘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容