js继承的几种方式

109 阅读3分钟

单纯用来当笔记用,不喜勿喷。也欢迎大家指出有错的地方。

第一种:借用构造函数继承

基本思想:子类的构造函数内部调用了父类型构造函数。说白了就是通过使用apply()和call()方法在新创建的对象上执行父类构造函数。

function Parents() {  this.age = [50, 52];}function Son() {  //继承了Parents    Parents.call(this);}console.log(new Son())

在 * 行通过call方法借调了父类型的构造方法。

同时需要注意的是通过构造方法的继承实际上是在新创建的Son实例的环境下调用了Parent构造方法,这样的好处就是每个Son的实例都有自己的属性的副本。

function Parents() {  this.age = [50, 52];}function Son() {  //继承了Parents  Parents.call(this);}let firstSon = new Son();firstSon.age.push(25);console.log(firstSon.age); // [50, 52, 25]let secondSon = new Son();console.log(secondSon.age); // [50, 52]

劣势

  1. 方法都在构造函数中定义,无法达到函数复用。

  2. 子类无法查看到父类的原型中定义的方法。

    function Parents() {  this.age = [50, 52];}function Son() {  //继承了Parents  Parents.call(this);}let firstSon = new Son();firstSon.age.push(25);console.log(firstSon.age); // [50, 52, 25]Parents.prototype.loveSon = function() {  console.log(true)}let papa = new Parents();papa.loveSon();  // truefirstSon.loveSon(); // Uncaught TypeError: firstSon.loveSon is not a function
    

第二种:原型链继承

**基本思想:**利用原型链让一个引用类型去继承另一个引用类型的属性和方法。

function Parents() {  this.parentsName = '父母的名字'}Parents.prototype.getParentsName = function() {  return this.parentsName}function Son() {  this.sonName = '儿子的名字'}Son.prototype = new Parents();  *Son.prototype.getSonName = function() {  return this.sonName}let firstSon = new Son();console.log(firstSon.getParentsName())

Parents 和 Son 类型分别有自己的属性和方法。通过创建 Parents 的实例,并将该实例赋值给Son.prototype实现原型链继承。 

劣势:

  1. 不能使用对象字面量创建原型方法,因为这样会重写原型链。

    function Parents() {  this.parentsName = '父母的名字'}Parents.prototype.getParentsName = function() {  return this.parentsName}function Son() {  this.sonName = '儿子的名字'}Son.prototype = new Parents();Son.prototype = {  getSonName : function() {    return this.sonName  }}let firstSon = new Son();console.log(firstSon.getParentsName())   // Uncaught TypeError: firstSon.getParentsName is not a function
    
  2. 包含引用类型值的原型属性会被所有的实例所共享。

    function Parents() { this.ages = [50, 52];}function Son() {}Son.prototype = new Parents();let firstSon = new Son();firstSon.ages.push(25);console.log(firstSon.ages); // [50, 52, 25]let secondSon = new Son();console.log(secondSon.ages) // [50, 52, 25]

第三种:组合继承(原型链+构造函数)

**基本思想:**使用原型链实现对原型属性和方法的继承,再通过借用构造函数来实现对实例属性的继承。

function Parents() {  this.ages = [50, 52];  this.parentsName = '父母的名字';}Parents.prototype.sayParentName = function() {  console.log(this.parentsName)}function Son() {  //继承属性  Parents.call(this);   //第一次调用父类Parents()  this.sonName = '儿子的名字';}//继承方法Son.prototype = new Parents();  //第二次调用父类Parents()Son.prototype.sayAge = function() {  console.log(this.ages)}let firstSon = new Son();console.log(firstSon)

组合继承结合了构造函数继承和原型继承的优点。既可以继承父类原型的内容,也不会造成原型里属性的修改。

劣势:

  1. 调用两次父类的构造函数。

第四种:原型式继承

基本思想:借助原型可以基于已有的对象创建新对象。

function createObj(obj) {  function Func() {  }  Func.prototype = obj;  return new Func();}

这种方式也可以通过Object.create() 方法实现。

第五种:寄生式继承

基本思想: 创建一个用于封装继承的函数,在函数内部以某种方式增强这个对象,最后返回这个对象

function createObj(obj) {  //调用函数创建一个新对象  let objClone = Object.create(obj);  //以某种方式增强这个新对象  objClone.sayHello = function() {    console.log('Hello')  }  //返回这个对象  return objClone;}

劣势:

  1. 每次创建对象都要创建一次方法。
  2. 无法复用。

六:寄生组合继承

基本思想:寄生式继承、原型链继承和构造继承组合体

function Parents(name) {  this.name = name;  this.ages = [50, 52];}Parents.prototype.getParentsName = function() {  console.log(this.name)}function Son(name, age) {  //继承属性  Parents.call(this, name);  this.age = age;}//寄生式继承let Func = function() {};Func.prototype = Parents.prototype;//原型链继承Son.prototype = new Func();let child = new Son('zcw', 25);console.log(child)