JavaScript 原型與原型鏈詳情
前言:
JavaScript常被描述為一種「基于原型的語言」——每個(gè)對象都擁有一個(gè)「原型對象」,對象以其原型為模板、從原型繼承屬性和放法。原型對象也可能擁有原型,并從中繼承屬性和方法,一層一層以此類推。這種關(guān)系常被稱為「原型鏈」,它解釋了為何一個(gè)對象會擁有定義在其他對象中的屬性和方法。
準(zhǔn)確的說,這些屬性和方法定義在Object的構(gòu)造函數(shù)的prototype屬性上,而非對象實(shí)例本身。
四句話道破原型與原型鏈:
- 每個(gè)函數(shù)(類)天生自帶一個(gè)屬性
prototype,屬性值是一個(gè)對象,里面存儲了當(dāng)前類供實(shí)例使用的屬性和方法 「(顯示原型)」 - 在瀏覽器默認(rèn)給原型開辟的堆內(nèi)存中有一個(gè)
constructor屬性:存儲的是當(dāng)前類本身(⚠️注意:自己開辟的堆內(nèi)存中默認(rèn)沒有constructor屬性,需要自己手動添加)「(構(gòu)造函數(shù))」 - 每個(gè)對象都有一個(gè)
__proto__屬性,這個(gè)屬性指向當(dāng)前實(shí)例所屬類的原型(不確定所屬類,都指向Object.prototype)「(隱式原型)」 - 當(dāng)你試圖獲取一個(gè)對象的某個(gè)屬性時(shí),如果這個(gè)對象本身沒有這個(gè)屬性,那么它會去它的隱式原型__proto__(也就是它的構(gòu)造函數(shù)的顯示原型
prototype)中查找?!福ㄔ玩湥?br />
構(gòu)造函數(shù),原型與實(shí)例的關(guān)系:
每個(gè)構(gòu)造函數(shù)(constructor)都有一個(gè)原型對象(prototype),原型對象(prototype)都包含一個(gè)指向構(gòu)造函數(shù)(constructor)的指針,而實(shí)例(instance)都包含一個(gè)指向原型對象(__proto__)的內(nèi)部指針
1、prototype(顯式原型)
每個(gè)函數(shù)都有一個(gè)prototype屬性
// 構(gòu)造函數(shù)(類)
function Person(name){
this.name = name
}
// new了一個(gè)實(shí)例 (對象)
var person = new Person('南玖')
console.log(person) //Person { name: '南玖' }
console.log(Person.prototype) //構(gòu)造函數(shù)(類)的原型 ----->對象
Person.prototype.age = 18 // 構(gòu)造函數(shù)原型
console.log(person.age) // 18
上面我們把這個(gè)函數(shù)Person的原型打印出來了,它指向的是一個(gè)對象,并且這個(gè)對象正是調(diào)用該構(gòu)造函數(shù)而創(chuàng)建的實(shí)例的原型

上面這張圖表示的是構(gòu)造函數(shù)與實(shí)例原型之間的關(guān)系,所以我們知道了構(gòu)造函數(shù)的prototype屬性指向的是一個(gè)對象。
那實(shí)例與實(shí)例原型之間的關(guān)系又是怎樣的呢?這里就要提到__proto__屬性了
2、__proto__(隱式原型)
從上面四句話中我們可以知道這是每一個(gè)Javascript對象(除null)都具有的一個(gè)屬性,這個(gè)屬性會指向該對象的原型(也就是實(shí)例原型)
因?yàn)樵?code>JavaScript中沒有類的概念,為了實(shí)現(xiàn)類似繼承的方式,通過__proto__將對象和原型聯(lián)系起來組成原型鏈,的以讓對象訪問到不屬于自己的屬性。
那么我們就能夠證明實(shí)例與實(shí)例原型之間的關(guān)系
console.log(person.__proto__) //實(shí)例(對象)的原型--->對象 console.log(person.__proto__ === Person.prototype) //實(shí)例的原型與構(gòu)造函數(shù)的原型相等

從上圖我們可以看出實(shí)例對象與構(gòu)造函數(shù)都可以指向原型,那么原型能不能指向構(gòu)造函數(shù)或者是實(shí)例呢?
3、constructor(構(gòu)造函數(shù))
原型是沒有屬性指向?qū)嵗?,因?yàn)橐粋€(gè)構(gòu)造函數(shù)可以創(chuàng)建多個(gè)實(shí)例對象;
從前面的四句話中我們知道「在瀏覽器默認(rèn)給原型開辟的堆內(nèi)存中有一個(gè)constructor屬性」,所以原型也是可以指向構(gòu)造函數(shù)的,這個(gè)屬性就是「constructor」
于是我們可以證明一下觀點(diǎn):
console.log(Person.prototype.constructor) //實(shí)例的顯式原型的構(gòu)造函數(shù)ƒ Person(name){this.name = name}
console.log(person.__proto__.constructor) //實(shí)例的隱式原型的構(gòu)造函數(shù) ƒ Person(name){this.name = name}
console.log(person.__proto__.constructor === Person.prototype.constructor)//true 實(shí)例原型的構(gòu)造函數(shù)與類的構(gòu)造函數(shù)相等
console.log(Person === Person.prototype.constructor) //true

實(shí)例對象的__proto__是如何產(chǎn)生的?
我們知道當(dāng)我們使用new 操作符時(shí),生成的實(shí)例對象就擁有了__proto__屬性
function Foo() {}
// 這個(gè)函數(shù)時(shí)Function的實(shí)例對象
// function是一個(gè)語法糖
// 內(nèi)部其實(shí)調(diào)用了new Function()
所以可以說,在new的過程中,新對象被添加了__proto__屬性并且鏈接到了構(gòu)造函數(shù)的原型上。
4、new的原理
說簡單點(diǎn)可以分為以下四步:
- 新建一個(gè)空對象
- 鏈接原型
- 綁定this,執(zhí)行構(gòu)造函數(shù)
- 返回新對象
function myNew() {
// 1.新建一個(gè)空對象
let obj = {}
// 2.獲得構(gòu)造函數(shù)
let con = arguments.__proto__.constructor
// 3.鏈接原型
obj.__proto__ = con.prototype
// 4.綁定this,執(zhí)行構(gòu)造函數(shù)
let res = con.apply(obj, arguments)
// 5.返回新對象
return typeof res === 'object' ? res : obj
}
5、原型鏈
說完了原型,我們再來看看什么是原型鏈?先來看一張圖:

這張圖中,由__proto__串起來的鏈?zhǔn)疥P(guān)系,我們就稱它為原型鏈
5.1 原型鏈的作用
原型鏈決定了JavaScript中繼承的實(shí)現(xiàn)方式,當(dāng)我們訪問一個(gè)屬性時(shí),它的查找機(jī)制如下:
- 訪問對象實(shí)例屬性,有的話直接返回,沒有則通過
__proto__去它的原型對象上查找 - 原型對象上能找到的話則返回,找不到繼續(xù)通過原型對象的
__proto__查找 - 一直往下找,直到找到Object.prototype,如果能找到則返回,找不到就返回
undefined,不會再往下找了,因?yàn)?code>Object.prototype.__proto__是null,說明了Object是所有對象的原型鏈頂層了。
從圖中我們可以發(fā)現(xiàn),所有對象都可以通過原型鏈最終找到 Object.prototype ,雖然 Object.prototype 也是一個(gè)對象,但是這個(gè)對象卻不是 Object 創(chuàng)造的,而是引擎自己創(chuàng)建了 Object.prototype 。所以可以這樣說,所有實(shí)例都是對象,但是對象不一定都是實(shí)例。
5.2 構(gòu)造函數(shù)的__proto__是什么呢?
由上面的原型鏈的解釋,我們應(yīng)該能夠理解構(gòu)造函數(shù)的__proto__的,在JavaScript中所有東西都是對象,那么構(gòu)造函數(shù)肯定也是對象,是對象就有__proto__ 。
function Person(){}
console.log(Person.__proto__)
console.log(Function.prototype)
console.log(Person.__proto__===Function.prototype) // true
「這也說明了所有函數(shù)都是Function的實(shí)例」
那這么理解的話,Function.__proto__豈不是等于Function.prototype。。。。我們不妨來打印一下看看
Function.__proto__ === Function.prototype // true
打印出來確實(shí)是這樣的。難道 Function.prototype 也是通過 new Function() 產(chǎn)生的嗎?
答案是否定的,這個(gè)函數(shù)也是引擎自己創(chuàng)建的。首先引擎創(chuàng)建了 Object.prototype ,然后創(chuàng)建了 Function.prototype ,并且通過 __proto__ 將兩者聯(lián)系了起來。這里也很好的解釋了上面的一個(gè)問題,為什么 let fun = Function.prototype.bind() 沒有 prototype 屬性。因?yàn)?Function.prototype 是引擎創(chuàng)建出來的對象,引擎認(rèn)為不需要給這個(gè)對象添加 prototype 屬性。
6、總結(jié)
Object是所有對象的爸爸,所有對象都可以通過__proto__找到它Function是所有函數(shù)的爸爸,所有函數(shù)都可以通過__proto__找到它Function.prototype和Object.prototype是兩個(gè)特殊的對象,他們由引擎來創(chuàng)建- 除了以上兩個(gè)特殊對象,其他對象都是通過構(gòu)造器 new 出來的
- 函數(shù)的
prototype是一個(gè)對象,也就是原型 - 對象的
__proto__指向原型,__proto__將對象和原型連接起來組成了原型鏈
到此這篇關(guān)于JavaScript 原型與原型鏈詳情的文章就介紹到這了,更多相關(guān)JavaScript 原型與原型鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS開發(fā)Blob和FileReader構(gòu)造函數(shù)使用示例詳解
這篇文章主要為大家介紹了JS開發(fā)Blob和FileReader構(gòu)造函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
JavaScript與JQuery框架基礎(chǔ)入門教程
這篇文章主要介紹了jQuery和JavaScript入門基礎(chǔ)知識學(xué)習(xí)指南,jQuery是當(dāng)下最主流人氣最高的JavaScript庫,需要的朋友可以參考下2021-07-07
javascript 操作cookies詳解及實(shí)例
這篇文章主要介紹了javascript 操作cookies詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02

