JavaScript中的继承,ES5继承与ES6继承的本质及区别
原型如何实现继承?
Class 如何实现继承?
Class 本质是什么?
1.组合继承
包括构造函数的继承和原型的继承,一般构造函数继承属性,原型继承方法
// 父类的构造函数
function Parent(val){
this.val = val
}
// 父类原型上的方法
Parent.prototype.getVal = function(){
console.log(this.val)
}
// 子类的构造函数
function Child(val){
Parent.call(this, val) // 构造函数的继承
}
// 子类的原型是父类的一个实例
Child.prototype = new Parent() // 原型的继承
let child = new Child(1)
child.getVal() // 1
child instanceof Parent // true
以上继承方式的核心是在子类的构造函数中通过Parent.call(this) 继承父类的属性,然后改变子类的原型为 new Parent() 来继承父类的函数
这种继承方式的优点在于构造函数可以传参,不会与父类引用属性共享,可以复用父类的函数,但是也存在一个缺点就是在继承父类函数的时候调用了父类构造函数,导致子类的原型上多了不需要的父类的属性。
2.寄生组合继承
组合继承的缺点在于继承父类函数时调用了父类的构造函数,寄生组合继承是对这一缺点进行了优化
function Parent(val) {
this.val = val
}
Parent.prototype.getVal = function(){
console.log(this.val)
}
function Child(val){
Parent.call(this, val)
}
Child.prototype = Object.create(Parent.prototype, {
construtor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
以上继承实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样即解决了会继承不需要的父类属性的问题,而且能正确的通过原型找到子类的构造函数
以上ES5的继承都是通过构造函数的继承级原型继承的方式去解决的,在ES6中,可以通过class声明类并且通过extends关键字实现继承,比 ES5 的通过修改原型链实现继承,要清晰和方便很多
3.ES6实现继承
class Parent{
constructor(val) {
this.val = val
}
getVal() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(val) {
super(val)
this.val = val
}
}
let child = new Child(1)
child.getVal() // 1
child instanced Paren // true
class实现继承的核心在于使用extends,表明继承自哪个父类,并且在子类构造函数中必须调用super,super(val)相当于ES5中的Parent.call(this, val),super关键字,它在这里表示父类的构造函数,用来新建父类的this对象
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。
注意: 如果不调用super方法,子类就得不到this对象。在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
ES5实现的继承和ES6实现的继承的区别
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
在JavaScript语言中并不存在类,class的本质就是函数,ES6中类中的constructor()构造函数相当于ES5中的函数,所有写在类中的方法其实都是写在了构造函数的原型上。
ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
super关键字的本质及如何使用
Super这个关键字既可以当作函数使用,也可以当作对象使用,这两种情况下用法完全不同
(1)super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
(2)super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
注意: super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)。
ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。