原型链继承/借用构造函数继承/组合继承/class继承

关于继承有很多方式,这里着重讲一下原型链继承/借用构造函数继承/组合继承以及各自的优缺点

1.原型链继承:子类型的原型对象 = 超类型的实例

function SuperType() {
    this.colors = ["red", "blue", "green"];
}
function SubType() {
}
//继承了 SuperType 
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black" 
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"

问题:

  a.由于包含引用类型值的原型属性会被所有实例共享,而字符串型、数值型则不被共享,所以对 instance1.colors 的修改,instance2.colors 也会被修改;基本类型没有这个问题:

function SuperType(){ 
 this.color = "red";
} 
function SubType(){ 
} 
//继承了 SuperType
SubType.prototype = new SuperType(); 
var instance1 = new SubType(); 
instance1.color = "black"; 
alert(instance1.color); //black
var instance2 = new SubType(); 
alert(instance2.color); //red

  b.在创建子类型的实例时,不能向超类型的构造函数中传递参数

 

2.借用构造函数:通过使用 apply()和 call()方法也可以在(将来)新创建的对象上执行构造函数

function SuperType(){ 
 this.colors = ["red", "blue", "green"]; 
} 
function SubType(){ 
 //继承了 SuperType 
 SuperType.call(this); 
} 
var instance1 = new SubType(); 
instance1.colors.push("black"); 
alert(instance1.colors); //"red,blue,green,black" 
var instance2 = new SubType(); 
alert(instance2.colors); //"red,blue,green"
通过使用 call()方法(或 apply()方法也可以),我们实际上是在(未来将要)新创建的 SubType 实例的环境下调用了 SuperType 构造函数。这样一来,就会在新 SubType 对象上执行 SuperType()函数中定义的所有对象初始化代码。结果,SubType 的每个实例就都会具有自己的 colors 属性的副本了。

问题:如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

 

3.组合继承:指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

function SuperType(name){ 
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
SuperType.prototype.sayName = function(){ 
 alert(this.name);
}; 
function SubType(name, age){ 
 //继承属性
 SuperType.call(this, name); 
 this.age = age; 
} 
//继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){ 
 alert(this.age); 
}; 
var instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
alert(instance1.colors); //"red,blue,green,black" 
instance1.sayName(); //"Nicholas"; 
instance1.sayAge(); //29 
var instance2 = new SubType("Greg", 27); 
alert(instance2.colors); //"red,blue,green" 
instance2.sayName(); //"Greg"; 
instance2.sayAge(); //27
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式

因此,使用继承时优先使用组合继承

 

4. class继承

在 ES6 中,我们可以使用 class 去实现继承,并且实现起来很简单

 1 class Parent {
 2   constructor(value) {
 3     this.val = value
 4   }
 5   getValue() {
 6     console.log(this.val)
 7   }
 8 }
 9 class Child extends Parent {
10   constructor(value) {
11     super(value)
12     this.val = value
13   }
14 }
15 let child = new Child(1)
16 child.getValue() // 1
17 child instanceof Parent // true

class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)

当然了,之前也说了在 JS 中并不存在类,class 的本质就是函数。

posted @ 2020-04-21 16:30  阿伮  阅读(352)  评论(0编辑  收藏  举报