共计 9859 个字符,预计需要花费 25 分钟才能阅读完成。
导航
[[深刻 01] 执行上下文](https://juejin.im/post/684490…
[[深刻 02] 原型链](https://juejin.im/post/684490…
[[深刻 03] 继承](https://juejin.im/post/684490…
[[深刻 04] 事件循环](https://juejin.im/post/684490…
[[深刻 05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻 06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻 07] 浏览器缓存机制(http 缓存机制)](https://juejin.im/post/684490…
[[深刻 08] 前端平安](https://juejin.im/post/684490…
[[深刻 09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻 10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻 11] 前端路由](https://juejin.im/post/684490…
[[深刻 12] 前端模块化](https://juejin.im/post/684490…
[[深刻 13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻 14] canvas](https://juejin.im/post/684490…
[[深刻 15] webSocket](https://juejin.im/post/684490…
[[深刻 16] webpack](https://juejin.im/post/684490…
[[深刻 17] http 和 https](https://juejin.im/post/684490…
[[深刻 18] CSS-interview](https://juejin.im/post/684490…
[[深刻 19] 手写 Promise](https://juejin.im/post/684490…
[[深刻 20] 手写函数](https://juejin.im/post/684490…
[[react] Hooks](https://juejin.im/post/684490…
[[部署 01] Nginx](https://juejin.im/post/684490…
[[部署 02] Docker 部署 vue 我的项目](https://juejin.im/post/684490…
[[部署 03] gitlab-CI](https://juejin.im/post/684490…
[[源码 -webpack01- 前置常识] AST 形象语法树](https://juejin.im/post/684490…
[[源码 -webpack02- 前置常识] Tapable](https://juejin.im/post/684490…
[[源码 -webpack03] 手写 webpack – compiler 简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…
原型链继承
- <font color=red> 将子类的 prototype 指向父类的实例,同时批改子类的 constructor 让其从新指向子类 </font>,或者说:将父类的实例作为子类实例的隐式原型
-
留神点:
在批改了 prototype 之后,须要从新批改 prototype.constructor 的指向
-
毛病:
- 在创立子类实例的时候,不能向父类传参
- 不能实现多继承(继承多个父类),因为是给 prototype 间接赋值
- 多个实例共享父类的属性和父类原型上的属性,当属性是援用类型时,子类实例间批改会相互影响【特地是数组】
- 在子类的 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】
- 毛病:
- 生成子类实例时,不能向父类传参
- 不能实现多继承
- 属性共享,批改子类实例上的原型链上的援用类型的属性时,子类实例会相互影响
-
在子类的 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 指向父类的实例,子类实例就能拜访父类实例和父类实例原型链上的属性和办法,毛病:不能实现多继承
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 第二个参数示意生成的原型上的属性
// 不要忘了从新指定构造函数
constructor: {value: Student
}
})
借用构造函数继承(经典继承)
- 原理:通过 call(this, 参数)绑定父类中的 this 为子类的实例,并执行父类,就相当于把父类中的 this 换成了子类实例
-
长处:
- 能实现多继承(即调用多个父类)
- 生成子类实例时,能够向父类传参
- 继承的属性是间接生成在子类实例上的,各个子类实例之间批改援用类型的属性相互不受影响
-
毛病:
-
不能继承父类实例对象原型链上的属性和办法
- (因为父类没有通过 new 命令生成父类实例,也没有扭转子类 prototype 的指向,不存在原型链继承)
- 就是构造函数的毛病,也是长处,作为毛病就是属性和办法都生成在实例上,每次 new 都会新生成一份,造成系统资源节约(即不共享属性),对于能够共享的只读属性,应该办法原型链上
借用构造函数继承 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 为子类实例,并执行 Super1(),相当于 this.name = name Super2.call(this, age) // 长处:能够多继承,同时继承了 Super1 和 Super2 中的属性,且在子类实例上批改属性互相不受影响 this.address = address // 毛病:不能继承父类实例原型链上的属性和办法 } const sub = new Sub('woow_wu7', 20, 'hangzhou') // 长处:能够向父类传参 console.log(sub)
-
组合式继承(原型链继承 + 借用构造函数继承)
- 组合继承:即原型链继承 + 借用构造函数继承
-
长处:
- 既具备借用构造函数继承的长处(向父类传参,多继承,不存在属性共享)
- 又具备原型链继承的长处(继承父类实例上的属性和父类实例原型链上的属性和办法,并且是共享)
-
毛病:
- <font color=red> 会调用两次父构造函数,导致子类实例和子类实例原型链上都有同一个属性或办法 </font>
- <font color=red> 父类被调用了两次,一次是借用构造函数是的 call 调用,一次是原型链继承时的 new 调用 </font>
- <font color=red> 因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约 </font>
组合式继承
- 借用构造函数继承 + 原型链继承
- 长处:多继承,将父类传参,某些属性不共享,继承父类实例原型链上的属性和办法
- 毛病:!!!!!!- 父类被调用了两次,一次是借用构造函数是的 call 调用,一次是原型链继承时的 new 调用
- 因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约
代码: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) // 借用构造函数,多继承,但不能继承原型链上的属性
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. 父类执行了两次
- 1. 在 new Sub('woow_wu7', 20, 'hangzhou')是会执行 Super.call(this, name)------- 生成一次 name // 'woow_wu7'
- 2. 在 Sub.prototype = new Super1() 执行了一次,又会生成一次 name // undefined
2020/12/25 温习组合式继承
<!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>
// 组合式继承 = 借用构造函数继承 + 原型链式继承
// 长处:两者组合,互相补充
// 毛病:// 1. 会调用两次父构造函数,导致 (子类实例 - 即借用构造函数继承) 和 (子类实例的原型链上 - 即原型链继承) 上都有雷同的属性和办法
// - 本例中:子类实例上有 superName1 属性;子类实例的原型链上也有 superName1 属性
// 2. 父类被调用了两次,一次是借用构造函数是的 call 调用,一次是原型链继承时的 new 调用
// 3. 因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约
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) {
// 借用构造函数继承
// 长处:能够向父构造函数传参,多继承,属性不共享
// 毛病:不能继承父类 prototype 对象原型链上的属性和办法
Super1.call(this, superName1)
Super2.call(this, superName2)
this.subName = subName
}
// 原型链继承
// 长处:能够继承父类实例原型链上的属性和办法,共享属性
// 毛病:在生成子类实例时不能向父类传传参,不能实现多继承,继承的属性是援用类型时,子类实例之间批改会相互影响
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> 次要解决了在组合继承中两次调用父类的问题,这导致子类实例的本身属性中有父类实例的属性,子类实例的原型链中也有父类实例原型中的属性 </font>
-
次要解决:
- 组合式继承中,父类被屡次调用,导致子类实例属性和子类实例原型链上有雷同的属性的问题
- 因为父类两次被调用,call 和 new,构造函数中的属性会两次生成,造成资源的节约
function Super(name) {
this.name = name
}
Super.prototype.getName = function() {
return ‘Super’ + this.name
}
function Sub(name, age) {
Super.call(this, name) // 借用构造函数
this.age = age
}
// Sub.prototype = new Super() —————- 原型链继承,(没用寄生组合继承之前,即没有应用过渡函数 Parasitic)
function Parasitic(){}
Parasitic.prototype = Super.prototype
Sub.prototype = new Parasitic()
// Parasitic 内没有任何属性
// 这样就没有执行父类(Super 构造函数),而是间接的只继承了父类实例原型上的属性
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 温习寄生组合继承
<!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() {} // 两头函数,自身没有任何属性和办法
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> 关键字实现继承
-
子类必须在 constructor 办法中调用 <font color=red>super 办法 </font>,否在新建实例时会报错
- 因为子类的 this 须要通过父类的构造函数获取,不调用 super 办法就得不到 this 对象
- 子类没有定义 constructor 会被默认增加
- <font color=red> 在子类的 constructor 中必须先调用 super()后能力应用 this</font>
-
<font color=red> 父类的静态方法也会被子类所继承 </font>
- es5 的借用构造函数式继承:
-
是先创立子类的 this,而后将父类的属性和办法帮到子类的 this 对象上
es6 的继承:
-
是将父类实例的属性和办法增加到 this 上,而后用子类的构造函数批改 this
super 关键字
- 能够作为函数,也能够作为对象
super 作为函数
- <font color=red>super 作为函数只能用于构造函数中,示意父类的构造函数,this 指向子类的实例 </font>
-
留神:
<font color=red>super 作为函数,尽管示意父类的构造函数,但返回的是子类的实例,即 super 外部的 this 指向的是子类的实例!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</font> - super 作为函数:只能用于构造函数中,示意父类的构造函数
-
super 作为函数:外部的 this 指向的是子类的实例
class A {
constructor() {
console.log(this, ‘this’)
}
}
class B extends A {
constructor() {
super() // 留神:super 最为函数,只能用于构造函数中示意父类的构造函数,外部 this 指向子类的实例
}
}new B() // B this ========> super 作为函数,外部 this 指向子类的实例
super 作为对象
- super 作为对象
- <font color=red> 在一般办法中:指向父类的原型,this 指向以后子类的实例 </font>(实例上的属性和办法无奈通过该 super 获取)
-
<font color=red> 在静态方法中:指向父类,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 在一般函数中是对象时,示意父类的原型 console.log(this.x); // 3 } } let b = new B();
super 作为对象,在静态方法中:示意父类 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 作为对象,在静态方法中,示意父类,调用父类的静态方法 myMethod } myMethod(msg) {super.myMethod(msg); } } Child.myMethod(1); // static 1 // Child.myMethod()是调用 Child 的静态方法,静态方法中的 super 对象示意父类 var child = new Child(); child.myMethod(2); // instance 2 // 实例上调用 myMethod,没构造函数中没有,就去原型上查找,super 对象在一般办法中示意父类的原型
super 总结
- <font color=red>super 作为函数,示意父类的构造函数,this 指向子类实例(此时 this 只能用于)</font>
- <font color=red>super 作为对象,在一般办法中,示意父类的原型,this 指向子类实例 </font></font>
- <font color=red>super 作为对象,在静态方法中,示意父类,this 指向子类 </font>
es6 继承
- class 作为构造函数的语法糖,同时具备
__proto__
和 prototype -
所以 class 同时具备两条继承链:
- 子类的
__proto__
总是指向父类(示意构造函数的继承) - 子类的
prototype.__proto__
总是指向父类的 prototype(示意办法的继承)
- 子类的
我的简书:https://www.jianshu.com/p/d88…
川神:https://juejin.im/post/684490…
https://www.jianshu.com/p/a88…