如果对JS继承还不甚了解,可以阅读一下《前端学习系列——(十)JavaScript的继承》。
我们先构造好父类:
function Super() {
this.name = '随风丶逆风'
}
寄生组合继承
ES5继承方式中,普遍认为寄生组合继承最优,这里就以它为例:
function Sub1() {
Super.call(this)
}
// 用于解决组合继承导致父类属性被两次实例化的问题。即子类一份属性,子类原型上又有一份
Sub1.prototype = Object.create(Super.prototype)
// 修正Sub原型对象上[[Constructor]]指针,从Super指向Sub,方便使用contructor判断构造函数
Sub1.prototype.constructor = Sub1
ES6继承
ES6新增了class语法糖,使用extends关键词进行继承。
值得注意的是,extends关键字可以继承任意具有[[Construct]]和prototype的对象,这是为了向后兼容,继承普通的构造函数。
class Sub2 extends Super {
}
两者的相同点
- 子类继承了父类的实例属性和方法
const sub1 = new Sub1()
const sub2 = new Sub2()
sub1.hasOwnProperty('name') // true
sub2.hasOwnProperty('name') // true
- 子类继承了父类的原型属性和方法,即子类的原型对象的[[Prototype]]指针指向父类的原型对象(原型链)
Sub1.prototype.__proto__ === Super.prototype // true
Sub2.prototype.__proto__ === Super.prototype //true
两者的不同点
都知道class是语法糖,虽然本质上还是原型继承那一套,但是从语法规范以及实现上而言,还是有些微差别。
ES6静态属性和方法的继承
ES6存在两条“继承链”:
- 其一,实例和原型的属性及方法的继承与ES5基本一致;
- 其二,构造函数继承,在JS中万物皆是对象,构造函数本身也是个对象,它的[[Prototype]]指针原本是指向Function.prototype,ES6为继承父类的静态属性和方法,将子类的[[Prototype]]指针直接指向了父类,这样就通过原型链访问到了父类的静态属性和方法。
Super.static = 'static'
Sub1.static // undefined
Sub2.static // 'static'
Sub1.__proto__ === Super // false
Sub2.__proto__ === Super // true
Sub1.hasOwnProperty('static') // false
Sub2.hasOwnProperty('static') // false
super关键字
- ES5是先构造子类的this,然后将父类的实例属性通过call或者bind绑定到子类this上;
- ES6子类是先构造父类this,然后在this上添加属性和方法,所以子类必须在constructor()中显式调用super()才能使用this,因为子类并不存在this