继承方式
ES5 prototype 继承
通过原型链(构造函数 + [[prototype]]
)指向实现继承。 (备注:后续__proto__我都会写成[[prototype]]这种形式)
子类的 prototype 为父类对象的一个实例。因此子类的原型对象包含指向父类的原型对象的指针,父类的实例属性为子类原型的属性。
// 父类:function SuperType;子类:function SubType;
SubType.prototype = new SuperType(); // SubType 继承 SuperType
// 根据上一节原型链讲到的知识点:实例化对象的__proto__等于构造函数的prototype
SubType.prototype.__proto__ === SuperType.prototype // true
上边的继承关系如下图:
内部实现机制上,ES5 的继承,实质是先创造子类的实例对象this上,然后再将父类的方法添加到这个this上。类似使用:Father.apply(this)
ES6 class 继承
通过class的extends
+ super
实现继承。
子类没有自己的this对象,因此必须在 constructor 中通过 super 继承父类的 this 对象,而后对此this对象进行添加方法和属性。
super关键字在构造函数中表示父类的构造函数,用来新建父类的 this 对象。
内部实现机制上,ES6 的继承机制完全不同,实质是先创造父类的实例对象this---需要提前调用super方法,然后再用子类的构造函数修改this指针。
super用法
super
可以作为函数和对象使用的。
当作为函数使用的时候,只能在子类的构造函数中使用----表示父类的构造函数,但是 super 中的 this 指向的是子类的实例,因此在子类中super()表示的是 Father.prototype.constructor.call(this)。
当作为对象使用的时候,super表示父类的原型对象,即表示 Father.prototype
二者区别
答:不是完全一样的,主要有以下几个差异点:
写法不一样。class的继承通过extends关键字和super函数、super方法继承。(关于super实现继承的使用方式,具体我就不展开了) 类内部定义的方法都是不可枚举的,这个 ES5 不一样 类不存在变量提升,这一点与 ES5 完全不同 类相当于实例的原型,所有在类中定义的方法都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就成为静态方法 内部实现机制不一样。
ES5 prototype 继承 内部实现方式
ES5的继承,实质上是先创造子类的实例对象this,然后再将父类的方法添加到子类(this)上面---Father.apply(this)。
ES6 class 继承 内部实现方式
ES6的继承机制完全不同,实质上是先创造父类的实例对象this,并将父类的属性和方法放到this上(前提是通过super函数调用),然后再用子类的构造函数修改this。
因为实现机制不同,导致这两种继承在继承原生构造函数时有些差异:
es5的写法不能继承原生构造函数(比如Array、Number等)
因为es5的继承是先创造子类的实例对象this,再将父类原型的属性和方法重写到子类上,因为没法访问父类的内部属性,导致es5的继承方式无法继原生的构造函数。
es6允许继承构造函数生成子类。因为es6是先创建父类的实例对象this,然后再用子类的构造函数修饰,所以子类就可以继承父类的所有属性和方法。因此class可以继承并自定义原生构造函数的子类。extends不仅可以用来继承类,还能用来继承原生构造函数,因此也就可以在原生数据结构的基础上,构造自定义的数据结构。
扩展
关于内部实现机制的说明,可以参考《阮一峰的es6文档-class的继承》相关部分
文中有不妥之处欢迎留言讨论,更多【每日一题】尽在公众号 【前端印记】。