JS的各种类型继承模式总结
原型式继承
- 原型式继承就是
Object.create()
的实现原理。 - 原型式继承非常适用于不需要单独创建构造函数,但仍需要在对象实例之间共享信息的场合。
1 |
|
记住
create()
这个函数,下文的代码中会反复用到
寄生式继承
1 |
|
与原型式继承比较接近的是寄生式继承,它的思想是在原型式继承的基础上,以某种方式对子对象进行改造(增强)。
寄生式 vs 原型式
与原型式继承的不同之处是,寄生式继承不仅实现了实例之间的继承关系,并且增强了子实例。
寄生式与原型式都适合于不需要构造函数,只需关注对象实例的场景
盗用构造函数
由于原型链的原因,以上两种继承方式创建的对象之间是会共享引用类型的属性的,这导致不同的对象之间无法拥有自己独立的数据。
通过调用父类构造函数的的call / apply函数,可以实现夫类型构造函数的借用。使得子类型的构造函数也能创建独立的父类型数据。
1 |
|
由SubType
构造函数创建的对象也包含SupType
构造函数的属性,并且是对象本身所有的。但缺点是不能重用父类型的方法(大量同名同作用,但内存地址不相同的函数)
组合继承
1 |
|
综合了原型链和盗用构造函数,使得子类型的实例既可以实现方法重用,又可以拥有自己的属性数据
寄生式组合继承
组合继承实现了基本的方法重用和独立属性,但他存在着效率问题。
- 最主要的效率问题是父类型的构造函数被调用了两次,一次是在盗用构造函数时,另一次是在给子类型构造函数的原型赋值时。实际上,对于第二次调用,目的只是为了重写子类型的原型,完全不需要调用父类型构造函数来实例化一个父类对象这么麻烦,可以通过创建一个继承父类型的简单对象(寄生于父类型原型的寄生虫),然后增强这个对象(寄生虫),实现子类型对父类型的继承
- 其次是子类型构造函数的原型中存在着冗余的父类型的属性。
1 |
|
可以看到没有了冗余的父类型属性,而且不同的子类型示例可以重用同一个父类型的方法。
总结
- 原型式继承和寄生式继承适用于继承某个对象实例的场景。创建的对象存在数据共享的问题
- 在子类型构造函数中盗用父类型构造函数,解决了数据共享问题,但是引发方法不能重用的问题
- 组合继承,组合了盗用构造函数和原型链,解决了上述两个问题,但存在效率问题和冗余父类型属性
- 寄生式组合继承解决了组合继承的效率问题、避免了冗余对的父类属性,是最佳的类型继承范式,是ES6 extends关键字的实现原理。
总而言之,在ES6之前,JS 的确可以通过各种操作模拟类似于类的行为,但最终实现的代码显得非常冗长和杂乱,这也正是ES6 推出类(class) 这个语法糖结构的必要性所在。但同时需要指明的是,class背后使用的仍然是原型和构造函数的概念。
JS的各种类型继承模式总结
http://example.com/2023/05/07/inherit/