JavaScript常用六种继承方案

55 阅读3分钟

继承,就是A的属性方法可以继承给B,B可以继续往下传。同时我们还希望

  • B能够新增自己的属性和方法
  • B能够修改继承来的属性和方法,同时不影响A也不干扰同代

JavaScript想实现继承的目的:重复利用另外一个对象的属性和方法

1.原型链的继承

构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针

function parent() {
      this.show = true;
      this.info = {
        name: '张三',
        age: 18
      }
    }
    parent.prototype.getInfo = function () {
      console.log(this.info);
      console.log(this.show);
    }
    function child() { }
    child.prototype = new parent();
    let child1 = new child();
    child1.info.gender = '男'
    child1.getInfo()  //{name:'张三',age:18,gender:'男'} true

原型链方案存在的缺点:多个实例对引用类型的操作会被篡改

2.借用构造函数继承
function Person(name, age) {
      this.name = '张三',
        this.age = 18
    }
    function fn(name, age) {
      Person.call(this, name, age);
      this.job = '赚钱'
    }
    let obj = new fn()
    console.log(obj.name,obj.age,obj.job);//张三 18 赚钱

fn()中的实例都会将Person中的属性复制一份

缺点:

  • 只能继承父类的属性和方法,不能继承原型属性/方法
  • 无法实现复用,每个子类都有父类实例函数的副本,影响性能
3.原型式继承
    let Person = {
      color: ['pink', 'black', 'red']
    }
    let anotherColor = Object(Person);
    anotherColor.color.push('white')
    console.log(Person.color);  //['pink','black','red','white']

缺点:

  • 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能
  • 无法传递参数
4.ES6类继承extends

extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。

    class Father{
      constructor(){

      }
      money(){
        console.log(111);
      }
    }
    class Son extends Father{

    }
    let son = new Son();
    son.money();   //111
5.寄生式继承

核心:在原型式继承的基础上,增强对象,返回构造函数

function newFn(original) {
      let newObj = Object(original);  //通过调用object()函数创建一个新对象
      newObj.say = function () { // 以某种方式开增强对象
        console.log('111');
      }
      return newObj;  //返回这个对象
    }
    let Person = {
      name: 'aaa',
      friends: ['aa', 'bb', 'cc']
    }
    let anotherFn = newFn(Person)
    anotherFn.say()

函数的主要作用是为构造函数新增属性和方法,以增强函数

缺点:

  • 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能
  • 无法传递参数
6.组合继承

组合继承综合了原型链继承借用构造函数继承,将两者有点结合了起来 基本的思路就是使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性,这样既可以把方法定义在原型上以实现使用,又可以让每个实例都有自己的属性

function Parent(name) {
      this.name = name
      this.colors = ["red", "blue", "yellow"]
    }
    Parent.prototype.sayName = function () {
      console.log(this.name);
    }

    function Child(name, age) {
      // 继承父类属性
      Parent.call(this, name)
      this.age = age;
    }
    // 继承父类方法
    Child.prototype = new Parent();

    Child.prototype.sayAge = function () {
      console.log(this.age);
    }

    let child1 = new Child("zhangsan", 19);
    child1.colors.push("pink");
    console.log(child1.colors); // ["red", "blue", "yellow", "pink"]
    child1.sayAge(); // 19
    child1.sayName(); // "zhangsan"

上面例子中,parent构造函数定义了name,colors两个属性,接着又在他的原型上添加了个sayName()方法。child构造函数内部调用了parent构造函数,同时传入了name参数,同时child.prototype也被赋值为parent实例,然后又在他的原型上添加了个sayAge()方法,这样就可以创建child1实例,让这个实例有自己的属性,包括colors,同时还共享了父类的sayName方法

优点

  1. 父类的方法可以复用
  2. 可以在child构造函数中向parent构造函数中传参
  3. 父类构造函数中的引用属性不会被共享

image.png 喜欢点个赞叭

20952EE5.jpg