一文搞懂JavaScript中原型與原型鏈
前言
js中的原型與原型鏈應(yīng)該是老生常談的話題了,在前端面試中基本都是必問(wèn)的一個(gè)問(wèn)題,但是很多人還是稀里糊涂的,只知道其表層含義,一但面試官問(wèn)深一點(diǎn)就支支吾吾了(我自己)。為了自己下次能在面試中"裝b",肝了一夜,特此記錄一下,加深印象,也希望能幫到有需要的小伙伴。
先來(lái)看一張圖↓
相信第一眼看到這個(gè)圖的人,都覺(jué)得很繁瑣,不想看了,其實(shí)你耐心的看完這篇文章就會(huì)覺(jué)得不過(guò)如此。這種圖真的特別有用、特別有用、特別有用,重要的事情說(shuō)三遍,那我們開(kāi)始吧。
構(gòu)造函數(shù)創(chuàng)建對(duì)象
我們先用一個(gè)構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)對(duì)象
function Person() { } var person = new Person(); person.name = 'Kevin'; console.log(person.name) // Kevin
這個(gè)例子就是用Person構(gòu)造函數(shù)new了一個(gè)實(shí)例對(duì)象person,這個(gè)就不用多說(shuō)了吧。
prototype
每個(gè)函數(shù)都有一個(gè)prototype屬性,不信的話,你可以隨便找個(gè)函數(shù),打開(kāi)F12都能看到。(注意:prototype是函數(shù)特有的屬性)
function Person() { } // 雖然寫在注釋里,但是你要注意: // prototype是函數(shù)才會(huì)有的屬性 Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin
那這個(gè)函數(shù)的 prototype 屬性到底指向的是什么呢?是這個(gè)函數(shù)的原型嗎?
其實(shí),函數(shù)的 prototype 屬性指向了一個(gè)對(duì)象,這個(gè)對(duì)象正是調(diào)用該構(gòu)造函數(shù)而創(chuàng)建的實(shí)例的原型,也就是這個(gè)例子中的 person1 和 person2 的原型。那什么是原型呢?你可以這樣理解:每一個(gè)JavaScript對(duì)象(null除外)在創(chuàng)建的時(shí)候就會(huì)與之關(guān)聯(lián)另一個(gè)對(duì)象,這個(gè)對(duì)象就是我們所說(shuō)的原型,每一個(gè)對(duì)象都會(huì)從原型"繼承"屬性。讓我們用一張圖表示構(gòu)造函數(shù)和實(shí)例原型之間的關(guān)系:
在這張圖中我們用Person.prototype表示實(shí)例原型。
那么我們?cè)撛趺幢硎緦?shí)例與實(shí)例原型,也就是 person 和 Person.prototype 之間的關(guān)系呢,這時(shí)候我們就要講到第二個(gè)屬性:
__proto__
為了證明每一個(gè)JavaScript對(duì)象(null除外)在創(chuàng)建的時(shí)候就會(huì)與之關(guān)聯(lián)另一個(gè)對(duì)象,這個(gè)對(duì)象就是我們所說(shuō)的原型這句話的真實(shí)性,我們可以用個(gè)例子來(lái)證實(shí)。
function Person() { } var person = new Person(); console.log(person.__proto__ === Person.prototype); // true
既然這樣,我們來(lái)更新一下關(guān)系圖:
既然實(shí)例對(duì)象和構(gòu)造函數(shù)都可以指向原型,那原型中是否有屬性可以指向?qū)嵗蛘哒f(shuō)是構(gòu)造函數(shù)呢?
首先我們可以排除原型是沒(méi)有屬性指向?qū)嵗?,因?yàn)橐粋€(gè)構(gòu)造函數(shù)可以new多個(gè)實(shí)例。
constructor
主要是講下原型中的constructor,每個(gè)原型都有一個(gè)constructor屬性指向構(gòu)造函數(shù)。老規(guī)矩,可以在瀏覽器驗(yàn)證一下。
function Person() { } console.log(Person === Person.prototype.constructor); // true
得到下面的關(guān)系圖:
從上面的關(guān)系圖以及舉的例子,我們來(lái)梳理總結(jié)一下:
function Person() { } var person = new Person(); console.log(person.__proto__ == Person.prototype) // true console.log(Person.prototype.constructor == Person) // true // 順便學(xué)習(xí)一個(gè)ES5的方法,可以獲得對(duì)象的原型 console.log(Object.getPrototypeOf(person) === Person.prototype) // true
了解完了構(gòu)造函數(shù)、實(shí)例、實(shí)例原型之間的關(guān)系,我們來(lái)講下實(shí)例與原型是怎么樣一個(gè)關(guān)系呢?
實(shí)例與原型
當(dāng)讀取上面的實(shí)例的屬性,找不到的時(shí)候,會(huì)發(fā)生什么呢?如果找不到的話,就會(huì)查找與實(shí)例相關(guān)聯(lián)的原型的屬性中查找,如果還是找不到就會(huì)去原型的原型中查找,一直找到最頂層為止。
function Person() { } Person.prototype.name = 'Kevin'; var person = new Person(); person.name = 'Daisy'; console.log(person.name) // Daisy delete person.name; console.log(person.name) // Kevin
在這個(gè)例子中,我們給實(shí)例對(duì)象 person 添加了 name 屬性,當(dāng)我們打印 person.name 的時(shí)候,結(jié)果自然為 Daisy。
但是當(dāng)我們刪除了 person 的 name 屬性時(shí),讀取 person.name,從 person 對(duì)象中找不到 name 屬性就會(huì)從 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找,幸運(yùn)的是我們找到了 name 屬性,結(jié)果為 Kevin。
但是萬(wàn)一還沒(méi)有找到呢?原型的原型又是什么呢?
其實(shí)原型對(duì)象就是通過(guò) Object 構(gòu)造函數(shù)生成的,結(jié)合之前所講,實(shí)例的 __proto__ 指向構(gòu)造函數(shù)的 prototype ,所以我們?cè)俑孪玛P(guān)系圖:
原型鏈
那 Object.prototype 的原型呢?null,我們可以打?。?/p>
console.log(Object.prototype.__proto__ === null) // true
所以O(shè)bject.prototype.__proto__ 的值為 null 跟 Object.prototype 沒(méi)有原型,其實(shí)表達(dá)了一個(gè)意思。
所以查找屬性的時(shí)候查到 Object.prototype 就可以停止查找了。
到此就分析完了,藍(lán)色的這條線其實(shí)就是原型鏈。
總結(jié)
這里有幾個(gè)點(diǎn)需要注意一下:
constructor
首先是 constructor 屬性,我們看個(gè)例子:
function Person() { } var person = new Person(); console.log(person.constructor === Person); // true
當(dāng)獲取 person.constructor 時(shí),其實(shí) person 中并沒(méi)有 constructor 屬性,當(dāng)不能讀取到constructor 屬性時(shí),會(huì)從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,所以:
person.constructor === Person.prototype.constructor
_proto_
其次是 __proto__ ,絕大部分瀏覽器都支持這個(gè)非標(biāo)準(zhǔn)的方法訪問(wèn)原型,然而它并不存在于 Person.prototype 中,實(shí)際上,它是來(lái)自于 Object.prototype ,與其說(shuō)是一個(gè)屬性,不如說(shuō)是一個(gè) getter/setter,當(dāng)使用 obj.__proto__ 時(shí),可以理解成返回了 Object.getPrototypeOf(obj)。
到此這篇關(guān)于一文搞懂JavaScript中原型與原型鏈的文章就介紹到這了,更多相關(guān)JavaScript原型 原型鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中日期的相關(guān)操作方法總結(jié)
這篇文章主要介紹了JavaScript中日期的相關(guān)操作方法總結(jié),是JS入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10實(shí)例講解JavaScript中的this指向錯(cuò)誤解決方法
JavaScript中this指向的處理是令大家普遍頭疼的問(wèn)題,這里我們舉一個(gè)實(shí)例講解JavaScript中的this指向錯(cuò)誤解決方法,需要的朋友可以參考下2016-06-06JS中構(gòu)造函數(shù)的基本特性與優(yōu)缺點(diǎn)
這篇文章介紹了JS中構(gòu)造函數(shù)的基本特性與優(yōu)缺點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06javascript indexOf方法、lastIndexOf 方法和substring 方法
indexOf() 方法可返回某個(gè)指定的字符串值在字符串中首次出現(xiàn)的位置。2009-03-03javascript類型系統(tǒng)——日期Date對(duì)象全面了解
下面小編就為大家?guī)?lái)一篇javascript類型系統(tǒng)——日期Date對(duì)象全面了解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07Javascript基礎(chǔ)教程之while語(yǔ)句
這篇文章主要介紹了Javascript基礎(chǔ)教程之while語(yǔ)句的相關(guān)資料,需要的朋友可以參考下2015-01-01Javascript學(xué)習(xí)筆記5 類和對(duì)象
首先,不得不說(shuō),我無(wú)法達(dá)到拋開(kāi)類和對(duì)象的概念來(lái)看Javascript的境界,對(duì)于Javascript是否是面向?qū)ο蟮恼f(shuō)法有很多,不過(guò)我最認(rèn)同的還是Javascript是一種“基于prototype的面向?qū)ο笳Z(yǔ)言”。2010-01-01深入理解JavaScript系列(45):代碼復(fù)用模式(避免篇)詳解
這篇文章主要介紹了深入理解JavaScript系列(45):代碼復(fù)用模式(避免篇)詳解,本文講解了默認(rèn)模式、借用構(gòu)造函數(shù)、借用構(gòu)造函數(shù)并設(shè)置原型、共享原型、臨時(shí)構(gòu)造函數(shù)、klass等內(nèi)容,需要的朋友可以參考下2015-03-03簡(jiǎn)單總結(jié)JavaScript中的String字符串類型
就像其他語(yǔ)言那樣,js中的字符串類型可以表示一串字符,由雙引號(hào)包住,這里簡(jiǎn)單總結(jié)JavaScript中的String字符串類型的一些基礎(chǔ)知識(shí)2016-05-05