前言
在巩固继承这部分知识时,突然想到早期分享时同事提出的一些疑问,即实现 JS 继承具体有那些方法。在经过一番调研后,此篇便是我所总结出的比较全面的方法。当然,文中记录的仅是一些基础的继承方法以及它们之间的的区别,以供参考。
(原型)继承
JavaScript 中有许多模拟类行为的方法,其中“继承”(更贴切的说法是委托)便是其中的一项,没有“继承”机制,JavaScript 中的类就是一个空架子。
假如我们以此例作为继承的模板,归结下来总共有 5 种继承的写法。
1 | // 父类 |
隐式链接法
ES6 之前比较非常规的写法是手动更改 __proto__
属性来最简化链式继承。
1 | Child.prototype.__proto__ = Father.prototype; |
优点:操作很简便直接
缺点:该方法并非标准,并且无法兼容所有浏览器,慎用。
直接引用法
直接将子类的 prototype 对象指向父类的 prototype 对象。
1 | Child.prototype = Father.prototype; |
优点:操作简单
缺点: Child 的 prototype 属性修改会直接影响到父类,此写法其实相当于直接在父类进行修改,新创建一个子类有些多此一举。
实例化方法
子类的 prototype 属性为父类的实例对象,借由对象的 prototype 链来传递。
1 | Child.prototype = new Father(); |
优点:能基本满足“继承”的需求
缺点:
- 需要创建一个新对象,然后将旧对象(原本存在的 prototype 对象,带有 constructor)抛弃掉,不能直接修改默认的 prototype 对象。
- 会产生副作用,比如:原型(prototype)上会有“脏”属性,本例的 prototype 上会有多余的 age 属性。
- 必要时还得自己重写一下 Child 的 constructor 指向,如“寄生组合式继承”。
“寄生组合式继承” 在 ES5 中为最佳写法,如果不考虑其稍微的复杂性。
对象构造法
通过 Object.create
方法来构造一个合适的关联对象。
1 | Child.prototype = Object.create(Father.prototype); |
优点:在 ES6 之前的最佳之选,优于实例化方法。
缺点:与实例化方法基本相同,但没有副作用。此外,其隐式 __proto__
链比实例化方法要多一层,链式查找方法相对会耗时长一点点,基本可忽略。
设置原型法
使用 ES6 的原型设置方法,此法在 ES6 环境为最佳之选,没有之一。
1 | Object.setPrototypeOf(Child.prototype, Father.prototype); |
优点:能覆盖所有需求,清晰易懂,可读性强。
缺点:性能比 Object.create
稍微差点,如果考虑性能的话可优先使用 Object.create
方法