JavaScript继承无统一标准,ES6 class+extends是主流写法,需先调用super();手动操作prototype或Object.setPrototypeOf更底层但易出错;组合优于继承。

JavaScript 实现继承没有单一“标准答案”,关键看目标环境、代码可维护性,以及是否要兼容老版本。ES6 class 语法只是语法糖,底层仍是基于原型链;而手动操作 prototype 或 Object.setPrototypeOf 更贴近本质,也更容易暴露问题。
用 class + extends 是最简明的现代写法
这是目前主流项目首选,语义清晰、支持 super()、能正确处理静态方法和 new.target。
但要注意:必须在子类构造函数中调用 super(),否则会报 ReferenceError: Must call super constructor in derived class before accessing 'this';且 super() 必须在使用 this 前调用。
示例:
立即学习“Java免费学习笔记(深入)”;
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 必须先调用
this.breed = breed;
}
bark() {
console.log(`${this.name} barks`);
}
}
-
extends会自动设置子类的prototype链,并让Dog.prototype.__proto__ === Animal.prototype - 静态方法也会被继承:
Dog.foo()可以访问Animal.foo()(如果定义了) - 不支持多重继承,
extends A, B是语法错误
手动设置 prototype 链是理解继承本质的关键
ES5 及更早环境或想彻底掌控原型关系时,常用 Object.create() 替换子类的 prototype,再补上 constructor 指回自身。
常见错误是直接赋值 Child.prototype = Parent.prototype——这会让两者共享同一原型对象,修改子类方法会意外影响父类。
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
正确做法:
function Parent(name) {
this.name = name;
}
Parent.prototype.speak = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 继承实例属性
this.age = age;
}
Child.prototype = Object.create(Parent.prototype); // 关键:创建新对象,链接到 Parent 原型
Child.prototype.constructor = Child; // 修复 constructor 指向
Child.prototype.play = function() {
console.log(`${this.name} plays`);
};
-
Parent.call(this, ...)确保子类实例拥有父类初始化的属性(如this.name) -
Object.create(Parent.prototype)创建干净的原型链,避免污染 - 漏掉
constructor = Child会导致new Child().__proto__.constructor === Parent,影响类型判断
用 Object.setPrototypeOf() 动态调整原型(慎用)
这个 API 允许运行时修改对象的 [[Prototype]],适合极少数需要动态继承的场景(比如插件系统、沙箱封装),但性能较差,且无法在旧版 IE 中使用。
它不能替代构造函数继承——只改原型链,不执行父类初始化逻辑,所以仍需显式调用 Parent.call(this)。
示例:
立即学习“Java免费学习笔记(深入)”;
function A() {}
A.prototype.hello = () => 'hi';
function B() {}
const b = new B();
Object.setPrototypeOf(b, A.prototype); // 把 b 的原型设为 A.prototype
console.log(b.hello()); // 'hi'
- 仅影响单个实例,不是整个类;若想批量设置,得对每个实例调用
- V8 引擎会对频繁修改
[[Prototype]]的对象降级为慢对象,影响后续属性访问性能 - 与
class不兼容:用class定义的类,其原型是不可写的(writable: false),强行setPrototypeOf会静默失败
真正容易被忽略的是:继承只是复用的一种手段,很多场景下组合(composition)比继承更灵活、更易测试。比如把通用行为抽成独立函数或类,通过属性挂载或参数传入,而不是硬套一层 extends。尤其当父类逻辑复杂、生命周期耦合强时,强行继承反而增加调试成本。










