继承的3种实现方式

作者: small_a | 来源:发表于2017-03-18 20:56 被阅读98次

关于继承,其实写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高程程序设计

相关文章

  • js中的继承

    继承 对象冒充的方式实现继承 弊端:只能继承构造函数里面的属性/方法。原型链上的无法继承 原型链的方式实现继承 弊...

  • 原型相关(二)

    1.继承 继承方式:接口继承(只继承方法签名)实现继承(继承实际的方法)ECMAScript只支持实现继承,并且主...

  • js中的继承-原型与原型链

    面向对象的语言支持两种继承方式,接口继承和实现继承js无法实现接口继承,只支持实现继承,主要通过原型链来实现。具体...

  • 005|JavaScript ES6新特性之Classes

    在过去,需要像 053|JavaScript 继承详解 那样实现继承。JavaScript这种继承实现方式与其它面...

  • 记录JAVA多线程学习

    1.实现方式:多线程的实现方式有两种方式,一种是继承Thread类,一种是实现runnable接口。继承threa...

  • 面向对象——继承

    许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。ECMAS...

  • UML图笔记

    一、泛化: 泛化 C++实现方式:类继承 二、实现 C++实现方式:类继承,且实现了父类的接口。 三、关联 单向关...

  • 7、面向对象的程序设计3(《JS高级》笔记)

    三、继承 许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际方法。由...

  • Javascript基础系列之继承

    继承 许多 OO 语言都支持两种继承方式:接口继承和实现继承。但是ECMAScript中只支持实现继承,下面就是实...

  • 高程三 关于原型链 阅读笔记

    许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于函数没...

网友评论

  • small_a:测试xss,<img src="" onerror="alert(111)">

本文标题:继承的3种实现方式

本文链接:https://www.haomeiwen.com/subject/gfzunttx.html