继承的3种实现方式

关于继承,其实写js将近一年,平时其实是几乎不怎么使用继承的,但是最近面试的时候遇到了继承的问题,所以查找了一些资料之后做一个总结。主要总结的点有:

  1. 原型链继承
  2. 组合继承
  3. 寄生组合继承

原型链继承

假设父类Person,子类Ninja。原型链继承的方式如下:

function Person(){
  this.info = {
    name:'xiaobo',
    age:23
  };
}
Person.prototype.sayName = function(){
  console.log(this.info.name);
}
Person.prototype.sayAge = function(){
  console.log(this.info.age);
}
function Ninja(age){
  this.info.age = age || 23;
}
//子类的原型直接等于父类的一个实例
Ninja.prototype = new Person();
var ninja1 = new Ninja(20);
var ninja2 = new Ninja(24);
ninja1.sayAge();//24
ninja2.sayAge();//24

这种方式的根本原理其实就是子类构造函数的原型等于父类的一个实例,这样很简单,但是也会带来一个问题:父类的非原型属性或者非原型方法会被所有子类的实例共享,从而导致引用类型的变量会被所有的子类实例共享,这个特性并不是继承的初衷。为了解决这个问题,所以有了组合继承。

组合继承

原型继承导致父类的非原型属性被所有实例共享从而导致的引用属性一个修改引起所有子类实例修改的情况,组合继承可以很好的解决这个问题。

function Person(){
  this.info = {
    name:'xiaobo',
    age:23
  };
}
Person.prototype.sayName = function(){
  console.log(this.info.name);
}
Person.prototype.sayAge = function(){
  console.log(this.info.age);
}
function Ninja(age){
//在构造函数中调用父类构造方法从而给每个子类实例添加一个属性,从而子类实例之间不会相互影响
  Person.call(this);
  this.info.age = age || 23;
}
Ninja.prototype = new Person();
var ninja1 = new Ninja(20);
var ninja2 = new Ninja(24);
var ninja3 = new Ninja();
ninja1.sayAge(); //20
ninja2.sayAge(); // 24
ninja3.sayAge(); //23
console.log(ninja1.__proto__.info.age); //23
console.log(ninja2.__proto__.info.age); //23
console.log(ninja3.__proto__.info.age); //23

但是组合继承又引入了一个问题,两次调用父类的构造函数,会造成一些冗余,在每个子类实例的原型上,其实也是存储了一个指向父类非原型属性的一个引用,数据有冗余,而寄生组合可以很好的解决这个问题。

寄生组合继承

function Person(){
  this.info = {
    name:'xiaobo',
    age:23
  };
}
Person.prototype.sayName = function(){
  console.log(this.info.name);
}
Person.prototype.sayAge = function(){
  console.log(this.info.age);
}
function Ninja(age){
  //调用父类构造函数
  Person.call(this);
  this.info.age = age || 23;
}
//寄生继承
Ninja.prototype = Object.create(Person.prototype);
Ninja.prototype.constructor = Ninja;

//此处添加Ninja的新方法
// ----- 
var ninja1 = new Ninja(20);
var ninja2 = new Ninja(24);
var ninja3 = new Ninja();
ninja1.sayAge();//20
ninja2.sayAge();//24
ninja3.sayAge();//23
console.log(ninja1.__proto__.info);//undefined
console.log(ninja2.__proto__.info);//undefined
console.log(ninja3.__proto__.info);//undefined
console.dir(Ninja.prototype);

这种继承方式,是公认的最理想的继承方式,能够很好的弥补之前的不足。在这里再简单介绍一下Object.create,内部发生的情况大概是这样:

Object.Prototype.create = Object.prototype.create || function(obj){
    function F(){};
    F.prototype = obj;
    return new F();
}

小结

个人认为,上述三种继承方式,原型链方式最为简单,但是存在问题,而组合继承方式弥补了不足,但是又带来了数据的冗余,寄生组合式能很好的解决所有的问题,是公认的最佳继承方式。

参考资料

  1. js高程程序设计

推荐阅读更多精彩内容