詳解js繼承

詳解js繼承

來(lái)源視頻講解:https://www.bilibili.com/video/BV1J54y1y7u9/?spm_id_from=333.337.search-card.all.click&vd_source=575ceee169404991816c658859de0f7f

第?部分:預(yù)備知識(shí)
1、構(gòu)造函數(shù)的屬性

funcion A(name) {
  this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
  this.arr = [1]; // 實(shí)例引?屬性 (該屬性,強(qiáng)調(diào)私?,不共享)
  this.say = function() { // 實(shí)例引?屬性 (該屬性,強(qiáng)調(diào)復(fù)?,需要共享)
    console.log('hello')
  }
}

注意:數(shù)組和?法都屬于‘實(shí)例引?屬性’,但是數(shù)組強(qiáng)調(diào)私有、不共享的。?法需要復(fù)?、共享。
構(gòu)造函數(shù)中,很少有數(shù)組形式的引?屬性,?部分情況都是:基本屬性 + ?法。
2、什么是原型對(duì)象
簡(jiǎn)單來(lái)說(shuō),每個(gè)函數(shù)都有prototype屬性,它就是原型對(duì)象,通過(guò)函數(shù)實(shí)例化出來(lái)的對(duì)象有proto屬性,指向原型對(duì)象。
let a = new A()
a.proto == A.prototype
// prototype的結(jié)構(gòu)如下
A.prototype = {
constructor: A,
...其他的原型屬性和?法
}
3、原型對(duì)象的作?
原型對(duì)象的?途是為每個(gè)實(shí)例對(duì)象存儲(chǔ)共享的?法和屬性,它僅僅是?個(gè)普通對(duì)象。并且所有的實(shí)例是共享同?個(gè)原型對(duì)象,因此有別于實(shí)例?法或?qū)傩?,原型?duì)象僅有?份。?實(shí)例有很多份,且實(shí)例屬性和?法是獨(dú)?的。在構(gòu)造函數(shù)中:為了屬性(實(shí)例基本屬性)的私有性、以及?法(實(shí)例引?屬性)的復(fù)?、共享。我們提倡:
將屬性封裝在構(gòu)造函數(shù)中
將?法定義在原型對(duì)象上
詳解js繼承 2
funcion A(name) {
this.name = name; // (該屬性,強(qiáng)調(diào)私有,不共享)
}
A.prototype.say = function() { // 定義在原型對(duì)象上的?法 (強(qiáng)調(diào)復(fù)?,需要共享)
console.log('hello')
}
// 不推薦的寫(xiě)法:原因
A.prototype = {
say: function() {
console.log('hello')
}
}
第?部分:五種js 繼承?式
?式1、原型鏈繼承
核?:將?類(lèi)實(shí)例作為?類(lèi)原型
優(yōu)點(diǎn):?法復(fù)?
由于?法定義在?類(lèi)的原型上,復(fù)?了?類(lèi)構(gòu)造函數(shù)的?法。?如say?法。
缺點(diǎn):
創(chuàng)建?類(lèi)實(shí)例的時(shí)候,不能傳?類(lèi)的參數(shù)(?如name)。
?類(lèi)實(shí)例共享了?類(lèi)構(gòu)造函數(shù)的引?屬性,?如arr屬性。
?法實(shí)現(xiàn)多繼承。
function Parent(name) {
this.name = name || '?親'; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // -- 將需要復(fù)?、共享的?法定義在?類(lèi)原型上
console.log('hello')
}
function Child(like) {
this.like = like;
}
Child.prototype = new Parent() // 核?,但此時(shí)Child.prototype.constructor==Parent
Child.prototype.constructor = Child // 修正constructor指向
let boy1 = new Child()
let boy2 = new Child()
// 優(yōu)點(diǎn):共享了?類(lèi)構(gòu)造函數(shù)的say?法
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say); // hello , hello , true
// 缺點(diǎn)1:不能向?類(lèi)構(gòu)造函數(shù)傳參
console.log(boy1.name, boy2.name, boy1.name===boy2.name); // ?親,?親,true
詳解js繼承 3
// 缺點(diǎn)2: ?類(lèi)實(shí)例共享了?類(lèi)構(gòu)造函數(shù)的引?屬性,?如arr屬性
boy1.arr.push(2);
// 修改了boy1的arr屬性,boy2的arr屬性,也會(huì)變化,因?yàn)閮蓚€(gè)實(shí)例的原型上(Child.prototype)有了?類(lèi)構(gòu)造函數(shù)的實(shí)例屬性arr;
所以只要修改了boy1.arr,boy2.arr的屬性也會(huì)變化。
console.log(boy2.arr); // [1,2]
注意1:修改boy1的name屬性,是不會(huì)影響到boy2.name。因?yàn)樵O(shè)置boy1.name相當(dāng)于在?類(lèi)實(shí)例新增了name屬性。
注意2:
console.log(boy1.constructor); // Parent 你會(huì)發(fā)現(xiàn)實(shí)例的構(gòu)造函數(shù)居然是Parent。
?實(shí)際上,我們希望?類(lèi)實(shí)例的構(gòu)造函數(shù)是Child,所以要記得修復(fù)構(gòu)造函數(shù)指向。
修復(fù)如下:Child.prototype.constructor = Child;
?式2、借?構(gòu)造函數(shù)
核?:借??類(lèi)的構(gòu)造函數(shù)來(lái)增強(qiáng)?類(lèi)實(shí)例,等于是復(fù)制?類(lèi)的實(shí)例屬性給?類(lèi)。
優(yōu)點(diǎn):實(shí)例之間獨(dú)?。
創(chuàng)建?類(lèi)實(shí)例,可以向?類(lèi)構(gòu)造函數(shù)傳參數(shù)。
?類(lèi)實(shí)例不共享?類(lèi)構(gòu)造函數(shù)的引?屬性。如arr屬性
可實(shí)現(xiàn)多繼承(通過(guò)多個(gè)call或者apply繼承多個(gè)?類(lèi))
缺點(diǎn):
?類(lèi)的?法不能復(fù)?
由于?法在?構(gòu)造函數(shù)中定義,導(dǎo)致?法不能復(fù)?(因?yàn)槊看蝿?chuàng)建?類(lèi)實(shí)例都要?jiǎng)?chuàng)建?遍?法)。
?如say?法。(?法應(yīng)該要復(fù)?、共享)
?類(lèi)實(shí)例,繼承不了?類(lèi)原型上的屬性。(因?yàn)闆](méi)有?到原型)
function Parent(name) {
this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
this.say = function() { // 實(shí)例引?屬性 (該屬性,強(qiáng)調(diào)復(fù)?,需要共享)
console.log('hello')
}
}
function Child(name,like) {
Parent.call(this,name); // 核? 拷?了?類(lèi)的實(shí)例屬性和?法
this.like = like;
}
let boy1 = new Child('?紅','apple');
let boy2 = new Child('?明', 'orange ');
// 優(yōu)點(diǎn)1:可向?類(lèi)構(gòu)造函數(shù)傳參
console.log(boy1.name, boy2.name); // ?紅, ?明
// 優(yōu)點(diǎn)2:不共享?類(lèi)構(gòu)造函數(shù)的引?屬性
boy1.arr.push(2);
console.log(boy1.arr,boy2.arr);// [1,2] [1]
詳解js繼承 4
// 缺點(diǎn)1:?法不能復(fù)?
console.log(boy1.say === boy2.say) // false (說(shuō)明,boy1和boy2的say?法是獨(dú)?,不是共享的)
// 缺點(diǎn)2:不能繼承?類(lèi)原型上的?法
Parent.prototype.walk = function () { // 在?類(lèi)的原型對(duì)象上定義?個(gè)walk?法。
console.log('我會(huì)?路')
}
boy1.walk; // undefined (說(shuō)明實(shí)例,不能獲得?類(lèi)原型上的?法)
?式3、組合繼承
核?:通過(guò)調(diào)??類(lèi)構(gòu)造函數(shù),繼承?類(lèi)的屬性并保留傳參的優(yōu)點(diǎn);然后通過(guò)將?類(lèi)實(shí)例作為
?類(lèi)原型,實(shí)現(xiàn)函數(shù)復(fù)?。
優(yōu)點(diǎn):
保留構(gòu)造函數(shù)的優(yōu)點(diǎn):創(chuàng)建?類(lèi)實(shí)例,可以向?類(lèi)構(gòu)造函數(shù)傳參數(shù)。
保留原型鏈的優(yōu)點(diǎn):?類(lèi)的?法定義在?類(lèi)的原型對(duì)象上,可以實(shí)現(xiàn)?法復(fù)?。
不共享?類(lèi)的引?屬性。?如arr屬性
缺點(diǎn):
由于調(diào)?了2次?類(lèi)的構(gòu)造?法,會(huì)存在?份多余的?類(lèi)實(shí)例屬性,具體原因??末。
注意:'組合繼承'這種?式,要記得修復(fù)Child.prototype.constructor指向
第?次Parent.call(this);從?類(lèi)拷??份?類(lèi)實(shí)例屬性,作為?類(lèi)的實(shí)例屬性,第?次
Child.prototype = new Parent();創(chuàng)建?類(lèi)實(shí)例作為?類(lèi)原型,Child.protype中的?類(lèi)屬性和?法
會(huì)被第?次拷?來(lái)的實(shí)例屬性屏蔽掉,所以多余。
function Parent(name) {
this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)?、共享的?法定義在?類(lèi)原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 核? 第?次
this.like = like;
}
Child.prototype = new Parent() // 核? 第?次
Child.prototype.constructor = Child // 修正constructor指向
let boy1 = new Child('?紅','apple')
let boy2 = new Child('?明','orange')
// 優(yōu)點(diǎn)1:可以向?類(lèi)構(gòu)造函數(shù)傳參數(shù)
console.log(boy1.name,boy1.like); // ?紅,apple
// 優(yōu)點(diǎn)2:可復(fù)??類(lèi)原型上的?法
console.log(boy1.say === boy2.say) // true
詳解js繼承 5
// 優(yōu)點(diǎn)3:不共享?類(lèi)的引?屬性,如arr屬性
boy1.arr.push(2)
console.log(boy1.arr,boy2.arr); // [1,2] [1] 可以看出沒(méi)有共享arr屬性。
// 缺點(diǎn)1:由于調(diào)?了2次?類(lèi)的構(gòu)造?法,會(huì)存在?份多余的?類(lèi)實(shí)例屬性
其實(shí)Child.prototype = new Parent()
console.log(Child.prototype.proto === Parent.prototype); // true
因?yàn)镃hild.prototype等于Parent的實(shí)例,所以proto指向Parent.prototype
?式4、組合繼承優(yōu)化1
核?:
通過(guò)這種?式,砍掉?類(lèi)的實(shí)例屬性,這樣在調(diào)??類(lèi)的構(gòu)造函數(shù)的時(shí)候,就不會(huì)初始化兩次實(shí)
例,避免組合繼承的缺點(diǎn)。
優(yōu)點(diǎn):
只調(diào)??次?類(lèi)構(gòu)造函數(shù)。
保留構(gòu)造函數(shù)的優(yōu)點(diǎn):創(chuàng)建?類(lèi)實(shí)例,可以向?類(lèi)構(gòu)造函數(shù)傳參數(shù)。
保留原型鏈的優(yōu)點(diǎn):?類(lèi)的實(shí)例?法定義在?類(lèi)的原型對(duì)象上,可以實(shí)現(xiàn)?法復(fù)?。
缺點(diǎn):
修正構(gòu)造函數(shù)的指向之后,?類(lèi)實(shí)例的構(gòu)造函數(shù)指向,同時(shí)也發(fā)?變化(這是我們不希望的)
注意:'組合繼承優(yōu)化1'這種?式,要記得修復(fù)Child.prototype.constructor指向
原因是:不能判斷?類(lèi)實(shí)例的直接構(gòu)造函數(shù),到底是?類(lèi)構(gòu)造函數(shù)還是?類(lèi)構(gòu)造函數(shù)。
function Parent(name) {
this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)?、共享的?法定義在?類(lèi)原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 核?
this.like = like;
}
Child.prototype = Parent.prototype // 核? ?類(lèi)原型和?類(lèi)原型,實(shí)質(zhì)上是同?個(gè)

Child.prototype.constructor = Child
詳解js繼承 6
let boy1 = new Child('?紅','apple')
let boy2 = new Child('?明','orange')
let p1 = new Parent('?爸爸')
// 優(yōu)點(diǎn)1:可以向?類(lèi)構(gòu)造函數(shù)傳參數(shù)
console.log(boy1.name,boy1.like); // ?紅,apple
// 優(yōu)點(diǎn)2:可復(fù)??類(lèi)原型上的?法
console.log(boy1.say === boy2.say) // true
// 缺點(diǎn)1:當(dāng)修復(fù)?類(lèi)構(gòu)造函數(shù)的指向后,?類(lèi)實(shí)例的構(gòu)造函數(shù)指向也會(huì)跟著變了。
沒(méi)修復(fù)之前:console.log(boy1.constructor); // Parent
修復(fù)代碼:Child.prototype.constructor = Child
修復(fù)之后:console.log(boy1.constructor); // Child
console.log(p1.constructor);// Child 這?就是存在的問(wèn)題(我們希望是Parent)
具體原因:因?yàn)槭峭ㄟ^(guò)原型來(lái)實(shí)現(xiàn)繼承的,Child.prototype的上?是沒(méi)有constructor屬性的,
就會(huì)往上找,這樣就找到了Parent.prototype上?的constructor屬性;當(dāng)你修改了?類(lèi)實(shí)例的
construtor屬性,所有的constructor的指向都會(huì)發(fā)?變化。
?式5、組合繼承優(yōu)化2 ?稱 寄?組合繼承 --- 完美?式
核?:
優(yōu)點(diǎn):完美
缺點(diǎn):---
function Parent(name) {
this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)?、共享的?法定義在?類(lèi)原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 核?
this.like = like;
}
// 核? 通過(guò)創(chuàng)建中間對(duì)象,?類(lèi)原型和?類(lèi)原型,就會(huì)隔離開(kāi)。不是同?個(gè)啦,有效避免了?式4的缺點(diǎn)。
Child.prototype = Object.create(Parent.prototype)
// 這?是修復(fù)構(gòu)造函數(shù)指向的代碼
Child.prototype.constructor = Child
let boy1 = new Child('?紅','apple')
let boy2 = new Child('?明','orange')
let p1 = new Parent('?爸爸')
注意:這種?法也要修復(fù)構(gòu)造函數(shù)的
修復(fù)代碼:Child.prototype.constructor = Child
詳解js繼承 7
修復(fù)之后:console.log(boy1.constructor); // Child
console.log(p1.constructor); // Parent 完美
第三部分:其他相關(guān)問(wèn)題
1、Object.create(object, propertiesObject)
Object.create()?法創(chuàng)建?個(gè)新對(duì)象,使?第?個(gè)參數(shù)來(lái)提供新創(chuàng)建對(duì)象的proto(以第?個(gè)參
數(shù)作為新對(duì)象的構(gòu)造函數(shù)的原型對(duì)象);
?法還有第?個(gè)可選參數(shù),是添加到新創(chuàng)建對(duì)象的屬性,寫(xiě)法如下。
const a = Object.create(Person.prototype, {
age: {
value: 12,
writable:true,
configurable:true,
}
})
new 與 Object.create() 的區(qū)別?
new 產(chǎn)?的實(shí)例,優(yōu)先獲取構(gòu)造函數(shù)上的屬性;構(gòu)造函數(shù)上沒(méi)有對(duì)應(yīng)的屬性,才會(huì)去原型上查找;
如果構(gòu)造函數(shù)中以及原型中都沒(méi)有對(duì)應(yīng)的屬性,就會(huì)報(bào)錯(cuò)。Object.create() 產(chǎn)?的對(duì)象,只會(huì)在原
型上進(jìn)?查找屬性,原型上沒(méi)有對(duì)應(yīng)的屬性,就會(huì)報(bào)錯(cuò)。
let Base1 = function() {
this.a = 1
}
let o1 = new Base1()
let o2 = Object.create(Base1.prototype)
console.log(o1.a); // 1
console.log(o2.a); // undefined
let Base2 = function() {}
Base2.prototype.a = 'aa'
let o3 = new Base2()
let o4 = Object.create(Base2.prototype)
console.log(o3.a); // aa
console.log(o4.a); // aa
let Base3 = function() {
this.a = 1
}
Base3.prototype.a = 'aa'
let o5 = new Base3()
let o6 = Object.create(Base3.prototype)
詳解js繼承 8
console.log(o5.a); // 1
console.log(o6.a); // aa
2、new 的過(guò)程
創(chuàng)建新對(duì)象(如obj)。
將新對(duì)象的proto指向構(gòu)造函數(shù)的prototype對(duì)象。
執(zhí)?構(gòu)造函數(shù),為這個(gè)新對(duì)象添加屬性,并將this指向創(chuàng)建的新對(duì)象obj。
當(dāng)構(gòu)造函數(shù)本?返回值為對(duì)象時(shí),返回該對(duì)象,否則返回新對(duì)象。
//創(chuàng)建Person構(gòu)造函數(shù),參數(shù)為name,age
function Person(name,age){
this.name = name;
this.age = age;
}
function _new(){
//1.拿到傳?的參數(shù)中的第?個(gè)參數(shù),即構(gòu)造函數(shù)名Func
var Func = [].shift.call(arguments);
//2.創(chuàng)建?個(gè)空對(duì)象obj,并讓其繼承Func.prototype
var obj = Object.create(Func.prototype);
//3.執(zhí)?構(gòu)造函數(shù),并將this指向創(chuàng)建的空對(duì)象obj
const result = Func.apply(obj,arguments)
//4.當(dāng)函數(shù)也有返回值且為對(duì)象時(shí)返回該對(duì)象,否則返回創(chuàng)建的新對(duì)象obj
return (result instanceOf Object ? result : obj)
}
let ming = _new(Person,'xiaoming',18);
console.log(ming);
[].shift.call表?刪除并返回auguments[0]。也可以通過(guò)以下?式取得函數(shù)名和函數(shù)的參數(shù):
function _new(Func, ...params){
...
}
Object.create創(chuàng)建obj,使得obj.proto = Func.prototype
3、為什么‘組合繼承’這種?式,會(huì)執(zhí)?兩次?類(lèi)構(gòu)造函數(shù)??
第?次:Child.prototype = new Parent()
‘new 的過(guò)程’的第三步,其實(shí)就是執(zhí)?了?類(lèi)構(gòu)造函數(shù)。
詳解js繼承 9
第?次:Parent.call(this,name,like)
call的作?是改變函數(shù)執(zhí)?時(shí)的上下?。?如:A.call(B)。其實(shí),最終執(zhí)?的還是A函數(shù),只不過(guò)是
?B來(lái)調(diào)??已。所以,你就懂了Parent.call(this,name,like) ,也就是執(zhí)?了?類(lèi)構(gòu)造函數(shù)Person。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是繼承? 簡(jiǎn)單說(shuō)就是:在js中獲取存在對(duì)象已有屬性和方法的一種方式.下面我總結(jié)了幾個(gè)繼承: 一丶原型鏈繼承 基...
    G_kai閱讀 753評(píng)論 0 0
  • 其實(shí)要總結(jié)這幾個(gè)概念已經(jīng)很久了,只是之前一直都覺(jué)得自己還不算完全掌握,而且知識(shí)點(diǎn)還不夠系統(tǒng),所以一直拖著,但是最近...
    Katherine的小世界閱讀 867評(píng)論 0 5
  • 用過(guò) React的讀者知道,經(jīng)常用 extends繼承 React.Component: // 部分源碼 func...
    金色888閱讀 271評(píng)論 0 0
  • 原文鏈接:zhuanlan.zhihu.com (一) 原型鏈繼承: function Parent(name) ...
    越努力越幸運(yùn)_952c閱讀 334評(píng)論 0 2
  • 構(gòu)造函數(shù)繼承 類(lèi)式繼承是在函數(shù)對(duì)象內(nèi)調(diào)用父類(lèi)的構(gòu)造函數(shù),使得自身獲得父類(lèi)的方法和屬性。call和apply方法為類(lèi)...
    前端大神888閱讀 216評(píng)論 0 0

友情鏈接更多精彩內(nèi)容