继承,是 JS 面试绕不开的灵魂拷问。本文将带你一网打尽 JS 继承的所有姿势,配合代码实例和细致讲解,助你面试不再慌张!
一、什么是继承?
继承,就是让子类可以访问到父类的属性和方法。JS 继承的实现方式多如牛毛,面试官最爱考察各种细节和坑点。
二、原型链继承
原理
子类的原型指向父类的实例。所有子类实例共享同一个父类实例。
代码演示
javascript
function Parent() {
this.name = 'parent'
this.like = ['a', 'b', 'c']
}
Child.prototype = new Parent()
function Child() {
this.age = 18
}
let c = new Child()
let d = new Child()
c.like.push('d')
console.log(c.like) // ['a', 'b', 'c', 'd']
console.log(d.like) // ['a', 'b', 'c', 'd']
优缺点
- 优点:实现简单,能访问父类属性和方法。
- 缺点:引用类型属性会被所有实例共享,互相影响,容易踩坑。
面试官小贴士
"你能说说原型链继承的缺陷吗?为什么 like 属性会被所有实例共享?"
三、构造函数继承
原理
在子类构造函数中调用父类构造函数,this 指向子类实例。
代码演示
javascript
Parent.prototype.say = function () {
console.log('hello')
}
function Parent() {
this.name = 'parent'
this.like = ['a', 'b', 'c']
}
function Child() {
this.age = 18
Parent.call(this)
}
let c = new Child()
console.log(c.say) // undefined
优缺点
- 优点:每个实例独立拥有父类属性,引用类型不再共享。
- 缺点:无法继承父类原型上的方法(如 say),只能继承构造函数里的属性。
面试官小贴士
"为什么 c.say 是 undefined?如何让子类也能继承父类原型上的方法?"
四、组合继承
原理
原型链继承 + 构造函数继承,双管齐下。
代码演示
javascript
Parent.prototype.say = function () {
console.log('hello')
}
function Parent() {
this.name = 'parent'
this.like = ['a', 'b', 'c']
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
function Child() {
this.age = 18
Parent.call(this)
}
let c = new Child()
let d = new Child()
d.like.push('d')
console.log(d.like); // ['a', 'b', 'c', 'd']
console.log(c.like); // ['a', 'b', 'c']
console.log(c.say); // function
console.log(c.constructor); // Child
优缺点
- 优点:既能继承父类属性,又能继承父类原型方法,引用类型不共享。
- 缺点:父类构造函数会执行两次(一次给原型,一次给实例),有点浪费性能。
面试官小贴士
"组合继承为什么会调用两次父类构造函数?有没有更优的方案?"
五、原型式继承
原理
用 Object.create 或类似方式,以某对象为原型创建新对象。
代码演示
javascript
let parent = {
name: 'parent',
like: ['a', 'b', 'c']
}
let child1 = Object.create(parent)
let child2 = Object.create(parent)
child1.like.push('d')
console.log(child1.like); // ['a', 'b', 'c', 'd']
console.log(child2.like); // ['a', 'b', 'c', 'd']
优缺点
- 优点:实现简单,适合对象克隆。
- 缺点:引用类型属性依然共享。
面试官小贴士
"Object.create(parent) 和 new Object(parent) 有什么区别?"
六、寄生式继承
原理
在原型式继承基础上,增强返回的新对象。
代码演示
javascript
let parent = {
name: 'parent',
like: ['a', 'b', 'c']
}
function clone(origin) {
let cloneObj = Object.create(origin)
cloneObj.getLike = function() {
return this.like
}
return cloneObj
}
let child1 = clone(parent)
let child2 = clone(parent)
child1.like.push('d')
console.log(child1.like); // ['a', 'b', 'c', 'd']
console.log(child2.like); // ['a', 'b', 'c', 'd']
console.log(child1.getLike()); // ['a', 'b', 'c', 'd']
console.log(child2.getLike()); // ['a', 'b', 'c', 'd']
优缺点
- 优点:可以扩展新对象。
- 缺点:引用类型属性依然共享。
面试官小贴士
"寄生式继承和原型式继承的本质区别是什么?"
七、寄生组合式继承(最优解)
原理
只继承父类原型,不调用父类构造函数,避免性能浪费。
代码演示
javascript
Parent.prototype.getName = function() {
return this.Name
}
function Parent() {
this.Name = 'parent'
this.like = ['a', 'b', 'c']
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
function Child() {
this.age = 18
Parent.call(this)
}
let c1 = new Child()
console.log(c1.getName()); // 'parent'
console.log(c1.constructor); // Child
优缺点
- 优点:只调用一次父类构造函数,性能最佳,继承属性和方法都不落下。
- 缺点:实现稍复杂,但值得!
面试官小贴士
"为什么寄生组合式继承被称为 JS 继承的终极方案?"
八、ES6 类继承
原理
用 class 和 extends 语法糖,优雅实现继承。
代码演示
javascript
class Parent {
constructor() {
this.Name = 'parent'
this.like = ['a', 'b', 'c']
}
getName() {
return this.Name
}
static say() {
console.log('hello');
}
}
class Child extends Parent {
constructor() {
super()
this.age = 18
}
}
let p = new Parent()
console.log(p.getName()); // 'parent'
let c = new Child()
console.log(c.getName()); // 'parent'
优缺点
- 优点:语法简洁,继承关系清晰,原型链自动处理。
- 缺点:底层依然是原型链,只是语法糖。
面试官小贴士
"class 继承和传统原型链继承的本质区别是什么?"
九、知识点总结与面试答题模板
继承方式对比表
方式 | 是否共享引用类型 | 是否继承原型方法 | 构造函数调用次数 | 优缺点 |
---|---|---|---|---|
原型链继承 | 是 | 是 | 1 | 引用类型共享 |
构造函数继承 | 否 | 否 | 1 | 不能继承原型方法 |
组合继承 | 否 | 是 | 2 | 性能浪费 |
原型式继承 | 是 | 是 | 0 | 引用类型共享 |
寄生式继承 | 是 | 是 | 0 | 引用类型共享 |
寄生组合式继承 | 否 | 是 | 1 | 性能最佳 |
ES6 类继承 | 否 | 是 | 1 | 语法糖 |
面试高频问题
- 说说 JS 继承的实现方式及优缺点?
- 为什么原型链继承会导致引用类型属性共享?
- 如何实现一个既能继承属性又能继承方法的子类?
- ES6 的 class 继承和传统继承有什么区别?
十、幽默收尾
JS 继承就像家庭聚会,谁家锅碗瓢盆都能借来用,但有时候大家都用同一个锅,炒出来的菜味道就不一样了!面试官最爱问的那些继承细节,你现在都能用段子和代码轻松拿下!
祝大家面试不再慌张,继承全家桶一把梭!🎉