javascript面向对象编程学习(三) —— 构造函数继承和组合继承

468 阅读3分钟

导读

javascript面向对象编程学习(一)
javascript面向对象编程学习(二) —— 原型链继承

一、借用构造函数继承

1、 如何实现借用构造函数继承?

利用call()apply()方法,在子构造函数Child()中,调用Person.call(this, name),增强子构造函数实例;实质等同于复制父实例给子函数。

1.1代码实现

 function Person(name) {
     this.name = name;
     this.names = ['大魔王','魔鬼', '恶魔'];
     this.getName = function () {
         console.log(this.name)
     }
   }
  Person.prototype.say = function (){
     console.log('my name is ' + this.name);
   }
   
   function Child(name, age){
     // 继承Person
     Person.call(this, name);
     this.age = age;
   }
   
   var child = new Child('小魔王', 22);
   var child1  = new Child('大魔王', 25);
   console.log(child.names);   // ["大魔王", "魔鬼", "恶魔"]
   console.log(child1.names);  // ["大魔王", "魔鬼", "恶魔"]
   
   child.names.push('小魔王'); 
   
   console.log(child.names);   // ["大魔王", "魔鬼", "恶魔", "小魔王"]
   console.log(child1.names);  // ["大魔王", "魔鬼", "恶魔"]
   
   console.log(child.name);    // 小魔王
   console.log(child.age);     // 22

2、为什么可以用call()apply()方法来实现继承?

首先,我们要了解一下call的定义:调用一个对象的一个方法,以另一个对象替换当前对象。
也就是说,当执行Person.call(this, name)的时候,实质就是Personthis指向了Child
因此,实例child也有了Person的属性和方法了,也就实现了继承!

3、关系图如下:

借用构造函数继承

4、缺点:

  1. 只能继承父类的实例属性和方法,不能继承原型属性/方法;
    Personthis指向了Child, 也就是说,Child只是复制了Person的属性和方法,原型链并没有和它产生关系;child实例的__proto__还是指向Child。如下:
    构造函数继承2
  2. 无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿。

二、 组合继承

1、什么是组合继承?

原型链继承构造函数继承优点组合起来。使用构造函数继承实现对实例上的属性和和方法继承,使用原型链继承实现对原型上的属性和方法的继承。这样就能使在原型上定义的方法能够实现函数复用,并且没个实例也有自己的属性。

2、代码实现

 function Person(name) {
     this.name = name;
	 this.names = ['大魔王','魔鬼', '恶魔'];
   }
   
   Person.prototype.sayName = function (){
     console.log('my name is:' + this.name);
   }
   
   function Child(name, age){
     // 继承Person实例属性
     Person.call(this,name);
	 this.age = age;
   }
   
   // 继承原型的属性和方法
   Child.prototype = new Person();
   Child.prototype.constructor = Child; // 因为原型链继承,会把constructor指向改变,所以要重新指回自身
   Child.prototype.sayAge = function (){
     console.log('my age is:' + this.age);
   }
   
   
   const child = new Child('小魔王', 22);
   child.names.push('人');
   child.sayName();             // my name is:小魔王
   child.sayAge();              // my age is:22
   console.log(child.names);    // ["大魔王", "魔鬼", "恶魔", "人"]
   
   const child2 = new Child('人', 28);
   child2.sayName();            // my name is:人
   child2.sayAge();             // my age is:28
   console.log(child2.names);   // ["大魔王", "魔鬼", "恶魔"]

3、关系图如下:

组合继承

4、缺点

父类实例的属性和方法既存在于子类的实例中也存在于原型中。如下图:

组合继承2

三、 总结

想要弄清楚构造函数继承,要先弄明白call()apply()方法的定义和使用,要清楚this指向。
组合继承,当你弄清楚原型链继承和构造函数继承,然后再把两者结合去思考,就自然而然懂了。
学习的最好的办法还是实践;就譬如我这样动起手来,画图和敲代码等等,比只看而不去动手,要了解得更加透彻!
你的赞是对我最大的支持,也是我最大的动力!