JavaScript原型鏈及常見的繼承方法
原型鏈
原型鏈的概念
在JavaScript中,每一個(gè)構(gòu)造函數(shù)都有一個(gè)原型,這個(gè)原型中有一個(gè)屬性constructor會(huì)再次指回這個(gè)構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)所創(chuàng)造的實(shí)例對(duì)象,會(huì)有一個(gè)指針(也就是我們說的隱式原型__proto__或者是瀏覽器中顯示的[[Prototype]])指向這個(gè)構(gòu)造函數(shù)的原型對(duì)象。如果說該構(gòu)造函數(shù)的原型對(duì)象也是由另外一個(gè)構(gòu)造函數(shù)所創(chuàng)造的實(shí)例,那么該構(gòu)造函數(shù)的原型對(duì)象也會(huì)存在一個(gè)指針指向另外一個(gè)構(gòu)造函數(shù)的原型對(duì)象,周而復(fù)始,就形成了一條原型鏈。 最特別的是所有的沒有經(jīng)過再繼承函數(shù)都是由Function實(shí)例化來的,所有的除了函數(shù)外的對(duì)象都是由Object實(shí)例化來的,其中Object也是由Function實(shí)例化來的,但是Object.prototype.__proto__ === null 是成立的。

再強(qiáng)調(diào)一遍:原型鏈?zhǔn)茄刂鴮?duì)象的隱式原型一層層的去尋找的,找到的是構(gòu)造函數(shù)所創(chuàng)造的實(shí)例。例如下:

這個(gè)就是相當(dāng)于由Studentnew 出來的實(shí)例s,查找自身的 name 屬性,然后沿著原型鏈查找,找到Student中的prototype當(dāng)中,然后找到了name這個(gè)屬性。

而這個(gè)例子,由紅框框起來的代碼(寄生繼承的關(guān)鍵代碼),代替注釋掉的部分,最終s是找不到name屬性的,這是因?yàn)榧t框中的代碼,僅僅是將Student的隱式原型指向了Person的顯示原型對(duì)象,未能創(chuàng)建任何的實(shí)例,當(dāng)然就不會(huì)存在屬性這個(gè)說法。
原型鏈的問題
原型鏈的問題主要有兩個(gè)方面,第一個(gè)問題是,當(dāng)原型中出現(xiàn)包含引用值(比如數(shù)組)的時(shí)候,所有在這條原型鏈中的實(shí)例會(huì)共享這個(gè)屬性,造成“一發(fā)而動(dòng)全身”的問題。第二個(gè)問題就是子類在實(shí)例化時(shí),不能夠給父類型的構(gòu)造函數(shù)傳參,即
無法在不影響所有對(duì)象實(shí)例的情況下把參數(shù)傳遞進(jìn)父類型的構(gòu)造函數(shù)傳參
幾種常見的繼承方法
盜用構(gòu)造函數(shù)
function SuperType() {
this.friends = ['張三','李四']
}
function SubType() {
SuperType.call(this);
}
const p1 = new SubType();
p1.friends.push('王武');
const p2 = new SubType();
console.log(p2.friends); // ['張三','李四', '王武']盜用構(gòu)造函數(shù)實(shí)現(xiàn)繼承在這個(gè)例子中有了充分的體現(xiàn): 首先在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù)。因?yàn)楫吘购瘮?shù)就是特定上下文中執(zhí)行代碼的簡(jiǎn)單對(duì)象,所以可以使用call()方法以創(chuàng)建的對(duì)象為上下文執(zhí)行的構(gòu)造函數(shù)。
盜用構(gòu)造函數(shù)的主要問題,也是創(chuàng)建對(duì)象的幾種方式中構(gòu)造函數(shù)模式自定義類型的問題:必須在構(gòu)造函數(shù)中定義方法,造成內(nèi)存浪費(fèi)。另外,子類也不能訪問父類原型上定義的方法,因此,盜用構(gòu)造函數(shù)也不會(huì)單獨(dú)使用。
組合繼承
組合繼承也稱為偽經(jīng)典繼承,綜合了原型鏈和構(gòu)造函數(shù),將兩者的有點(diǎn)結(jié)合起來?;镜乃悸肪褪鞘褂迷玩溊^承原型上的屬性和方法,而通過盜用構(gòu)造函數(shù)繼承實(shí)現(xiàn)實(shí)例的屬性。這樣就可以把方法定義在原型上實(shí)現(xiàn)復(fù)用,又可以讓每個(gè)實(shí)例有自己的屬性。
function SuperType(name) {
this.name = name;
this.friends = ['張三','李四'];
}
SuperType.prototype.sayName = function() {
console.log(this.name)
}
// 繼承方法
SubType.prototype = new SuperType();
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
const p1 = new SubType('趙六', 12);
const p2 = new SubType('趙六2', 22);
// 創(chuàng)建的 p1 和 p2 能夠擁有自己的屬性并且引用值屬性也是獨(dú)立的,此外,每一個(gè)實(shí)例能夠公用父類的方法。組合繼承已經(jīng)接近完美了,但是,我們發(fā)現(xiàn),實(shí)現(xiàn)組合繼承就要調(diào)用兩次父類構(gòu)造函數(shù)。在本質(zhì)上,子類型最終是要包含超類對(duì)象的所有實(shí)例屬性,子類構(gòu)造函數(shù)只要在執(zhí)行時(shí)重寫自己的原型就行了,這就為減少一次調(diào)用父類構(gòu)造函數(shù)提供了思路。
原型式繼承
const person = {
name: 'zs',
friends: ['ls','ww']
}
// 創(chuàng)造出一個(gè)實(shí)例,這個(gè)實(shí)例的隱式原型指向 person
const anotherPerosn = Object.create(person);
anotherPerosn.name = 'xm'
anotherPerosn.friends.push('zl')
console.log(anotherPerosn.name) // xm
console.log(anotherPerosn.friends) // ['ls','ww', 'zl'];
const anotherPerosn2 = Object.create(person);
anotherPerosn.name = 'xh'
anotherPerosn.friends.push('dd')
console.log(anotherPerosn2.name) // xh
console.log(anotherPerosn2.friends) // ['ls','ww', 'zl', 'dd'];對(duì)于原型鏈繼承就不再過多的解釋了。。。。
寄生式繼承
寄生式繼承與原型式繼承比較相似,都會(huì)存在屬性引用值共享的問題。
function createAnotherPerson(original) {
const clone = Object.create(original); //通過調(diào)用函數(shù)創(chuàng)建一個(gè)新的對(duì)象
clone.sayHi = function() { // 以某種方式增強(qiáng)這個(gè)對(duì)象
console.log('Hi');
}
return clone;
}寄生式繼承,不僅存在著屬性引用值共享的問題而且函數(shù)還不能進(jìn)行復(fù)用。
寄生組合式繼承
// 實(shí)現(xiàn)了寄生式組合繼承的核心邏輯
function inheritPrototype(subFn, parentFn){
subFn.prototype = Object.create(parentFn.prototype); // 創(chuàng)建賦值對(duì)象
Object.defineProperty(subFn.prototype,'constructor', { // 增強(qiáng)對(duì)象
enumerable: false,
writable: false,
configurable: false,
value: subFn,
})
}
function Person(name, age, address) {
this.name = name;
this.age = age;
this.address = address;
}
Person.prototype.eating = function() {
console.log(this.name + "正在吃飯");
} // 共享方法
function Student(name, age, address, sno) {
Person.call(this, name, age, address); // 綁定 this 確保創(chuàng)建出來的對(duì)象是相互獨(dú)立的
this.sno = sno;
this.studing = function() {
console.log(`${this.name}正在學(xué)習(xí)`)
}
}
function Teacher(name, age, address, tno) {
Person.call(this, name, age, address)
this.tno = tno;
}寄生+組合式(構(gòu)造函數(shù)+原型鏈)完美的解決了其他繼承出現(xiàn)的問題。
到此這篇關(guān)于JavaScript原型鏈及常見的繼承方法的文章就介紹到這了,更多相關(guān)JS原型鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript在for循環(huán)中綁定事件解決事件參數(shù)不同的情況
響應(yīng)一堆相似的事件,但是每個(gè)事件的參數(shù)都不同,在這種情況下就可以使用JavaScript 在for循環(huán)中綁定事件,下面有個(gè)不錯(cuò)的示例,大家可以參考下2014-01-01
Javascript實(shí)現(xiàn)base64的加密解密方法示例
下文是base64用javascript寫出來的函數(shù)和方法。非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧2017-06-06
基于JavaScript實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)頁面
這篇文章主要為大家詳細(xì)介紹了基于JavaScript實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)頁面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
微信小程序?qū)崿F(xiàn)圖片滾動(dòng)效果示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)圖片滾動(dòng)效果,結(jié)合實(shí)例形式分析了微信小程序基于swiper組件的圖片滾動(dòng)效果相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-12-12
JavaScript中數(shù)字計(jì)算時(shí)丟失精度問題解決方法
在前端開發(fā)中,精度丟失是一個(gè)常見的問題,特別是在涉及到浮點(diǎn)數(shù)計(jì)算時(shí),下面這篇文章主要給大家介紹了關(guān)于JavaScript中數(shù)字計(jì)算時(shí)丟失精度問題的解決方法,需要的朋友可以參考下2024-09-09
scrapyd schedule.json setting 傳入多個(gè)值問題
這篇文章主要介紹了scrapyd schedule.json setting 傳入多個(gè)值,本文給出了問題分析及思路解決方案,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2019-08-08
微信小程序?qū)崿F(xiàn)滑動(dòng)/點(diǎn)擊切換Tab及scroll-left的使用
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)滑動(dòng)/點(diǎn)擊切換Tab,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
你所不了解的javascript操作DOM的細(xì)節(jié)知識(shí)點(diǎn)(一)
這篇文章主要介紹了你所不了解的javascript操作DOM的細(xì)節(jié)知識(shí)點(diǎn)的相關(guān)資料,需要的朋友可以參考下2015-06-06
JavaScript動(dòng)態(tài)插入script的基本思路及實(shí)現(xiàn)函數(shù)
偶爾需要?jiǎng)討B(tài)插入javascript代碼的需求,基本思路是動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽,設(shè)置其src屬性,type屬性等,需要的朋友可以參考下2013-11-11

