1.原型链继承
核心:
将父类的实例作为子类的原型。 优点:
- 实现简单。 缺点:
- 包含引用类型值的原型属性会被所有实例共享,这会导致对一个实例的修改会影响另一个实例。
- 要想为子类新增属性和方法,必须在new Cat()这样的语句后执行,不能放在构造器中 如:在创建 Child 的实例时,不能向Cat传参。如果要加,只能在new Cat()以后加 由于这两个问题的存在,实践中很少单独使用原型链。
// 原型链继承
function SuperType() {
this.name = 'zhangsan';
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType() {}
// 父类的实例作为子类的原型
SubType.prototype = new SuperType();
// 测试
let instance1 = new SubType();
instance1.sayName();
2.借用构造函数继承
核心:
用.call()或.apply()将父类构造函数引入子类函数,等于是复制父类的实例属性给子类。 优点:
- 1.只继承了父类构造函数的属性,没有继承父类原型的属性。
- 2.解决了原型链继承的两个缺点。
- 3.可以继承多个构造函数属性(call多个)
- 4.在子实例中可向父实例传参。 缺点:
- 1.只能继承父类构造函数的属性。
- 2.无法实现构造函数的复用。(每次用都需要重新调用)
- 3.每个新实例都有父类构造函数的副本,臃肿。所以借用构造函数继承很少单独使用。
// 借用构造函数继承
function SuperType(name) {
this.name = name;
}
function SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
// 测试
let person1 = new SubType('zhangsan',22);
console.log(person1.name);
console.log(person1.age);
3.组合继承
核心:
通过构造函数实现对实例属性的继承,通过原型链实现对方法的继承。 优点:
- 1.可以继承父类原型上的属性,可以传参,可以复用。
- 2.每个新实例引入的构造函数属性是私有的。 缺点:
- 调用了两次(call一次,new一次)父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
// 组合继承
// 父类
function SuperType(name) {
this.name = name;
this.colors = ['blue','red'];
}
// 父类方法
SuperType.prototype.sayName = function() {
console.log(this.name);
}
// 子类
function SubType(name,age) {
// 继承属性 构造继承
SuperType.call(this,name);
this.age = age;
}
// 继承方法 原型继承
SubType.prototype = new SuperType()
// 子类方法
SubType.prototype.sayAge = function() {
console.log(this.age);
}
// 测试
let person1 = new SubType('zhangsan',22);
person1.sayName();
person1.sayAge();
person1.colors.push('black');
console.log(person1.colors);
4.原型式继承
核心:
ES5 Object.create的模拟实现,将传入的对象作为创建的对象的原型。 优点:
- 参考原型链继承 缺点:
- 参考原型链继承
// 原型式继承
// 先封装一个函数容器,用来输出对象和承载继承的原型
function createObj(o) {
function F() {}
F.prototype = o; // 继承了传入的参数
return new F(); // 返回函数对象
}
// 测试
let person = {
name: 'zhangsan',
friends: ['lisi','wangwu']
}
let person1 = createObj(person);
let person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name);
person1.friends.push('zhou');
console.log(person2.friends);
5.寄生式继承
核心:
就是给原型式继承外面套了个壳子。 优点:
- 没有创建自定义类型,因为只是套了个壳返回对象,这个函数顺理成章就成了创建的新对象。 缺点:
- 跟借用构造函数模型一样,每次创建对象都会创建一遍方法。
- 没用到原型,无法复用。
// 寄生式继承
// 在原型式的外面套了个函数
function createObj(o) {
// 这一步可以看做是原型式继承的简写
let clone = Object.create(o);
// 新增属性
clone.sayName = function() {
console.log('hi');
}
return clone; // 返回这个对象
}
// 测试
function child() {}
let child1 = createObj(child)
child1.sayName()
6.寄生组合式继承
核心:
使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型,寄生式组合继承可以算是引用类型继承的最佳模式。
// 寄生组合式继承
// 父类
function SuperType(name) {
this.name = name;
this.colors = ['blue','red'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
// 子类
function.SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
// 封装一个继承函数
function extend(subtype,supertype) {
let prototype = Object(supertype.prototype);
prototype.constructor = subtype;
subtype.prototype = prototype;
}
// 调用
extend(SubType,SuperType)
// 测试
SubType.prototype.sayAge = function() {
console.log(this.age);
}
let person1 = new SubType('zhangsan',22);
person1.sayName();
person1.sayAge();
person1.colors.push('black');
console.log(person1.colors);