摘要:函数继承是在JS里比拟根底也是比拟重要的一部分,而且也是面试中经常要问到的。上面带你疾速理解JS中有哪几种是经常出现且必须把握的继承形式。把握上面的内容面试也差不多没问题啦~

本文分享自华为云社区《人类高质量JS函数继承》,作者:北极光之夜。

一. 前言:

函数继承是在JS里比拟根底也是比拟重要的一部分,而且也是面试中经常要问到的。上面带你疾速理解JS中有哪几种是经常出现且必须把握的继承形式。把握上面的内容面试也差不多没问题啦~

二.原型链继承:

原型链继承的要点在于父类的实例作为子类的原型。间接看上面这个例子:

 // 父函数 Person  function Person(name, age) {    // 定义一些属性    this.name = name;    this.age = age;    this.nature = ["auroras", "wind", "moon"];  } // 定义Person原型上的一个办法  Person.prototype.sayLove = function () {    console.log(this.name + " like " + this.nature[0]);  };   // 子函数 Jack  function Jack() {}  // 父类的实例作为子类的原型 (-------------------实现外围--------------------------)  Jack.prototype = new Person();

当初咱们创立两个Jack 的实例,测试看是否实现了继承Person:

  var jack1 = new Jack();  var jack2 = new Jack();  jack2.nature[0] = "sea";  jack1.sayLove();  jack2.sayLove();     console.log(jack1.nature);  console.log(jack2.nature);  

看运行后果的确继承了,能执行sayLove办法。但有甚多毛病,创立Jack实例的时候传递不了参数name和age,而且不同实例间nature援用类型属性相互影响,一个扭转那都扭转:

三.借用构造函数继承(对象假装):

外围在于“盗用构造函数”(constructor stealing)。在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简略对象,所以能够应用apply()和call()办法以新创建的对象为上下文执行构造函数。它能解决原型链继承中传参数和援用类型属性抵触。还是间接看例子:

// 父函数 Person function Person(name, age) {   // 定义一些属性   this.name = name;   this.age = age;   this.nature = ["auroras", "wind", "moon"]; }// 定义Person原型上的一个办法 Person.prototype.sayLove = function () {   console.log(this.name + " like " + this.nature[0]); }; // 子函数 Lucy function Lucy(name, age) { // 通过call把this指向Lucy,相当于拷贝了一份父函数 Person 里的内容(---------实现外围--------------)       Person.call(this, name, age);     } //给子函数原型上也定义一个办法  Lucy.prototype.syaName = function () {       console.log("My name is " + this.name);     };

当初咱们创立两个Lucy 的实例,测试看是否实现了继承Person:

  var lucy1 = new Lucy("lucy1", "20");  var lucy2 = new Lucy("lucy2", "22");  lucy2.nature[0] = "sea";  console.log(lucy1.name);  console.log(lucy1.nature);  console.log(lucy2.nature);  lucy1.syaName();  lucy2.syaName();  lucy1.sayLove();

后果看能够继承了,能传参数,援用类型属性也不相互影响,然而毛病不言而喻,能够看到报错,无奈应用父类的原型上的办法sayLove。

四.组合继承:

组合继承就是联合了原型链继承和借用构造函数继承两者的外围实现的一种继承办法,既能传递参数,援用类型属性也互不影响,同时子类也能获取失去父类的办法。这也是目前比拟罕用的继承形式。间接看例子:

 // 父函数 Person  function Person(name, age) {    // 定义一些属性    this.name = name;    this.age = age;    this.nature = ["auroras", "wind", "moon"];  } // 定义Person原型上的一个办法  Person.prototype.sayLove = function () {    console.log(this.name + " like " + this.nature[0]);  };   // 子函数Lisa      function Lisa(name, age) {// 通过call把this指向Lisa,相当于拷贝了一份父函数 Person 里的内容(------实现外围-----------)              Person.call(this, name, age);      }// 父类的实例作为子类的原型 (--------------实现外围-------------------)            Lisa.prototype = new Person();  //小知识点,这里是让Lisa的constructor从新指向Lisa,不然因为Lisa的原型为Person实例,constructor会指向Person      Lisa.prototype.constructor = Lisa;

当初咱们创立两个Lisa 的实例,测试看是否实现了继承Person:

  var lisa1 = new Lisa("lisa1", "20");  var lisa2 = new Lisa("lisa2", "21");  lisa2.nature[0] = "sea";  console.log(lisa1.name);  console.log(lisa1.nature);  console.log(lisa2.nature);  lisa1.sayLove();  lisa2.sayLove();

能够看到基本上实现了咱们继承的性能。也修补了原型链和借用构造函数继承的毛病。然而呢,它还是有一个小毛病,就是能够看到在代码正文实现外围那,两次都调用了Person,那么Lisa原型上和实例上有了两份雷同的属性,那就会多少有一些性能节约。

五.寄生组合继承:

其实寄生组合继承和组合继承差不多的,就是多了一个解决组合继承上原型和实例产生两份雷同属性的毛病。解决外围是咱们既然只是想要子类原型赋值为父类原型,那没必要new一个父类实例。间接发明一个新对象,它值为父类的原型,再将它赋值给子类原型就行了。

其中用到Object.create(proto,[propertiesObject])这个办法创立一个新对象。相当于新对象的__proto__为其参数proto。当然Object.create可能低版本ie没有,所以上面也自定义封装了Object.create办法,当然只是简略封装。间接看例子:

 // 父函数 Person  function Person(name, age) {    // 定义一些属性    this.name = name;    this.age = age;    this.nature = ["auroras", "wind", "moon"];  } // 定义Person原型上的一个办法  Person.prototype.sayLove = function () {    console.log(this.name + " like " + this.nature[0]);  };   // 子函数 Andy function Andy(name, age) {        Person.call(this, name, age);      }// 如果没有 Object.create()办法,简略封装下      if (!Object.create) {        Object.create = function (proto) {          function Temp() {}          Temp.prototype = proto;          return new Temp();        };      }// 调用Object.create办法,新建一对像,其__proto__为Person.prototype,并赋值给 Andy.prototype (-------实现外围----------)      Andy.prototype = Object.create(Person.prototype);  //批改constructor指向      Andy.prototype.constructor = Andy;

当初咱们创立两个Andy的实例,测试看是否实现了继承Person:

  console.log(Andy.prototype.__proto__ === Person.prototype);  var andy1 = new Andy("andy1", "20");  var andy2 = new Andy("andy2", "21");  andy2.nature[0] = "sea";  console.log(andy1.name);  console.log(andy1.nature);  console.log(andy2.nature);  andy1.sayLove();  andy2.sayLove();

完满运行:

六.class继承:

ES6出了class语法糖之后,就能够通过class定义类并实现类的继承。间接看例子:

//定义一个父类 Animal  class Animal {   //这里constructor指向类自身,跟es5行为一样的    constructor(name) {      this.name = name;    }    likeEat() {      console.log(this.name + " like eat " + this.food);    }  }//定义一个子类 Dog ,通过 extends 继承父类Animal  class Dog extends Animal {       constructor(name, food) {      //通过super(属性名)继承父类属性     super(name);     this.food = food;    }    likeEat() {     //通过super.+父类办法 实现继承父类办法      super.likeEat();    }  }

new一个Dog实例,测试看看,Dog是否继承了Animal:

var jinmao = new Dog("jinmao", "bone");console.log(jinmao.name);jinmao.likeEat();

能够看到完满实现了:

七.总结:

下面就是这篇文章的全部内容啦,如果有谬误的中央恳请指出~

点击关注,第一工夫理解华为云陈腐技术~