继承
相关问题
- 关于 ES5 和 ES6 的继承问题
- 原型链概念
回答关键点#
原型链继承 构造函数继承 ES6 类继承
继承是指子类型具备父类型的属性和行为,使代码得以复用,做到设计上的分离。JavaScript 中的继承主要通过原型链和构造函数来实现。
常见的继承方法有:
- ES6 中 class 的继承、
- 原型链继承、
- 寄生组合式继承
知识点深入
1. 原型链
原型链的本质是拓展原型搜索机制。
每个实例对象都有一个私有属性 proto。该属性指向它的构造函数的原型对象 prototype。该原型对象的 proto 也可以指向其他构造函数的 prototype。依次层层向上,直到一个对象的 proto 指向 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或直到这个链表结束(Object.prototype.proto === null)。
2. 原型链继承#
原型链继承的思想:一个引用类型继承另一个引用类型的属性和方法。
function SuperType() {
this.b = [1, 2, 3];
}
function SubType() {}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
var sub1 = new SubType();
var sub2 = new SubType();
// 这里对引用类型的数据进行操作
sub1.b.push(4);
console.log(sub1.b); // [1,2,3,4]
console.log(sub2.b); // [1,2,3,4]
console.log(sub1 instanceof SuperType); // true
优点:
- 父类新增原型方法/原型属性,子类都能访问到。
- 简单、易于实现。 缺点:
- 无法实现多继承。
- 由于原型中的引用值被共享,导致实例上的修改会直接影响到原型。
- 创建子类实例时 ,无法向父类构造函数传参。
3. 构造函数继承#
构造函数继承的思想:子类型构造函数中调用父类的构造函数,使所有需要继承的属性都定义在实例对象上。
function SuperType(name) {
this.name = name;
this.b = [1, 2, 3];
}
SuperType.prototype.say = function () {
console.log("HZFE");
};
function SubType(name) {
SuperType.call(this, name);
}
var sub1 = new SubType();
var sub2 = new SubType();
// 传递参数
var sub3 = new SubType("Hzfe");
sub1.say(); // 使用构造函数继承并没有访问到原型链,say 方法不能调用
console.log(sub3.name); // Hzfe
sub1.b.push(4); // 解决了原型链继承中子类实例共享父类引用属性的问题
console.log(sub1.b); // [1,2,3,4]
console.log(sub2.b); // [1,2,3]
console.log(sub1 instanceof SuperType); // false
优点:
- 解决了原型链继承中子类实例共享父类引用属性的问题。
- 可以在子类型构造函数中向父类构造函数传递参数。
- 可以实现多继承(call 多个父类对象)。
缺点:
- 实例并不是父类的实例,只是子类的实例。
- 只能继承父类的实例属性和方法,不能继承原型属性和方法。
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。