Appearance
寄生组合式继承与 class 继承
难度等级:🔴 高级 分类:JavaScript 归档日期:2026-10-09
请介绍 JavaScript 的寄生组合式继承和
class继承,说明原型链、constructor、super,以及类字段与 getter/setter 的关系。
面试口述答案
JavaScript 的继承主要依靠原型链:读取对象属性时,先查自身,没有就沿 [[Prototype]] 向上查找,直到 null。
寄生组合式继承分为两步:通过 Parent.call(this, ...) 执行父构造函数,为每个实例初始化独立属性;通过 Object.create(Parent.prototype) 连接子类原型与父类原型,继承共享方法。它不需要为了连接原型链而执行父构造函数。
替换子类的 prototype 后,通常还要修复 constructor。不修复不会影响实例创建、方法继承和 instanceof,但读取实例的 constructor 时可能得到父构造函数。
class 继承用 extends 自动建立继承关系,用 super() 调用父构造函数。在派生类构造函数中,必须先执行 super(),才能使用 this。super.method() 从父级原型查找方法,但执行时的 this 仍然是当前实例。
两种写法并不完全等价。extends 还会建立构造函数自身的原型链,从而继承静态成员;类方法默认不可枚举,类内部采用严格模式,类构造函数必须使用 new 调用。
属性方面,构造函数中的 this.name = value 和类字段 name = value 通常都产生实例自身属性,但前者是赋值,可能触发原型上的 setter;后者直接定义自身属性,不触发继承的 setter。类体中的普通方法和 getter/setter 则定义在原型上。
最小代码示例
两段代码分别运行。
寄生组合式继承:
js
function Animal(name) {
this.name = name
this.favoriteFoods = [] // 每个实例独立拥有
}
Animal.prototype.introduce = function () {
return `我叫${this.name}`
}
function Cat(name, personality) {
Animal.call(this, name)
this.personality = personality
}
Cat.prototype = Object.create(Animal.prototype)
Object.defineProperty(Cat.prototype, 'constructor', {
value: Cat,
writable: true,
configurable: true,
enumerable: false,
})
Cat.prototype.introduce = function () {
return `${Animal.prototype.introduce.call(this)},性格${this.personality}`
}
const huhui = new Cat('灰灰', '安静')class 继承:
js
class Animal {
constructor(name) {
this.name = name
this.favoriteFoods = []
}
introduce() {
return `我叫${this.name}`
}
}
class Cat extends Animal {
constructor(name, personality) {
super(name)
this.personality = personality
}
introduce() {
return `${super.introduce()},性格${this.personality}`
}
}
const huhui = new Cat('灰灰', '安静')两者的实例原型链都是:
js
Object.getPrototypeOf(huhui) === Cat.prototype // true
Object.getPrototypeOf(Cat.prototype) === Animal.prototype // true
Object.getPrototypeOf(Animal.prototype) === Object.prototype // true
Object.getPrototypeOf(Object.prototype) === null // true追问要点
| 追问 | 回答要点 |
|---|---|
prototype、[[Prototype]]、__proto__ 有什么区别? | prototype 是构造函数提供给实例的原型对象;[[Prototype]] 是对象内部的原型关系;__proto__ 是访问该关系的传统访问器,通常使用 Object.getPrototypeOf() 读取 |
不修复 constructor 会怎样? | 读取时可能沿原型链得到父构造函数;不影响 new Child() 和普通的 instanceof 检查 |
instanceof 判断什么? | 通常检查右侧构造函数的 prototype 是否出现在左侧对象的原型链上 |
| 实例属性和原型属性怎么选择? | 独立状态放实例上,共享方法放原型上;数组、对象放原型上会共享其内容 |
| 给实例的同名属性赋值会怎样? | 对普通可写原型数据属性,会创建自身属性并遮蔽原型属性;访问器属性要遵循 setter 规则 |
super.name 能读取父构造函数创建的 name 吗? | 不能直接读到实例自身属性;应使用 this.name。实例方法里的 super.name 从父级原型开始查找 |
super 调用 getter 时,this 是谁? | 当前调用的实例 |
| 类字段和构造函数赋值同时存在,谁生效? | 基类字段先初始化,再执行构造函数体;子类字段在 super() 返回后、子类构造函数剩余代码执行前初始化 |
原型上只有 get name(),执行 this.name = value 会怎样? | 没有自身属性遮蔽时,类中的赋值会抛出 TypeError;类字段 name = value 则直接创建自身属性,遮蔽 getter |
特别注意:constructor 是可修改的普通属性,不是决定继承关系的机制;super 决定父级属性的查找起点,并不代表另一个“父类实例”。
完整示例:动物 → 猫 → 美短
用三层继承演示实例属性、原型上的共享默认值与方法、方法重写和调用父级实现。两段代码分别运行。
1. 寄生组合式继承
js
// ===== 1. 动物 =====
function Animal(name, age) {
this.name = name
this.age = age
this.isAlive = true
// 每个实例拥有独立数组
this.favoriteFoods = []
}
// 原型上的共享默认值
Animal.prototype.kingdom = '动物界'
// 原型上的共享方法
Animal.prototype.introduce = function () {
return `我叫${this.name},今年${this.age}岁。`
}
Animal.prototype.addFavoriteFood = function (food) {
this.favoriteFoods.push(food)
}
Animal.prototype.birthday = function () {
this.age += 1
return `${this.name}现在${this.age}岁了。`
}
// ===== 2. 猫 =====
function Cat(name, age, personality) {
// 初始化父级实例属性
Animal.call(this, name, age)
this.personality = personality
}
// 连接原型链,不执行 Animal 构造函数
Cat.prototype = Object.create(Animal.prototype)
// 修复 constructor
Object.defineProperty(Cat.prototype, 'constructor', {
value: Cat,
writable: true,
configurable: true,
enumerable: false,
})
Cat.prototype.species = '猫'
Cat.prototype.meow = function () {
return `${this.name}:喵~`
}
// 重写方法,并调用父级实现
Cat.prototype.introduce = function () {
const basicInfo = Animal.prototype.introduce.call(this)
return `${basicInfo}我是一只猫,性格${this.personality}。`
}
// ===== 3. 美短 =====
function AmericanShorthair(name, age, personality, coatColor) {
Cat.call(this, name, age, personality)
this.coatColor = coatColor
}
AmericanShorthair.prototype = Object.create(Cat.prototype)
Object.defineProperty(AmericanShorthair.prototype, 'constructor', {
value: AmericanShorthair,
writable: true,
configurable: true,
enumerable: false,
})
AmericanShorthair.prototype.breed = '美国短毛猫'
AmericanShorthair.prototype.showCoat = function () {
return `${this.name}的毛色是${this.coatColor}。`
}
AmericanShorthair.prototype.introduce = function () {
const catInfo = Cat.prototype.introduce.call(this)
return `${catInfo}我的品种是${this.breed},毛色是${this.coatColor}。`
}
// ===== 4. 创建灰灰 =====
const huhui = new AmericanShorthair(
'灰灰',
10,
'安静',
'银虎斑',
)
huhui.addFavoriteFood('小鱼干')
huhui.addFavoriteFood('鸡肉')
console.log(huhui.introduce())
// 我叫灰灰,今年10岁。我是一只猫,性格安静。
// 我的品种是美国短毛猫,毛色是银虎斑。
console.log(huhui.meow()) // 灰灰:喵~
console.log(huhui.showCoat()) // 灰灰的毛色是银虎斑。
console.log(huhui.favoriteFoods) // ['小鱼干', '鸡肉']2. class 继承
js
// ===== 1. 动物 =====
class Animal {
constructor(name, age) {
this.name = name
this.age = age
this.isAlive = true
// 每个实例拥有独立数组
this.favoriteFoods = []
}
// 类方法自动定义在原型上
introduce() {
return `我叫${this.name},今年${this.age}岁。`
}
addFavoriteFood(food) {
this.favoriteFoods.push(food)
}
birthday() {
this.age += 1
return `${this.name}现在${this.age}岁了。`
}
}
// 普通数据属性手动放在原型上
Animal.prototype.kingdom = '动物界'
// ===== 2. 猫 =====
class Cat extends Animal {
constructor(name, age, personality) {
// 先调用 super,才能使用 this
super(name, age)
this.personality = personality
}
meow() {
return `${this.name}:喵~`
}
// 重写方法,并调用父级实现
introduce() {
const basicInfo = super.introduce()
return `${basicInfo}我是一只猫,性格${this.personality}。`
}
}
Cat.prototype.species = '猫'
// ===== 3. 美短 =====
class AmericanShorthair extends Cat {
constructor(name, age, personality, coatColor) {
super(name, age, personality)
this.coatColor = coatColor
}
showCoat() {
return `${this.name}的毛色是${this.coatColor}。`
}
introduce() {
const catInfo = super.introduce()
return `${catInfo}我的品种是${this.breed},毛色是${this.coatColor}。`
}
}
AmericanShorthair.prototype.breed = '美国短毛猫'
// ===== 4. 创建灰灰 =====
const huhui = new AmericanShorthair(
'灰灰',
10,
'安静',
'银虎斑',
)
huhui.addFavoriteFood('小鱼干')
huhui.addFavoriteFood('鸡肉')
console.log(huhui.introduce())
// 我叫灰灰,今年10岁。我是一只猫,性格安静。
// 我的品种是美国短毛猫,毛色是银虎斑。
console.log(huhui.meow()) // 灰灰:喵~
console.log(huhui.showCoat()) // 灰灰的毛色是银虎斑。
console.log(huhui.favoriteFoods) // ['小鱼干', '鸡肉']3. 在控制台查看灰灰的原型链
在浏览器控制台运行寄生组合式继承示例后,执行 console.dir(huhui) 展开:
