JavaScript中的原型(Prototype)機(jī)制是其面向?qū)ο缶幊痰暮诵奶匦灾?,它允許對(duì)象之間共享屬性和方法,從而實(shí)現(xiàn)繼承。本文將從淺入深地解析原型、原型鏈、以及一些特殊情況下的應(yīng)用,旨在讓你對(duì)這一概念有更清晰的理解。
原型(顯示原型)
每個(gè)JavaScript函數(shù)都有一個(gè)名為prototype
的屬性,這是一個(gè)對(duì)象,用于存儲(chǔ)所有通過(guò)該函數(shù)構(gòu)造出的對(duì)象所共享的屬性和方法。當(dāng)使用new
關(guān)鍵字基于某個(gè)構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例化對(duì)象時(shí),這個(gè)實(shí)例化對(duì)象會(huì)自動(dòng)鏈接到構(gòu)造函數(shù)的prototype
對(duì)象上,這便是原型繼承的基礎(chǔ)。而該原型指的是函數(shù)的原型prototype
。
function Person() {}
Person.prototype.say = function() { console.log('Hello'); };
在這個(gè)例子中,所有通過(guò)new Person()
創(chuàng)建的實(shí)例化對(duì)象都會(huì)繼承構(gòu)造函數(shù)的原型上的say
方法。
隱式原型和原型鏈
每個(gè)JavaScript對(duì)象(除null
外)都有一個(gè)內(nèi)部屬性[[Prototype]]
,通??梢酝ㄟ^(guò)__proto__
訪問(wèn)(隱式原型__proto__
指向的是對(duì)象的顯示原型prototype
)。當(dāng)訪問(wèn)一個(gè)對(duì)象的屬性或方法時(shí),如果對(duì)象本身沒(méi)有,則引擎會(huì)繼續(xù)在其[[Prototype]]
指向的對(duì)象中尋找,這一過(guò)程形成了一條鏈,即原型鏈。
對(duì)于p
來(lái)說(shuō),其原型鏈上首先查找自身的屬性,若未找到,則會(huì)沿著p.__proto__ -> Person.prototype -> Object.prototype -> null
這條鏈進(jìn)行查找。
也就是說(shuō),v8在查找對(duì)象的屬性時(shí),如果沒(méi)找到,就會(huì)順著對(duì)象的隱式原型往上查找,如果還找不到,再順著隱式原型的隱式原型往上找,直到找到null為止,在這個(gè)過(guò)程中,但凡有一個(gè)步驟能找到,就會(huì)返回值。這個(gè)鏈狀的查找過(guò)程稱為原型鏈
是不是所有的對(duì)象都有原型??
答案當(dāng)然是,并非所有對(duì)象都有原型。使用Object.create(null)
可以創(chuàng)建一個(gè)沒(méi)有原型(__proto__
會(huì)指向null
)的對(duì)象,這樣的對(duì)象不會(huì)繼承任何默認(rèn)的Object方法。
修改原型的屬性
實(shí)例化對(duì)象是不可以修改原型上的屬性的
Person.prototype.like = '聽(tīng)歌';
let p = new Person();
p.like = '擼鐵';
let s = new Person()
console.log(p)
console.log(s.like)
由后面實(shí)例化出來(lái)的s
對(duì)象中可以看出,當(dāng)執(zhí)行p.like
的時(shí)候,并不是修改原型上的屬性,而是在p
對(duì)象上添加一個(gè)like
屬性并賦值為擼鐵
。
多級(jí)繼承示例
多級(jí)繼承展示了如何通過(guò)原型鏈實(shí)現(xiàn)深層次的屬性和方法繼承,每個(gè)子類通過(guò)其原型鏈連接到父類的原型。
Grand.prototype.lastname = '張';
function Grand(){
this.name='三'
}
Father.prototype=new Grand()
function Father(){
this.age=40
}
Son.prototype=new Father()
function Son(){
this.like='coding'
}
let son = new Son();
// console.log(son.like)
// console.log(son.age)
console.log(son.name)
下面我將描述出輸出son.name
時(shí)的查找路線:
原型鏈的實(shí)踐與注意事項(xiàng)
修改數(shù)組的push
方法展示了原型鏈上方法覆蓋的原理。
// var arr=[1,2,3]
// arr.push(4)//1,2,3,4
// 原型鏈
Array.prototype.push = function(){
this[0]='a'
}
var arr=[1,2,3] // new Array() --> this
arr.push(4)
console.log(arr)//4,2,3
Number.prototype.toString
等內(nèi)建原型方法的調(diào)用,說(shuō)明了即使是最基本的數(shù)據(jù)類型也是基于原型鏈實(shí)現(xiàn)方法訪問(wèn)的。
var num=1// new Number()
console.log(num.toString())
構(gòu)造函數(shù)與原型對(duì)象的區(qū)分
構(gòu)造函數(shù)本身也是一個(gè)對(duì)象,它有自己的屬性和方法,這些不通過(guò)原型鏈傳遞給實(shí)例。例如,Foo.b=2
設(shè)置的是構(gòu)造函數(shù)的靜態(tài)屬性,而非原型屬性。
Foo.prototype.a=1
function Foo() {
// this={
// }
// this.__proto__ = Foo.prototype
// return this
// b=2
}
Foo.b=2
console.log(Foo);//{b:2}
let f = new Foo();
console.log(f.b);//undefined
最后
總的來(lái)說(shuō),JavaScript的原型和原型鏈機(jī)制提供了一種靈活而強(qiáng)大的對(duì)象繼承方式。理解這一機(jī)制對(duì)于深入掌握J(rèn)avaScript的面向?qū)ο缶幊讨陵P(guān)重要。通過(guò)上述示例的逐步分析,我們不僅看到了原型如何讓對(duì)象共享屬性和方法,還見(jiàn)識(shí)到了原型鏈如何構(gòu)建起對(duì)象間屬性查找的路徑。這將會(huì)使我們更好的理解為什么可以調(diào)用不屬于他身上的屬性和方法,便于我們更好的掌握這一門編程語(yǔ)言??!
作者:常樂(lè)hhh
鏈接:https://juejin.cn/post/7379897208532926518
來(lái)源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
該文章在 2024/6/17 16:32:31 編輯過(guò)