注意:文中错误的地方已经在掘金的文章中修改了(简书这里其实也改了的)
https://juejin.im/editor/drafts/5e1fc5b251882520a06362bb
(1)原型链继承
- 将子类的protoytpe指向父类的实例(将父类的实例作为子类实例的隐式原型)
- 或者说:将父类实例作为子类的显示原型
- 缺点
- 在创建子类实例时,不能向父类传参
- 不能实现多继承(继承多个父类)
- 多个实例共享父类的属性,当父类属性是引用类型时,子类实例之间修改会相互影响,【特别是数组】
- 要在子类prototype上挂方法,必须在子类实例的隐式原型指向父类实例之后
(Sub.prototype = new Super('woow-wu')之后Sub.prototype.getSex
(1) 原型继承 - 将父类的实例作为子类的原型
// 父类
function Super(name) {
this.name = name
}
Super.prototype.getAddress = function() {
return 'chongqing'
}
// 子类
function Sub(age) {
this.age = age
}
Sub.prototype = new Super('woow-wu') -------- 将父类的实例作为子类实例的原型,子类的实例就可以访问父类实例和父类原型上的属性和方法
Sub.prototype.getSex = function() {
return 'man'
}
const sub = new Sub(20)
console.log(sub.name) ------------------- 先在sub实例上找name,未找到再在super实例上查找,未找到再在super原型上查找 // 'woow-wu'
console.log(sub.getAddress()) // 'chongqing'
console.log(sub.age) // 20
console.log(sub.getSex()) // man
(2)借用构造函数继承(经典继承)
- 优点
- 在创建子类实例时,可以向父类传参
- 继承的属性是直接生成在子类实例上的,子类实例继承的属性相互不受影响(并没有继承父类的原型)
- 可以实现多继承(即调用多个父类)
- 缺点
- 不能继承父类原型链上的属性和方法
(因为没有new Super,即根本没有new父类,不存在原型链继承)
- 就是构造函数的缺点,也是优点,作为缺点就是属性和方法都生成在实例上,每次new都会新生成一份,造成系统资源浪费(即不共享属性),对于可以共享的只读属性,应该方法原型链上
- 不能继承父类原型链上的属性和方法
function Super1 (name) {
this.name = {name: name}
}
function Super2 (sex) {
this.sex = sex
}
Super.prototype.getAddress = function() {
return 'chongqing'
}
function Sub(name, sex) {
Super.call(this, name) -------------- 直接调用Super类(函数),将Super中的this绑定在sub实例上,并传参给父类
Super2.call(this, sex)
}
const sub1 = new Sub('woow-wu', 'man') ------------------------------ 可以向父类传参
console.log(sub1) // {name: {…}, sex: "man"}
const sub2 = new Sub('wang', 'woman')
console.log(sub2) // {name: {…}, sex: "woman"} ----------------------- 可以实现多继承,且实例之间的属性互不影响
(3)组合式继承
原型链继承和借用构造函数继承的组合
-
优点
- 既具有借用构造函数的优点,如
可以传参,多继承,不存在属性共享
- 也具有原型链继承的优点,可以继承父类实例原型链上的属性和方法
- 既具有借用构造函数的优点,如
-
缺点
- 会调用两次父构造函数,导致子类实例和子类实例原型上都有同一个属性
function Super(name) {
this.name = name
this.score = 100
}
Super.prototype.getName= function() {
return 'super' + this.name
}
function Sub(name, dress) {
Super.call(this, name)
this.address = address
}
Sub.prototype = new Super() // 注意,这里没有传参,在原型链继承这条线上,父类实例的原型上的nane属性是undefined
Sub.ptototype.getSex = function() {
return 'man'
}
cosnt sub = new Sub('woo-wu', 'chongqing')
console.log(sub)
组合继承最大的缺点:
1. 父类执行了两次
- 1. 在new Sub('woo-wu', 'chongqing')是会执行Super.call(this, name)------- 生成一次name,score
- 2. 在Sub.prototype = new Super() 执行了一次,又会生成一次name,score
(4)原型式继承
- 就是Object.create()的模拟实现
const obj = {
name: 'wang',
frends: ['li', 'go']
}
obj.__proto__ .age = 20
function createObj(obj) {
function F(){}
F.prototype = obj
return new F()
}
const a = createObj(obj)
const b = createObj(obj)
a.name = 'xxxxx'
// ----------------- 这样实际上是直接在a对象上添加了name属性,而不是修改a原型上的属性, 原型上的属性值不能直接通过实例来修改
// ----------------- 如果要修改,需要通过 a.__proto__.name = 30这样来修改
console.log(a.name) // xxxxx
console.log(b.name) // 'wang'
console.log(a.age) // 20
(5)寄生组合式继承
- 寄生组合式继承解决了什么问题?
- 主要解决的就是组合式继承中,执行了两次父类的构造函数,这样导致了子类的实例中有父类的属性,同时子类的原型中也有父类的属性
寄生组合式继承
如图4
组合式继承存在的问题:
1. 父类构造函数执行了两次
- 在借用构造函数式的时候执行了1次
- 在原型继承的时候执行了1次
- 所以子类实例上有父类中的属性和方法,子类的原型对象(即父类的实例)上也有父类的属性和方法
function Father (name) {
this.name = name
}
Father.prototype.addressFather = 'china'
function Child(name, sex) {
Father.call(this, name) //----------------------------------------- 借用构造函数继承
this.sex = sex
}
// Child.prototype = new Father() //------------------------------ 原型继承
function F(){}
F.prototype = Father.prototype
Child.protype = new F() --------------- 这样就没有执行父类(构造函数),而是间接只继承了父类的原型
Child.prototype.addressChild = 'shanghai'
const child = new Child('wang', 'man')
console.log(child)
console.log(child.name) // wang
console.log(child.__proto__.name)
![](https://img.haomeiwen.com/i6493119/65530a396992ded0.png)
组合继承: 下图 图5
function Father (name) {
this.name = name
}
Father.prototype.addressFather = 'china'
function Child(name, sex) {
Father.call(this, name) //----------------------------------------- 借用构造函数继承
this.sex = sex
}
Child.prototype = new Father() //------------------------------ 原型继承
Child.prototype.addressChild = 'shanghai'
const child = new Child('wang', 'man')
console.log(child)
console.log(child.name) // wang
console.log(child.__proto__.name) ----------------------------- undefined,因为new Father()时没有传参
![](https://img.haomeiwen.com/i6493119/6c3039c72a8f3d08.png)
https://www.jianshu.com/p/a8844b28ff79
https://juejin.im/post/591523588d6d8100585ba595
(6) es6中的继承
- class作为构造函数的语法糖,同时具有
__proto__
和prototype
,因为函数是函数也是对象 - 所以class同时具有两条继承链
- 子类的
__proto__
总是指向父类 (表示构造函数的继承) - 子类的prototype.
__proto__
总是指向父类的prototype(表示方法的继承)
- 子类的
1. 作为对象,------------------------------ A.__proto__ = B
2. 作为构造函数,-------------------------- A.prototype.__proto__ = B.prototype
![](https://img.haomeiwen.com/i6493119/c8bb124ab3d6926b.png)
(7) es6中的继承 (其他)
- calss 通过 extends关键字实现继承
- 父类的静态方法也会被子类继承
- Object.getPrototype(a) === b 可以用来从子类上获取父类
new.target
- 函数内部可以使用new.target,如果是new命令调用函数,new.target指向当前正在执行的函数,否则是undefined
class Father {
constructor() {
console.log(new.target.name)
}
}
class Child extends Father {
constructor() {
super() ------------------- super作为函数,只能用于构造函数,表示父类的构造函数
------------------- 但是super内部的this指代的是子类的实例
}
}
const fahter = new Father()
// Father
const child = new Child()
// Child ------ 因为super虽然是借用了父类的构造函数,但是this绑定在子类的实例上,(类似借用构造函数继承)
new.target用在函数内部,如果该函数通过new命令调用,new.target指正在执行的函数
super关键字(函数 或者 对象)
- super作为函数:
- super作为函数时,表示
父类的构造函数
(super作为函数时,只能用在构造函数中) - super作为函数时,----------- 返回的是子类的实例,super内部的this指的是子类的实例
- super作为函数时,表示
- super作为对象:
- 在普通方法中 ------------------ 指向父类的原型,同样this指向子类实例(和super作为函数时一样)
- 在静态方法中 ------------------ 指向父类,内部的this指向子类,而不是子类的实例
class A {
constructor() {
this.name = 'woow-wu'
}
go() {
console.log('home')
}
}
class B extends A {
constructor() {
super() -------------------- super作为函数,只能用于构造函数,表示父类的构造函数,但this指向子类实例
super.go() ------------------ super作为对象,在普通函数中指 父类的原型 ---- 所以无法继承父类实例上的属性和方法
}
getName() {
return super.name
}
}
cosnt b = new B()
b.getName ----------------------------- undefined,父类实例上的name并没有被继承,super指的是父类的原型
super作为对象,表示父类的原型,内部的this指的是子类的实例
super作为函数,表示父类的构造函数,内部的this指的是子类的实例,(作为函数,只能用于构造函数中)
class A {
constructor() {
this.name = 'wang'
}
getAName() {
return this.name
}
}
class B extends A {
constructor() {
super()
this.name = 'li'
}
getBName() {
return super.getAName()
}
}
const b = new B()
const res = b.getBName()
console.log(res) // li
super作为对象,在静态方法中,指向父类
super作为对象,在普通方法中,指向父类的原型
class Father {
constructor() {}
setAge(age) {
console.log(age * 100)
}
static setAge(age) {
console.log(age)
}
}
class Child extends Father {
constructor() {
super()
}
getAge(age) {
super.setAge(age) // 通过实例调用,super在普通方法中,表示父类的原型
}
static getAge(age) {
super.setAge(age) // 静态方法中的super对象,指的是父类,父类直接调用的是静态方法setAge
}
}
const child = new Child()
Child.getAge(10) // 10 静态方法直接通过类直接调用
child.getAge(10) // 1000 通过实例调用的是类原型上的方法
网友评论