基于 JavaScript 类继承扩展时钟:为 Clock 添加可配置精度参数(ExtendedClock 实战解析)

📅 发布时间:2026/10/6 18:46:52
基于 JavaScript 类继承扩展时钟:为 Clock 添加可配置精度参数(ExtendedClock 实战解析)
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本文以 Modern JavaScript Tutorial 的 JavaScript 核心教程1-js中「类继承」章节的实战任务为骨架深入拆解如何在不修改父类的前提下通过class extends、super()构造器调用与方法覆写为定时打印时间的Clock类扩展出支持自定义刷新精度precision的ExtendedClock子类并剖析派生类构造器必须先行调用super()的底层原因帮助你彻底掌握 JavaScript 类继承的正确姿势。任务背景一个每秒打印一次的 Clock在进入实现之前先回顾任务本身。该练习位于 class-inheritance 任务目录要求如下已有Clock类它每隔一秒在控制台打印当前时间创建一个继承自Clock的新类ExtendedClock为其新增参数precision——即两次「滴答」之间间隔的毫秒数默认值为10001 秒你的代码必须放在extended-clock.js文件中不得修改原始的clock.js只能在它的基础上做扩展。这是一个教科书级别的继承实践场景父类功能可用但不够灵活刷新间隔被写死为 1000ms子类需要在不侵入父类源码的前提下复用其全部能力并覆盖「行为参数」。父类 Clock 源码解析任务的起点是 source.view/clock.js也是 solution.view/clock.js完整代码如下class Clock { constructor({ template }) { this.template template; } render() { let date new Date(); let hours date.getHours(); if (hours 10) hours 0 hours; let mins date.getMinutes(); if (mins 10) mins 0 mins; let secs date.getSeconds(); if (secs 10) secs 0 secs; let output this.template .replace(h, hours) .replace(m, mins) .replace(s, secs); console.log(output); } stop() { clearInterval(this.timer); } start() { this.render(); this.timer setInterval(() this.render(), 1000); } }对其结构做三点拆解方便与后续的扩展逻辑对照构造器接收一个配置对象constructor({ template })使用了对象解构参数调用方传入{ template: h:m:s }时this.template被赋值为模板字符串其中h、m、s是小时、分钟、秒的占位符。render()负责格式化并输出读取系统时间对小时、分钟、秒分别补零if (hours 10) hours 0 hours;再通过字符串的replace方法把h:m:s模板中的占位符逐一替换为实际数值最后console.log打印。start()与stop()管理定时器start()先立即渲染一次再用setInterval每 1000ms 渲染一次并将定时器 ID 保存在this.timerstop()通过clearInterval(this.timer)清除定时器。注意setInterval的回调使用了箭头函数() this.render()确保回调中的this始终指向实例。关键缺陷一目了然刷新间隔 1000 被硬编码在start()中。如果想让时钟每 10 秒才「滴答」一次或者以更精细的节奏刷新就必须改动父类——而任务明确禁止这样做。这正是引入子类的动机。标准答案ExtendedClock 的完整实现solution.view/extended-clock.js 给出了完整解法class ExtendedClock extends Clock { constructor(options) { super(options); let { precision 1000 } options; this.precision precision; } start() { this.render(); this.timer setInterval(() this.render(), this.precision); } };这段代码只用了三处语法却完整实现了「继承 扩展 覆写」三个层次的目标语法元素作用class ExtendedClock extends Clock声明继承关系ExtendedClock.prototype的原型被指向Clock.prototype子类自动获得render()、stop()等父类方法super(options)在子类构造器中调用父类构造器先完成this.template template的初始化start()覆写完全重写父类start()唯一差异是把硬编码的1000换成实例属性this.precision对照父类源码可以看到ExtendedClock只覆写了start()并未覆写render()和stop()——它们直接从Clock.prototype继承而来这也是「继承」价值的最直观体现父类中与间隔无关的渲染逻辑完全复用子类只关心自己需要改变的部分。解构赋值与默认值precision 的兜底逻辑构造器中的一行值得单独强调let { precision 1000 } options;这是对象解构 默认值的组合写法从options对象中取出precision属性若调用方未提供该属性或显式传入undefined则自动赋值为1000。也就是说new ExtendedClock({ template: h:m:s })→precision 1000行为与父类完全一致new ExtendedClock({ template: h:m:s, precision: 10000 })→precision 10000每 10 秒刷新一次。默认值语义与任务要求「Should be1000(1 second) by default」完全吻合同时保证了向后兼容任何原本使用Clock的调用方式换用ExtendedClock都不会意外改变刷新频率。为什么构造器里必须首先调用 super()派生类构造器的第一条语句必须是super(...)否则会抛出ReferenceError。这不是语法洁癖而是 JavaScript 引擎层面的硬性规定原因在 class-inheritance 章节正文 中有详细论证普通函数以及不继承任何类的基类构造器被new执行时引擎会先创建空对象并绑定为this而派生构造器带有一个特殊内部标记[[ConstructorKind]]: derived它不会自己创建this而是期待父类构造器来完成这个工作。所以ExtendedClock的构造器里constructor(options) { super(options); // ① 必须先调用由 Clock 构造器创建 this 并设置 this.template let { precision 1000 } options; this.precision precision; // ② 之后才能安全地使用 this 添加自己的属性 }如果颠倒顺序先写this.precision precision再写super(options)就会触发与练习 1-class-constructor-error 中相同的经典报错Error: this is not defined——此时this尚不存在。此外还有一个隐含细节若子类不声明自己的构造器引擎会自动生成constructor(...args) { super(...args); }把全部参数透传给父类。本任务中ExtendedClock需要额外初始化precision因此必须显式声明构造器并手动调用super(options)——注意这里传给super的是整个options对象父类用解构从中取template而不是只传template字段。覆写 start()把硬编码间隔替换为实例属性父类start()与子类start()的对比是理解方法覆写的最佳教材// Clock.prototype.start —— 硬编码 1000ms start() { this.render(); this.timer setInterval(() this.render(), 1000); } // ExtendedClock.prototype.start —— 使用 this.precision start() { this.render(); this.timer setInterval(() this.render(), this.precision); }两者只有最后一个参数不同其余逻辑先立即渲染一次、再启动定时器、把定时器 ID 存入this.timer完全一致。这正是「覆写」的正确姿势子类方法在原型链上更靠前调用instance.start()时优先命中子类版本而stop()因为父类实现已经足够通用通过this.timer清除定时器无论定时器由谁创建所以无需覆写——这也验证了this.timer被设计为实例属性的价值它让stop()与start()之间的解耦跨越了继承边界。关于方法覆写还有一个高级知识点如果想在子类方法中「先执行父类逻辑、再追加自己的逻辑」可以使用super.method()。本任务的start()选择了完全重写而非基于父类叠加因为父类的1000常量必须被替换super.start()在这里派不上用场但若未来需要「父类逻辑 额外日志」super.start()就是正确工具。完整运行示例低刷新率时钟solution.view/index.html 展示了扩展后的实际用法!DOCTYPE HTML script srcclock.js/script script srcextended-clock.js/script script let lowResolutionClock new ExtendedClock({ template: h:m:s, precision: 10000 }); lowResolutionClock.start(); /script运行要点脚本加载顺序clock.js必须先于extended-clock.js加载因为ExtendedClock的类定义在求值extends Clock时需要Clock已经存在构造参数传入整个配置对象{ template: h:m:s, precision: 10000 }template被父类构造器消费precision被子类消费各取所需互不干扰预期行为立即在控制台打印一次当前时间此后每 10000ms10 秒打印一次直到调用stop()。而 source.view/index.html 则展示了任务出发时的状态只有Clock在跑ExtendedClock的用法被注释掉等待读者实现后放开。两份 HTML 一前一后构成了「任务态 → 完成态」的完整对照。知识点串联本章节的理论支撑本练习是 Class inheritance 章节 的配套实战两者可以对照阅读理论要点恰好全部落在本任务中extends的底层机制class Child extends Parent会把Child.prototype.[[Prototype]]指向Parent.prototype。查找方法时引擎沿着原型链自底向上搜索找不到render就先看ExtendedClock.prototype再看Clock.prototype——这是ExtendedClock无需重复定义render()/stop()的根本原因。覆写构造器必须先super()[[ConstructorKind]]: derived使得派生构造器不会自动创建this必须先由父类构造器完成创建。本章节还用「覆写类字段」的陷阱做了对比说明父类构造器执行时子类的字段尚未初始化字段初始化发生在super()返回之后因此父类构造器访问this.name这类字段时永远拿到的是父类字段值而访问方法时却会命中子类覆写——这是 JavaScript 类字段与方法的微妙差异。super的内部实现[[HomeObject]]方法被定义时引擎会给函数记录一个内部属性[[HomeObject]]super.method()正是依靠它定位到「定义该方法所在对象」的原型从而找到父类方法。这也解释了为什么含super的方法不宜在不同对象间随意拷贝拷贝后[[HomeObject]]仍指向原对象行为会与预期不符。覆写方法时super.method()的用法若子类方法想「基于父类方法做增量」可在子类方法内部调用super.method()再追加自己的逻辑本任务因需替换常量而选择了完全覆写但两种模式在本章节中均有标准范例。验证与自查清单完成实现后可以从以下维度验证正确性默认值行为new ExtendedClock({ template: h:m:s })的刷新间隔应为 1000ms与父类Clock行为一致自定义精度传入precision: 10000后观察两次console.log输出的时间差约为 10 秒父类未被改动确认clock.js内容与原始版本一致所有扩展都收敛在extended-clock.js中方法复用stop()与render()无需在子类中重复定义即可正常工作说明继承链生效错误排查若构造器内未先调用super(options)就使用this应能观察到this is not defined报错这正是派生构造器规则的直接体现。至此你不仅完成了「为 Clock 添加可配置精度」这一具体练习更通过extends、super()、方法覆写、解构默认值四者的组合掌握了 JavaScript 类继承在真实场景中最常见的完整工作流继承复用父类能力、构造器显式初始化、按需覆写行为参数且全程零侵入父类源码。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐Modern JavaScript Tutorial 实战用 class 继承与 super 构造器实现可调精度的 ExtendedClock 时钟类Modern JavaScript Tutorial 实战用 class 继承与 super 构造器实现可调精度的 ExtendedClock 时钟类 在 M文档/教程前端Bilibili-EvolvedTypeScript配置继承扩展基础配置Bilibili EvolvedTypeScript配置继承扩展基础配置 在Bilibili Evolved项目中TypeScript配置文件的合理组织对项前端音视频JavaScript 类继承机制深度解析JavaScript 类继承机制深度解析 引言 在面向对象编程中类继承是一个核心概念。JavaScript 虽然基于原型继承但通过 ES6 的 class文档教程前端上一篇用 IR 传递测试包验证 Slang 编译器-dump-ir、多目标 emit 与诊断测试的实战方法下一篇PaddleOCR 文本识别模块使用教程模型选型、API 推理与二次开发全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考