JavaScript的几种继承方式

吐槽君 分类:javascript

继承是JavaScript中比较重要的一点,其中包括构造函数继承、原型链继承、组合继承、寄生继承、组合寄生继承。

构造函数继承

特点:顾名思义,只对父类的构造函数进行继承,不是传统意义上的继承。
实现方式:子类通过call的方式去调用父类构造函数,然后把this对象传进去,执行父类构造函数之后,子类就拥有了父类定义的属性和方法。

function Flower(){
  this.colors = ['红色','黄色'];
  this.print = function(){
    console.log(this.colors)
  }
}
function Violate(){
  Flower.call(this)
}

var child1 = new Violate()
var child2 = new Violate()

console.log(child1.print()) // ["红色", "黄色"]
console.log(child2.print()) // ["红色", "黄色"]

child1.colors.push('紫色')

console.log(child1.print()) // ["红色", "黄色", "紫色"]
console.log(child2.print()) // ["红色", "黄色"]
//改变的只有child1的属性值
 

优点:所有的基本属性独立,不会被其他实例所影响。
缺点:所有希望共享的方法和属性也独立了,没有办法通过修改父类某一处来达到所有子实例同时更新的效果。同时,每次创建子类都会调用父类构造函数一次,所以每个子实例都拷贝了一份父类函数的内容,如果父类很大的话会影响性能。

原型链继承

特点:使用原型链的特点进行继承,也不是真正的继承。
实现方式:原型链的特点是如果一个对象某个属性找不到,会沿着它的原型往上去寻找,直到原型链的最后才会停止寻找。我们把父函数的实例赋值给子函数的原型,此时由于赋值,子函数的构造函数也发生了改变需要指向回自生的构造函数。

function Flower(){
  this.colors = ['红色','黄色'];
  this.print = function(){
    console.log(this.colors)
  }
}
function Violate(){
}

Violate.prototype = new Flower()
Violate.prototype.constructor = Violate

var child1 = new Violate()
var child2 = new Violate()

console.log(child1.print()) // ["红色", "黄色"]
console.log(child2.print()) // ["红色", "黄色"]

child1.colors.push('紫色')

console.log(child1.print()) // ["红色", "黄色", "紫色"]
console.log(child2.print()) // ["红色", "黄色", "紫色"]
//child的值同步发生了变化
 

优点:很好的实现了方法的共享。
缺点:正是因为什么都共享了,所以导致一切的属性都是共享的,只要某一个实例进行修改,那么所有的属性都会变化。

组合继承

特点:融合了构造函数继承和原型链继承,发挥两者的优势之处,因此,它算是真正意义上的继承方式。
实现方式:在子类中,使用了call来调用父类构造函数,同时又将父函数的实例赋值给子函数的原型。

function Flower(color){
  this.color = color;
}
Flower.prototype.print = function() {
    console.log(this.color);
}
function Violate(color){
  Flower.call(this,color)
}

Violate.prototype = new Flower()
Violate.prototype.constructor = Violate

var child1 = new Violate('紫色')
console.log(child1.print()) //紫色
var child1 = new Violate('蓝色')
console.log(child1.print()) //蓝色
 

优点:很好的融合了构造函数继承和原型链继承,发挥两者的优势之处,因此,它算是真正意义上的继承方式。
缺点:每次我们实例化子类的时候,仍然都需要调用一次父类构造函数,如果父类构造函数较复杂则会很消耗性能。

寄生式继承

针对组合继承,实际上我们并不需要重新执行父类函数,我们只是想要继承父类的原型。

var obj = {
    color: 'red',
    print: function() {
        console.log(this.color);
    }
};

var child1 = Object.create(obj);
son1.print(); // red

var child2 = Object.create(obj);
son2.print(); // red
 

寄生式继承本质上还是原型链继承,Object.create(obj);方法意思是以obj为原型构造对象,所以寄生式继承不需要构造函数,但是同样有着原型链继承的优缺点,也就是它把所有的属性和方法都共享了。

寄生组合式继承

寄生组合式继承就是寄生继承与组合继承的结合,是目前继承中最完美的解决方案,也是es6的class语法实现原理。

function Flower(color){
  this.color = color;
}
Flower.prototype.print = function() {
    console.log(this.color);
}
function Violate(color){
  Flower.call(this,color)
}

Violate.prototype = Object.create(Flower.prototype)
Violate.prototype.constructor = Violate

var child1 = new Violate('紫色')
console.log(child1.print()) //紫色
var child1 = new Violate('蓝色')
console.log(child1.print()) //蓝色
 

这段代码不同之处只有一个,就是把原来的Violate.prototype = new Flower();修改为了Violate.prototype = Object.create(Flower.prototype);

我们前面讲过,Object.create方法是以传入的对象为原型,创建一个新对象;创建了这个新对象之后,又赋值给了Violate.prototype,因此Violate的原型最终指向的其实就是父类的原型对象,和new Flower()是一样的效果,但是相较于组合继承,优化了一些性能。

回复

我来回复
  • 暂无回复内容