真实面经题目 · 原创解析

说一下 JS 的继承机制

这道题考察 JS 继承背后的原型链查找、实例属性初始化、方法复用和 ES6 class 语义。高质量回答要把对象怎么找属性、子类实例怎么拿到父类初始化结果、不同继承写法各自的坑讲清楚。

出现于:携程 · 前端

60 秒回答模板

JS 的继承本质是两件事:属性查找沿原型链向上走,实例创建时把父构造函数里的实例属性初始化到当前对象上。原型链继承能复用父类原型方法,但如果用 new Parent() 作为 Child.prototype,父构造函数里的引用属性会被所有子实例共享;构造函数继承用 Parent.call(this) 能隔离实例属性,但拿不到父类原型方法。组合继承结合两者,但会执行两次父构造函数。更推荐寄生组合继承:Child.prototype = Object.create(Parent.prototype) 只建立原型连接,Parent.call(this) 只负责初始化实例属性,再把 constructor 指回 Child。ES6 class extends 是这套机制的语法封装,子类构造函数里必须先 super() 才能使用 this。

考点 核心机制与工程取舍
难度 中高频面试题
回答目标 按定义、机制、场景讲清楚

深入解析

01

原型链查找

访问 child.name 时,JS 先查实例自身属性;没有再查 Child.prototype;还没有就继续查 Parent.prototype、Object.prototype,最终到 null。继承能复用方法,是因为子实例的原型链能连到父类原型,而不是复制了一份父类方法。

02

构造函数初始化

new Child() 会创建空对象、把对象原型连到 Child.prototype、用这个对象作为 this 执行 Child。父构造函数里的 this.items = [] 这类实例属性,只有在子构造函数中执行 Parent.call(this, ...) 才会落到每个子实例自己身上。

03

两种基础写法的缺陷

Child.prototype = new Parent() 能访问 Parent.prototype 上的方法,但 new Parent() 产生的 items、options 等引用属性会挂在 Child.prototype 上,多个子实例会共享。只写 Parent.call(this) 能让实例属性独立,但 Child.prototype 没有连到 Parent.prototype,父类原型方法无法复用。

04

组合继承与寄生组合继承

组合继承通常写 Parent.call(this) 加 Child.prototype = new Parent(),实例属性隔离、方法也能复用,但父构造函数执行两次。寄生组合继承把连原型和初始化实例属性拆开:Object.create(Parent.prototype) 只连原型不执行父构造;Parent.call(this) 只在创建实例时执行一次;constructor 再修正回 Child。

05

class extends 与 super

class Child extends Parent 底层仍然建立 Child.prototype 到 Parent.prototype 的链,同时处理静态继承。super(...args) 调用父类构造逻辑来初始化当前实例;派生类构造函数里 this 必须等 super() 返回后才可用,super.method() 查的是父类原型方法,并以当前子实例作为 this 调用。

易错点

  • 把 Child.prototype = Parent.prototype 当成继承写法,导致子类原型方法污染父类原型。
  • 只用 Child.prototype = new Parent(),却不说明父构造函数里的数组、对象会变成共享原型属性。
  • 只写 Parent.call(this) 就说完成继承,漏掉 Parent.prototype 上的方法无法复用。
  • 讲组合继承时漏掉父构造函数执行两次,无法解释副作用、昂贵初始化或必填参数的成本。
  • Object.create(Parent.prototype) 后忘记修正 Child.prototype.constructor = Child。
  • 把 class extends 说成全新的继承体系,忽略它仍然依赖原型链和 super() 规则。

面试官追问

new Child() 具体做了什么?

new 会创建新对象,把新对象的内部原型指向 Child.prototype,再用这个对象作为 this 执行 Child。继承关系来自 Child.prototype 后面接到哪里;实例属性来自构造函数执行时给 this 赋值。

为什么 Child.prototype = new Parent() 会导致引用属性共享?

这次 new Parent() 创建的是所有子实例共同使用的原型对象。如果 Parent 里有 this.items = [],这个数组会挂到 Child.prototype.items 上,多个子实例自身没有 items 时读到同一个数组。

组合继承为什么会执行两次父构造函数?

第一次发生在 Child.prototype = new Parent(),目的是把子原型接到父原型,但顺带执行了父构造;第二次发生在 Child 构造函数里的 Parent.call(this),目的是给真实子实例初始化父类实例属性。

寄生组合继承为什么更合理?

它把连原型和初始化实例属性拆成两个动作。Object.create(Parent.prototype) 创建以父原型为原型的新对象,不执行 Parent;Parent.call(this) 只在创建子实例时执行一次。

class extends 只是语法糖吗?

class 没有改变 JS 基于原型的继承模型,但统一封装了原型链、构造函数调用、静态继承和方法定义语义。派生类构造函数里必须先 super(),否则 this 不可用。