ES5与ES6中的继承

时间:2021-06-25 06:12:51

ES5继承
在ES5中没有类的概念,所以一般都是基于原型链继承,具体的继承方法有以下几种:

父类:
function Father (name) {
this.name = name || 'sam'
this.sleep = function () { // 实例方法
return this.name + '正在睡觉'
}
}
Father.prototype.eat = funciton () { // 原型方法
return this.name + '正在吃饭'
}

一,原型链继承
核心:将父类的实例作为子类的原型

子类:
function Son () {
this.age = 12
}
Son.prototype = new Father()
Son.prototype.name = 'Sam'
var son = new Son()
document.write(son.age)
document.write(son.name) // Sam
document.write(son.sleep()) // Sam正在睡觉
document.write(son.eat()) // Sam正在吃饭

优点:

(1)非常纯粹的继承关系,实例是子类的实例,也是父类的实例
(2)父类新增的属性和方法,子类都能访问到
(3)简单,易于实现
缺点:

(1)无法实现多继承
(2)来自原型对象的引用属性是所有子类实例共享的
(3)创建子类实例时,无法向父类的构造函数传递参数

二,构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类

子类:
function Son (age) {
Father.call(this) //实现了继承
this.age = age
}
var son = new Son()
console.log(son.name) // 返回sam
console.log(son.sleep()) // 返回sam正在睡觉
console.log(son.eat()) // 报错Uncaught TypeError

优点:
(1)子类创建时,可以通过call或者apply向父类传递参数
(2)可以实现多继承(call多个父类对象)
缺点:
(1)实例并不是父类的实例,只是子类的实例
(2)只能继承父类的实例属性和方法,不能继承原型属性和方法
(3)无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
三,实例继承
核心:为父类实例添加新特性,作为子类实例返回

子类:
function Son (name) {
var instance = new Father()
instance.name = name || 'Tom'
return instance
}
var son = new Son()
console.log(son.name)
console.log(son.sleep())
console.log(son.eat())

缺点:
(1)实例是父类的实例,不是子类的实例
(2)不支持多继承
四,组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

子类:
function Son (age) {
  Father.call(this,'sam')
  this.age = age
}
Son.prototype = new Father()
var son = new Son()
console.log(son.name)
console.log(son.eat())
console.log(son.sleep())

优点:
(1)可以继承实例属性和方法,还可以继承原型属性和方法
(2)既是子类的实例,也是父类的实例
(3)不存在引用属性共享问题
(4)可传参
(5)函数可复用
缺点:调用了两次父类构造函数,生成了两份实例
ES6继承
ES6里面引入了类class的概念,class可以通过extends关键字实现继承,这比ES5通过修改原型链实现继承,要清晰和方便很多
父类(关于ES6中通过class定义一个实例对象的方法这里略过)

class Father {
constructor (name) {
this.name =name
}
eat () {
return this.name + '正在吃饭'
}
sleep () {
return this.name + '正在睡觉'
}
}
子类
class Son extends Father {
}
class Son extends Father {
constructor () {
super()
}
}

以上两种写法是相等的。如果子类中没有定义constructor,这个方法会被默认添加,不管有没有显示定义,任何一个子类都有constructor方法。

子类
class Son extends Father {
constructor (color) {
this.color = color
}
}
var son = new Son();
console.log(son.color) // 报错

在子类的构造函数中,如果用了constructor方法,则必须调用super方法。只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例的加工,只有super方法才能返回父类实例

class Son extends Father {
constructor (name,age) {
super (name)
this.age = age
}
play () {
return super.eat()+ '正在玩耍'
}
}
var son = new Son('sam')
console.log(son.play())