用 98K 狙击步枪实现 JS 继承

手写继承就像使用 Kar98 狙击步枪,老、繁琐,但是稳固、可靠。
要实现子类继承父类的属性和方法,Java 来的小伙伴可能直接一个 extend 就解决了,JS 里确实也有这个语法糖,可见:ES6 面向对象编程的语法糖。
但是手写一遍能帮助你深入理解 JS 的原型链以及面向对象编程,更别提后来的很多新特性也是基于这个实现的
例子介绍
这次我们就来实现一个模态框(Modal),父类模态框仅仅有弹窗标题和内容两个属性以及一个 show 方法
子类模态框 Modal_plus
除了有父亲的所有属性和方法,还会有自己独有的 theme 属性和 changeTitle 方法。
源码实现
1 | function Modal(title, content) { |
关键点 1:继承属性
1 | Modal.call(this, title, content); |
这段代码本质就是把父类里的两条 this 赋值语句拿过来执行一遍,但是是赋值给子类自己,因为第一个参数 this 的执行环境是在子类里。call 改变了执行父类构造函数时候的 this 环境。
这句执行完以后,父类的属性就都给子类了。
有疑问的话请见:
关键点2:实现原型链继承
1 | // 完成原型链继承,到这一步父亲的 show 方法也能用了 |
Object.create()
是一个非常纯粹的方法,它的目的只有一个:
创建一个新对象,并让这个新对象的 proto 指向你传入的参数。
为什么不直接写成 Modal_plus.prototype = Modal.prototype
?
Modal_plus.prototype = Modal.prototype
?这样做,Modal_plus.prototype
和 Modal.prototype
会指向同一个对象。之后如果你给子类添加方法,父类也会得到,这显然是错误的。
这一步之后,原型链就此形成。
关键点3:修复 constructor
constructor 属性是什么?
默认情况下,任何函数的 prototype 对象上,都有一个 constructor 属性,这个属性会指回该函数本身。
-
在代码的最开始,Modal.prototype.constructor 指向 Modal 函数。
-
在代码的最开始,Modal_plus.prototype.constructor 指向 Modal_plus 函数。
这个属性的主要作用是让实例能够知道自己是由哪个构造函数创建的。例如,modal.constructor 应该返回 Modal。
但是执行完刚才的原型链继承代码后,会产生一个副作用:
Modal_plus.prototype.constructor 值变成了 Modal,而不是期望的 Modal_plus。
这显然是不对的,它理应指向它自己。
所以,我们简单修复一下即可,很好理解:
1 | // 修复 construct 指针,否则它会指向 modal 而不是期望的它自己 |
使用测试
测试有没有继承到父亲的方法和属性
1 | // ================ 测试有没有继承到父亲的方法和属性 |
测试父亲会不会受子定义的方法影响
1 | // ================ 测试父亲会不会受子定义的方法影响 |
如果刚才没用 Object.create()
,而是直接赋值,那这边就会发现父亲也能执行孩子的方法。
测试 construct 情况
1 | // ================ 测试 construct 情况 |
- 标题: 用 98K 狙击步枪实现 JS 继承
- 作者: 三葉Leaves
- 创建于 : 2025-07-22 00:00:00
- 更新于 : 2025-08-15 12:09:51
- 链接: https://blog.oksanye.com/933053f33e05/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
预览: