JavaScript 继承
JavaScript 继承是经常被深入考察的话题,以下是一些常见的问题:
基础概念问题
- JavaScript 中有哪些实现继承的方式?
-
原型链继承
-
构造函数继承(借用构造函数)
-
组合继承(原型链+构造函数)
-
原型式继承
-
寄生式继承
-
寄生组合式继承
-
ES6 Class 继承
- 原型链继承的优缺点是什么?
-
优点:简单,能继承父类原型上的属性和方法
-
缺点:所有实例共享原型属性;无法向父类构造函数传参
- 构造函数继承的优缺点是什么?
-
优点:可以在子类中向父类传递参数;避免了引用属性共享问题
-
缺点:不能继承父类原型上的方法;方法都在构造函数中定义,无法复用
深入技术问题
- 组合继承有什么问题?如何优化?
-
问题:会调用两次父类构造函数
-
优化:使用寄生组合式继承
- 寄生组合式继承为什么是最佳实践?
-
只调用一次父类构造函数
-
避免在子类原型上创建不必要的属性
-
保持原型链不变
- ES6 Class 继承和 ES5 继承有什么区别?
-
Class 继承使用
extends和super -
Class 继承实质上是语法糖,底层还是基于原型链
-
Class 继承有更严格的语法检查
代码实现问题
- 手写一个寄生组合式继承的实现
function inheritPrototype(subType, superType) {
const prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
- 解释下面代码的输出结果
function Parent() {
this.name = 'parent';
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {
this.name = 'child';
}
Child.prototype = new Parent();
const child = new Child();
child.sayName(); // 输出什么?为什么?
实际应用问题
- 如何实现多重继承?
- JavaScript 本身不支持多重继承,但可以通过混入(Mixin)模式模拟
-
在大型项目中,你会选择哪种继承方式?为什么?
-
通常会选择 ES6 Class 继承,因为语法清晰,易于维护
-
如果需要更精细控制,可能会选择组合继承或寄生组合式继承
-
-
如何判断一个对象是否是另一个对象的原型?
-
使用
Object.prototype.isPrototypeOf() -
或
instanceof操作符
-
准备这些问题时,最好能够结合实际代码示例来解释,展示你对 JavaScript 继承机制的深入理解。
function Parent() {
this.name = 'parent';
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {
this.name = 'child';
}
Child.prototype = new Parent();
const child = new Child();
child.sayName(); // 输出什么?为什么?
这段代码的输出结果是:
"child"
代码解析:
-
Parent构造函数:
-
定义了一个实例属性
this.name = 'parent' -
在
Parent.prototype上添加了一个方法sayName(),用于打印this.name
-
Child构造函数:
-
定义了一个实例属性
this.name = 'child' -
将
Child.prototype设置为new Parent(),即Child继承Parent的原型链
-
const child = new Child():
-
执行
Child构造函数,this.name被赋值为'child' -
child的原型 (__proto__) 指向Parent的实例(new Parent())
-
child.sayName():
-
child本身没有sayName方法,所以沿着原型链查找 -
在
Parent.prototype上找到sayName并调用 -
sayName()中的this指向child实例,所以this.name是'child'(而不是Parent的'parent')
关键点:
-
this的指向:sayName()被child调用,所以this指向child实例,而不是Parent实例。 -
原型链查找:
child本身没有sayName,所以会去Child.prototype(即new Parent())上找,再往上到Parent.prototype找到sayName。
如果修改代码:
Child.prototype = Parent.prototype; // 直接继承 Parent.prototype,而不是 new Parent()
const child = new Child();
child.sayName(); // 仍然输出 "child"
- 这样
Child.prototype直接指向Parent.prototype,不再执行new Parent(),但this仍然指向child,所以this.name还是'child'。
总结:
-
原型链继承 的关键是
Child.prototype = new Parent(),使得Child的实例可以访问Parent原型上的方法。 -
this指向调用者,所以child.sayName()打印的是child的name,而不是Parent的name。










网友评论