導(dǎo)航
[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運算符
[深入07] 瀏覽器緩存機制(http緩存機制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)
[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CI
[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
原型鏈繼承
- <font color=red>將子類的prototype指向父類的實例,同時修改子類的constructor讓其重新指向子類</font>,或者說:將父類的實例作為子類實例的隱式原型
- 注意點:
在修改了prototype之后,需要重新修改prototype.constructor的指向
- 缺點:
- 在創(chuàng)建子類實例的時候,不能向父類傳參
- 不能實現(xiàn)多繼承(繼承多個父類),因為是給prototype直接賦值
- 多個實例共享父類的屬性和父類原型上的屬性,當屬性是引用類型時,子類實例間修改會相互影響【特別是數(shù)組】
- 在子類的prototype上掛屬性和方法,必須在修改子類的prototype指向之后。
Sub.prototype = new Super('woow_wu7')之后Sub.prototype.sex = 'man',不然會被新的引用代替
原型鏈繼承
- 原理:將子類的prototype指向父類的實例,同時要修改子類的constructor屬性讓其重新指向子類
- 因為修改了子類prototype指向父類實例后,子類的prototype.constructor就指向了父類(修改改回來,防止引用出錯)
- 2021/07/24補充
- 因為:具體是 ( child.constructor ) => ( Child.prototype.constructor ) => ( 父類實例的constructor,即father.constructor ) => ( Father.prototype.constructor ) => Father
- 所以:當修改了prototype后,Child.prototype.constructor 指向了 Father,所以constructor需要重新指回Child
- 即:修改了 【 Child.prototype = new Father() 】 之后,需要修改Child.prototype.constructor的指向 【 Child.prototype.constructor = Child 】
- 缺點:
1. 生成子類實例時,不能向父類傳參
2. 不能實現(xiàn)多繼承
3. 屬性共享,修改子類實例上的原型鏈上的引用類型的屬性時,子類實例會相互影響
4. 在子類的prototype上掛屬性和方法時,需要在子類的prototype指向父類的實例之后
代碼示例:
// 父類
function Super(name) {
this.name = name
}
Super.prototype.age = 20
// 子類
function Sub(address) {
this.address = address
}
Sub.prototype = new Super('woow_wu7') // 原型鏈繼承:將子類的prototype指向父類的實例,子類實例就能訪問父類實例和父類實例原型鏈上的屬性和方法,缺點:不能實現(xiàn)多繼承
Sub.prototype.constructor = Sub // 記得在修改prototype后,需要修改constructor指向,防止引用出錯,不修改的話,constructor指向了Super
Sub.prototype.sex = 'man' // 缺點:掛載屬性必須在上面步驟之后
const sub = new Sub('hangzhou') // 缺點:只能向子類傳參,不能向父類傳參
console.log(sub.address, '子類實例自身屬性')
console.log(sub.sex, '子類實例原型上的屬性')
console.log(sub.name, '子類實例原型上的屬性 => 父類實例上的屬性')
console.log(sub.age, '子類實例原型的原型上的屬性 => 父類實例原型上的屬性') // 一層層上溯
修改constructor也可以用下面的方式
Sub.prototype = Object.create(Super.prototype, {
// Oject.create第二個參數(shù)表示生成的原型上的屬性
// 不要忘了重新指定構(gòu)造函數(shù)
constructor: {
value: Student
}
})
借用構(gòu)造函數(shù)繼承(經(jīng)典繼承)
- 原理:通過call(this, 參數(shù))綁定父類中的this為子類的實例,并執(zhí)行父類,就相當于把父類中的this換成了子類實例
- 優(yōu)點:
- 能實現(xiàn)多繼承(即調(diào)用多個父類)
- 生成子類實例時,可以向父類傳參
- 繼承的屬性是直接生成在子類實例上的,各個子類實例之間修改引用類型的屬性互相不受影響
- 缺點:
- 不能繼承父類實例對象原型鏈上的屬性和方法
- (因為父類沒有通過new命令生成父類實例,也沒有改變子類prototype的指向,不存在原型鏈繼承)
- 就是構(gòu)造函數(shù)的缺點,也是優(yōu)點,作為缺點就是屬性和方法都生成在實例上,每次new都會新生成一份,造成系統(tǒng)資源浪費(即不共享屬性),對于可以共享的只讀屬性,應(yīng)該方法原型鏈上
- 不能繼承父類實例對象原型鏈上的屬性和方法
借用構(gòu)造函數(shù)繼承
function Super1(name) {
this.name = name
}
function Super2(age) {
this.age = age
}
Super1.prototype.sex = 'man'
function Sub(name, age, address) {
Super1.call(this, name) // 通過call,綁定super1的this為子類實例,并執(zhí)行Super1(),相當于 this.name = name
Super2.call(this, age) // 優(yōu)點:可以多繼承,同時繼承了Super1和Super2中的屬性,且在子類實例上修改屬性相互不受影響
this.address = address // 缺點:不能繼承父類實例原型鏈上的屬性和方法
}
const sub = new Sub('woow_wu7', 20, 'hangzhou') // 優(yōu)點:可以向父類傳參
console.log(sub)
組合式繼承(原型鏈繼承+借用構(gòu)造函數(shù)繼承)
- 組合繼承:即原型鏈繼承 + 借用構(gòu)造函數(shù)繼承
- 優(yōu)點:
- 既具有借用構(gòu)造函數(shù)繼承的優(yōu)點(向父類傳參,多繼承,不存在屬性共享)
- 又具有原型鏈繼承的優(yōu)點(繼承父類實例上的屬性和父類實例原型鏈上的屬性和方法,并且是共享)
- 缺點:
- <font color=red>會調(diào)用兩次父構(gòu)造函數(shù),導(dǎo)致子類實例和子類實例原型鏈上都有同一個屬性或方法</font>
- <font color=red>父類被調(diào)用了兩次,一次是借用構(gòu)造函數(shù)是的call調(diào)用,一次是原型鏈繼承時的new調(diào)用</font>
- <font color=red>因為父類兩次調(diào)用,所以子類和父類實例原型鏈上有相同的屬性和方法,造成浪費</font>
組合式繼承
- 借用構(gòu)造函數(shù)繼承 + 原型鏈繼承
- 優(yōu)點:多繼承,將父類傳參,某些屬性不共享,繼承父類實例原型鏈上的屬性和方法
- 缺點:?。。。。?!
- 父類被調(diào)用了兩次,一次是借用構(gòu)造函數(shù)是的call調(diào)用,一次是原型鏈繼承時的new調(diào)用
- 因為父類兩次調(diào)用,所以子類和父類實例原型鏈上有相同的屬性和方法,造成浪費
代碼:
function Super1(name) {
this.name = name
}
function Super2(age) {
this.age = age
}
Super1.prototype.getName = function() {
return 'Super1' + this.name
}
Super2.prototype.getAge = function() {
return 'Super2' + this.age
}
function Sub(name, age, address) {
Super1.call(this, name) // 借用構(gòu)造函數(shù),多繼承,但不能繼承原型鏈上的屬性
Super2.call(this, age)
this.address = address
}
Sub.prototype = new Super1()
// 注意:這里沒有傳參,在原型鏈繼承這條線上,父類實例上的nane屬性是undefined
// 注意:原型鏈繼承這條線,還是不能多繼承,(如不能同時繼承Super1和Super2所在的prototye)因為是直接賦值
Sub.prototype.constructor = Sub // 記得修改constructor指向,重新指回Sub,不然會指向Super1
Sub.prototype.getAddress = function() {
return 'Sub' + this.address
}
const sub = new Sub('woow_wu7', 20, 'hangzhou')
console.log(sub)
組合繼承最大的缺點:
1. 父類執(zhí)行了兩次
- 1. 在new Sub('woow_wu7', 20, 'hangzhou')是會執(zhí)行Super.call(this, name)------- 生成一次name // 'woow_wu7'
- 2. 在Sub.prototype = new Super1() 執(zhí)行了一次,又會生成一次name // undefined
2020/12/25復(fù)習組合式繼承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 組合式繼承 = 借用構(gòu)造函數(shù)繼承 + 原型鏈式繼承
// 優(yōu)點:兩者組合,相互補充
// 缺點:
// 1. 會調(diào)用兩次父構(gòu)造函數(shù),導(dǎo)致 (子類實例-即借用構(gòu)造函數(shù)繼承 ) 和 ( 子類實例的原型鏈上-即原型鏈繼承 ) 上都有相同的屬性和方法
// - 本例中:子類實例上有 superName1 屬性;子類實例的原型鏈上也有 superName1 屬性
// 2. 父類被調(diào)用了兩次,一次是借用構(gòu)造函數(shù)是的call調(diào)用,一次是原型鏈繼承時的new調(diào)用
// 3. 因為父類兩次調(diào)用,所以子類和父類實例原型鏈上有相同的屬性和方法,造成浪費
function Super1(name) {
this.superName1 = name
}
function Super2(name) {
this.superName2 = name
}
Super1.prototype.superAge1 = 10
Super2.prototype.superAge2 = 20
function Sub(superName1, superName2, subName) {
// 借用構(gòu)造函數(shù)繼承
// 優(yōu)點:可以向父構(gòu)造函數(shù)傳參,多繼承,屬性不共享
// 缺點:不能繼承父類prototype對象原型鏈上的屬性和方法
Super1.call(this, superName1)
Super2.call(this, superName2)
this.subName = subName
}
// 原型鏈繼承
// 優(yōu)點:可以繼承父類實例原型鏈上的屬性和方法,共享屬性
// 缺點:在生成子類實例時不能向父類傳傳參,不能實現(xiàn)多繼承,繼承的屬性是引用類型時,子類實例之間修改會相互影響
Sub.prototype = new Super1()
Sub.prototype.constructor = Sub
Sub.prototype.subAge = 30
const sub = new Sub('super1', 'super2', 'sub')
console.log('sub', sub)
console.log('sub.superName1', sub.superName1)
console.log('sub.superName2', sub.superName2)
console.log('sub.subName', sub.subName)
console.log('sub.superAge1', sub.superAge1)
console.log('sub.subAge', sub.subAge)
</script>
</body>
</html>
寄生組合式繼承
- 寄生組合繼承:<font color=red>主要解決了在組合繼承中兩次調(diào)用父類的問題,這導(dǎo)致子類實例的自身屬性中有父類實例的屬性,子類實例的原型鏈中也有父類實例原型中的屬性</font>
寄生組合式繼承
- 主要解決:
- 組合式繼承中,父類被多次調(diào)用,導(dǎo)致子類實例屬性和子類實例原型鏈上有相同的屬性的問題
- 因為父類兩次被調(diào)用,call和new,構(gòu)造函數(shù)中的屬性會兩次生成,造成資源的浪費
function Super(name) {
this.name = name
}
Super.prototype.getName = function() {
return 'Super' + this.name
}
function Sub(name, age) {
Super.call(this, name) // 借用構(gòu)造函數(shù)
this.age = age
}
// Sub.prototype = new Super() ---------------- 原型鏈繼承,(沒用寄生組合繼承之前,即沒有使用過渡函數(shù)Parasitic)
function Parasitic(){}
Parasitic.prototype = Super.prototype
Sub.prototype = new Parasitic()
// Parasitic內(nèi)沒有任何屬性
// 這樣就沒有執(zhí)行父類(Super構(gòu)造函數(shù)),而是間接的只繼承了父類實例原型上的屬性
Sub.prototype.constructor = Sub // 修改prototype要同時修改conscrutor指向
Sub.prototype.getAge = function() {
return 'Sub' + this.age
}
const sub = new Sub('woow_wu7', 20)
console.log(sub)
2020/12/25復(fù)習寄生組合繼承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 寄生組合式繼承
function Super1(name) {
this.superName1 = name
}
function Super2(name) {
this.superName2 = name
}
Super1.prototype.superAge1 = 20
function Sub(superName1, superName2, name) {
Super1.call(this, superName1)
Super2.call(this, superName2)
this.subName = name
}
function Parasitic() { } // 中間函數(shù),本身沒有任何屬性和方法
Parasitic.prototype = Super1.prototype
// 這樣 sub 實例就能繼承 Super1.prototype上的屬性和方法,而這條繼承線不用在繼承 super1 實例上的方法
Sub.prototype = new Parasitic()
Sub.prototype.constructor = Sub
Sub.prototype.subAge = 30
const sub = new Sub('super1', 'super2', 'sub')
console.log('sub', sub)
</script>
</body>
</html>
class
- class可以通過 <font color=red>extends</font>關(guān)鍵字實現(xiàn)繼承
- 子類必須在constructor方法中調(diào)用<font color=red>super方法</font>,否在新建實例時會報錯
- 因為子類的this需要通過父類的構(gòu)造函數(shù)獲取,不調(diào)用super方法就得不到this對象
- 子類沒有定義constructor會被默認添加
- <font color=red>在子類的constructor中必須先調(diào)用super()后才能使用this</font>
- <font color=red>父類的靜態(tài)方法也會被子類所繼承</font>
es5的繼承(借用構(gòu)造函數(shù)式繼承)
- es5的借用構(gòu)造函數(shù)式繼承:
- 是先創(chuàng)建子類的this,然后將父類的屬性和方法幫到子類的this對象上
es6的繼承:
- 是將父類實例的屬性和方法添加到this上,然后用子類的構(gòu)造函數(shù)修改this
super關(guān)鍵字
- 可以作為函數(shù),也可以作為對象
super作為函數(shù)
- <font color=red>super作為函數(shù)只能用于構(gòu)造函數(shù)中,表示父類的構(gòu)造函數(shù),this指向子類的實例</font>
- 注意:
<font color=red>super作為函數(shù),雖然表示父類的構(gòu)造函數(shù),但返回的是子類的實例 ,即super內(nèi)部的this指向的是子類的實例?。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。?!</font>
super作為函數(shù)
- super作為函數(shù):只能用于構(gòu)造函數(shù)中,表示父類的構(gòu)造函數(shù)
- super作為函數(shù):內(nèi)部的this指向的是子類的實例
class A {
constructor() {
console.log(this, 'this')
}
}
class B extends A {
constructor() {
super() // 注意:super最為函數(shù),只能用于構(gòu)造函數(shù)中表示父類的構(gòu)造函數(shù),內(nèi)部this指向子類的實例
}
}
new B() // B this ========> super作為函數(shù),內(nèi)部this指向子類的實例
super作為對象
- super作為對象
- <font color=red>在普通方法中:指向父類的原型,this指向當前子類的實例</font>(實例上的屬性和方法無法通過該super獲取)
- <font color=red>在靜態(tài)方法中:指向父類,this指向子類</font>
由于this指向子類實例,所以如果通過super對某個屬性賦值,這時super就是this,賦值的屬性會變成子類實例的屬性。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3; // !?。。?!super對某個屬性賦值,super就表示this,即子類的實例?。。。?!
console.log(super.x); // undefined // super在普通函數(shù)中是對象時,表示父類的原型
console.log(this.x); // 3
}
}
let b = new B();
super作為對象,在靜態(tài)方法中:表示父類
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg); // super作為對象,在靜態(tài)方法中,表示父類,調(diào)用父類的靜態(tài)方法myMethod
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1);
// static 1
// Child.myMethod()是調(diào)用Child的靜態(tài)方法,靜態(tài)方法中的super對象表示父類
var child = new Child();
child.myMethod(2);
// instance 2
// 實例上調(diào)用myMethod,沒構(gòu)造函數(shù)中沒有,就去原型上查找,super對象在普通方法中表示父類的原型
super總結(jié)
- <font color=red>super作為函數(shù),表示父類的構(gòu)造函數(shù),this指向子類實例(此時this只能用于)</font>
- <font color=red>super作為對象,在普通方法中,表示父類的原型,this指向子類實例</font></font>
- <font color=red>super作為對象,在靜態(tài)方法中,表示父類,this指向子類</font>
es6繼承
- class作為構(gòu)造函數(shù)的語法糖,同時具有
__proto__和 prototype - 所以 class 同時具有兩條繼承鏈:
- 子類的
__proto__總是指向父類(表示構(gòu)造函數(shù)的繼承) - 子類的
prototype.__proto__總是指向父類的prototype(表示方法的繼承)
- 子類的
我的簡書:http://m.itdecent.cn/p/d8809038cd38
川神:https://juejin.im/post/6844903780035592205#heading-0
http://m.itdecent.cn/p/a8844b28ff79