继承
1. 原型链继承
缺点
- 父类属性无法实例化,子类只能通过原型链访问同一个变量,当改变原型链上属性的值的时候,其他子类实例也会受到影响。
function Parent() {
this.name = 'parentName'
this.actions = ['eat', 'drink']
}
Parent.prototype.getName = function () {
return this.name
}
function Child () {
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
const c1 = new Child()
const c2 = new Child()
c1.actions.pop()
console.log(c1.actions)
console.log(c2.actions)
- 创建 Child 实例的时候不能传参。就算子类可以传参,由于父类无法实例化,所以无法传递到父类实例上。
2. 构造函数继承
缺点
- 子类只能继承父类构造函数定义在函数体内的属性和函数,不能继承父类 prototype 上的属性和函数
定义在构造函数内的属性和函数在类实例化的时候都会创建新的内存来存储。多占一块内存。
function Parent(name, actions) {
this.name = name
this.actions = actions
this.eat = function () {
}
}
function Child(id, name, actions) {
Parent.call(this, name, actions)
this.id = id
}
const c1 = new Child(1, 'ss', ['eat'])
const c2 = new Child(2, 'xy', ['fly'])
console.log(c1)
console.log(c2)
console.log(c1.eat === c2.eat) // false
3. 组合继承
原型链调用父类属性无法实例化,子类实例共有相同的父类属性,修改原型链上的属性会影响到其他的实例对象。且父类实例化的时候无法传参
构造函数继承想要继承的属性和方法必须定义在父类构造函数体内,每次实例化的时候都需要申请新的内存来存,浪费内存空间。可以传参了
综合两者的优点就是组合继承
function Parent(name, actions) {
this.name = name
this.actions = actions
}
Parent.prototype.eat = function () {
console.log(this.name + ' - 吃')
}
function Child(id, ...args) {
Parent.apply(this, args)
this.id = id
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
const c1 = new Child(1, 'ss', ['eat'])
const c2 = new Child(2, 'xy', ['fly'])
console.log(c1) // Child { name: 'ss', actions: [ 'eat' ], id: 1 }
console.log(c2) // Child { name: 'xy', actions: [ 'fly' ], id: 2 }
console.log(c1.eat === c2.eat) // true
console.log(c1 instanceof Child) // true
c1.eat() // ss - 吃
c2.eat() // xy - 吃
缺点
Parent 被调用了两次,函数体内的代码被执行了两次,而有一次是完全没必要的
this.name = name
this.actions = actions
这会在 Child 的原型对象上也留下name
和actions
,是没有必要的
寄生组合式继承
为了解决第二次调用 Parent 导致多余的属性被创建出来
function Parent(name, actions) {
this.name = name
this.actions = actions
}
Parent.prototype.eat = function () {
console.log(this.name + ' - 吃')
}
function Child(id, ...args) {
Parent.apply(this, args)
this.id = id
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
const c1 = new Child(1, 'ss', ['eat'])
const c2 = new Child(2, 'xy', ['fly'])
console.log(c1) // Child { name: 'ss', actions: [ 'eat' ], id: 1 }
console.log(c2) // Child { name: 'xy', actions: [ 'fly' ], id: 2 }
console.log(c1.eat === c2.eat) // true
console.log(c1 instanceof Child) // true
c1.eat() // ss - 吃
c2.eat() // xy - 吃
通过 Parent 的原型对象来创建一个新的对象,把这个新的对象作为 Child 的原型对象
class 继承
class Parent {
constructor(name) {
this.name = name
}
eat () {
console.log(this.name + ' - 吃')
}
}
class Child extends Parent {
constructor (id, name) {
super(name)
this.id = id
}
}
const c1 = new Child(1, 'ss')
const c2 = new Child(2, 'xy')
console.log(c1) // Child { name: 'ss', actions: [ 'eat' ], id: 1 }
console.log(c2) // Child { name: 'xy', actions: [ 'fly' ], id: 2 }
console.log(c1.eat === c2.eat) // true
console.log(c1 instanceof Child) // true
c1.eat() // ss - 吃
c2.eat() // xy - 吃