简单通俗的方式认识js继承

Posted 洛阳醉长安行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单通俗的方式认识js继承相关的知识,希望对你有一定的参考价值。

前戏:

js 继承是在前端面试及js库开发中经常遇到的问题。在面向对象开发时继承知识也是非常重要,本人阅读了一些文章总结了js继承的要点,希望能对大家有所帮助。

什么是继承?

通俗的说:某一个工程需要蓝图实现来工程,蓝图就是。但在一个新工程的蓝图设计时需要复制一些老蓝图上的工程经验来提升效率,这个过程就相当于继承。

js继承有哪些? 一起来看看

1.类式继承

//父类
function SuperClass() {
  this.superValue = true;
}
SuperClass.prototype.getSuperValue = function() {
  return this.superValue;
}
//子类
function SubClass() {
  this.subValue = false;
}
SubClass.prototype = new SuperClass();

SubClass.prototype.getSubValue = function() {
  return this.subValue;
}

var instance = new SubClass();

console.log(instance instanceof SuperClass)//true
console.log(instance instanceof SubClass)//true
console.log(SubClass instanceof SuperClass)//false

这种方式是最原始的方式,使用父类的一个实例重写到子类原型对象上,从而实现父类成员的继承,有点继承的样子,但是总觉得怪怪的,你确定把一个实例赋值到原型上真的没有问题吗?

缺点:

  • 引用类型问题,如果父类有个数组成员,只实例化一次赋值到原型上,那就是每次new出来的子实例都是在使用同一个数组,后果可想而知。
  • 不能传参
  • 重写了prototype,如果要额外给子类原型加东西就要加在SubClass.prototype = new SuperClass();重写语句之后,很不方便
  • instance.constructor 构造器指向父类。子类的实例构造器是父类?那不胡扯吗?

2.构造函数继承

function SuperClass(id) {
  this.books = [\'a\',\'b\'];
  this.id = id;
}
SuperClass.prototype.showBooks = function() {
  console.log(this.books);
}
function SubClass(id) {
  //继承父类
  SuperClass.call(this,id);
}

var instance1 = new SubClass(10);

var instance2 = new SubClass(11);

instance1.books.push(\'c\');
console.log(instance1)
console.log(instance2)
instance1.showBooks();

比上面一个稍微好了一点,也能传参了,每次出来的数组也是唯一的了。原理是在子类构造的时候使用call的特性借用一下父类的构造函数,把父类的成员都设置在子类中来实现继承。额。。那原型呢?
缺点:

  • 继承不了父类原型上的成员
  • 每次实例化子类就会call一下父类,多执行了一遍

3.组合式继承

function SuperClass(name) {
  this.name = name; 
  this.books = [\'A\',\'B\'];
}
SuperClass.prototype.getBooks = function() {
    console.log(this.books);
}
function SubClass(name,time) {
  SuperClass.call(this,name);
  this.time = time;
}
SubClass.prototype = new SuperClass();

SubClass.prototype.getTime = function() {
  console.log(this.time);
}

还可以,能用,原理也就是结合了以上两种(构造函数、类式继承)的继承方式,并修复了严重的缺点。以前应该用这种方法的人也比较多吧
缺点:

  • call一遍就算了又new了一遍 不觉得怪怪的么?call已经设置了一遍成员,prototype只要父的prototype啊,不要把其他无关的东西搞进来。
  • prototype还是被重写了

4.原型式继承

function inheritObject(o) {
    //声明一个过渡对象
  function F() { }
  //过渡对象的原型继承父对象
  F.prototype = o;
  //返回过渡对象的实例,该对象的原型继承了父对象
  return new F();
}
var book = {
    name:\'A book\',
    likeBook:[\'B Book\',\'C book\']
}
var newBook = inheritObject(book);
newBook.name = \'AA1 book\';
newBook.likeBook.push(\'CC book\');
var otherBook = inheritObject(book);
otherBook.name = \'EE book\';
otherBook.likeBook.push(\'FF book\');
console.log(newBook,otherBook);

这就像类式继承一样,直接把父的成员放到子类的原型上,通过原型链来实现继承。inheritObject就像是object.create()和new的模拟实现,产出非常纯净的对象。
黑人问号.jpg。使用这种方法意思是放弃了构造函数来实现继承?
缺点:

  • 子类设置成员写在外面,设置成员的代码一堆一堆的,代码组织差。
  • 每次继承都用同一个likeBook ,并没有新创建一个,后果可想而知,那还不是和第一种方法差不多,用不了
  • 这算的上是一种继承么,先拿到父类的东西然后再去想子类?

5.寄生式继承

var book = {
    name:\'A book\',
    likeBook:[\'B book\',\'C book\']
}
function createBook(obj) {
    //通过原型方式创建新的对象
  var o = new inheritObject(obj);
  // 拓展新对象
  o.getName = function(name) {
    console.log(name)
  }
  // 返回拓展后的新对象
  return o;
}

依然保留了inheritObject函数来实现父成员的继承,相比上面的原型继承代码更有组织性了,单独封装了一个函数来实现继承,然后使用原型继承来继承对应的父类
缺点:
已经差不多做到了继承,但是原型继承问题依然没有解决

6.寄生组合式继承

function inheritObject(o) {

  function F() { }
  F.prototype = o;
  return new F();
}

function inheritPrototype(subClass,superClass) {
    // 复制一份父类的原型副本到变量中
  var p = inheritObject(superClass.prototype);
  // 修正因为重写子类的原型导致子类的constructor属性被修改
  p.constructor = subClass;
  // 设置子类原型
  subClass.prototype = p;
}

//继承
function SuperClass(name) {
  this.name = name;
  this.books=[\'a book\',\'b book\'];
}
SuperClass.prototype.getName = function() {
  console.log(this.name);
}
function SubClass(name,time) {
  SuperClass.call(this,name);
  this.time = time;
}
inheritPrototype(SubClass,SuperClass);
SubClass.prototype.getTime = function() {
  console.log(this.time);
}
var instance1 = new SubClass(\'f1\',\'2017\')
var instance2 = new SubClass(\'r2\',\'2018\');

instance1.books.push(\'new book\');

console.log(instance1,instance2);

继承终极版。这个继承方式是在解决组合继承遇到的问题,先给子类连接上纯净的父原型成员,然后再借用父类构造函数获取到父构造函数里的成员,基本解决上面出现的问题。

总结

剩下没说的就是ES6的继承,也是相当于一个ES6的语法糖,源码也是基于寄生组合式继承。缺点就是对低版本的浏览器不友好, 以上总结了6种js继承方式,每一个继承方式都是修复之前继承的不足,重点是理解它们的特点和之间的差异,弄明白为什么要这样做,这样才能彻底理解js继承的内容。最后,希望能对你有帮助~

以上是关于简单通俗的方式认识js继承的主要内容,如果未能解决你的问题,请参考以下文章

面向对象编程是啥意思

认识Vue.js+Vue.js的优缺点+和与其他前端框架的区别

从头认识js-js中的继承

js的继承方式分别适合哪些应用场景

JS继承方式

一文带你认识 NodeJs