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