跳到主要内容

继承的方式2:借助原型链(Prototype Chain Inheritance)

面试速答(30 秒版 TL;DR)

  • 核心:让 Child.prototype 成为 Parent 的一个实例(或等价的“以 Parent.prototype 为原型的对象”),从而让子类实例沿原型链访问父类原型方法。
  • 经典写法(不推荐):Child.prototype = new Parent()
  • 主要问题:无法给父构造函数传参(或很别扭);父构造函数执行一次会把引用类型属性挂到 Child.prototype 上,导致所有子实例共享同一份引用类型属性

图示:属性查找沿着原型链向上委托


经典实现(不推荐)与问题演示

function Parent() {
this.tags = []; // 引用类型
}

Parent.prototype.add = function (x) {
this.tags.push(x);
};

function Child() {}

Child.prototype = new Parent(); // Parent 构造函数执行了一次
Child.prototype.constructor = Child;

const c1 = new Child();
const c2 = new Child();

c1.add("a");
console.log(c1.tags); // ["a"]
console.log(c2.tags); // ["a"] 共享了同一个 tags(来自 Child.prototype)

为什么会共享:因为 tags 被创建在 Child.prototype 上,而所有实例读取/写入时都访问到同一个引用。


常见追问

Q1:那我把 tags 放到构造函数里不是就好了?

放构造函数里当然更合理,但这套“纯原型链继承”本身不调用父构造函数去初始化每个实例,所以你最终还是会走向“组合继承”(call + 原型链)。

Q2:只用原型链继承,有什么好处?

实现极其简单,并且能复用父类原型方法;但现实项目中通常要结合 call 才能同时解决“实例属性初始化”和“原型方法复用”。


易错点/坑

  • Child.prototype = new Parent() 会执行父构造函数,可能带来副作用(发请求、注册事件、读环境等)。
  • 需要手动修正 constructor 指回 Child

速记要点(可背诵)

  • 纯原型链继承能拿到父类原型方法,但会引入“共享引用类型属性”和“父构造函数副作用”等问题。