Javascript继承

105 阅读2分钟

借助构造函数实现继承

核心代码是Animal的构造函数在Dog的构造函数中执行(call)。

  function Animal() {
    this.name = 'cat';
    this.msg = {
      age: 9
    }
  }
  Animal.prototype.walk = function () {
    console.log(this.name +" walk");
  }
  function Dog() {
    Animal.apply(this);      //核心代码
    this.type = 'hashiqi';
  }
  let dog1 = new Dog();
  let dog2 = new Dog();
  dog1.msg.age = 12;
  console.log(dog1, dog2);
  1. dog1 和 dog2 已经有了 name 和 msg 属性;并且 msg 属性并不会相互影响;
  2. 缺点是:Dog这个类并没有继承Animal原型对象prototype上定义的方法!

借助原型继承

核心代码是Dog的原型直接指向new Animal对象,这样Dog的类会借助原型对象继承Animal的属性和方法。

    function Animal() {
        this.name = 'cat';
        this.msg = {
          age: 9
        }
    }
    Animal.prototype.greet = function () {
        console.log('hello')
    };
    function Dog() {
        this.name = 'dog';
    }

    Dog.prototype = new Animal();  //核心代码

    const dog1 = new Dog();
    dog1.msg.age = '99';

    const dog2 = new Dog();
    dog2.msg.age = '199';
    console.log(dog1, dog2);
  1. dog1和dog2借助原型对象已经继承了Animal的属性和方法;
  2. 缺点也比较明显了,因为dog1和dog2的原型对象指向的是同一个原型对象,这就导致了它们继承的属性如果是引用类型(masg)的话,会相互影响。

借助构造函数和原型链结合

核心思想是结合前面两种方法:Animal的构造函数在Dog中执行(call); Dog的原型对象执行一个Animal对象,借助原型链继承Animal的方法。

  function Animal() {
    this.name = 'cat';
    this.msg = {
      age: 9
    }
  }
  Animal.prototype.greet = function () {
    console.log('hello')
  };

  function Dog() {
    Animal.call(this);   //核心代码
    this.name = 'dog';
  }

  Dog.prototype = new Animal();   //核心代码

  const dog1 = new Dog();
  dog1.msg.age = '99';

  const dog2 = new Dog();
  dog2.msg.age = '199';
  console.log(dog1, dog2);
  1. dog1和dog2借助构造函数继承了Animal的属性;借助原型对象继承了Animal的属性和方法;同时dog1和dog2属性也保持了独立性,不会相互影响;
  2. 缺点是Animal的构造方法执行了2遍:在Dog构造函数中执行了一遍,在原型对象上执行了一遍。

组合优化方式

借助Object.create()方法,将Dog的原型对象的__proto__指向Animal的原型对象。即将Dog的原型对象也看成一个对象,将这个对象的原型指向 Animal;同时修改Dog的原型对象的constructor属性指向Dog函数。

  function Animal() {
    this.name = 'cat';
    this.msg = {
      age: 23
    }
  }
  Animal.prototype.greet = function () {
    console.log('hello')
  };

  function Dog() {
    Animal.call(this);
    this.name = 'dog';
  }

  Dog.prototype = Object.create(Animal.prototype);
  Dog.prototype.constructor = Dog;
  let dog = new Dog();
  console.log(dog);

对于Object.create()方法的理解,创建对象的如下两种方法的区别:

  {
    // new Object() 方式创建
    let a = {  rep : 'apple' }
    let b = new Object(a)
    console.log(b) // {rep: "apple"}
  }
  {
    // Object.create() 方式创建
    let a = { rep: 'apple' }
    let b = Object.create(a)
    console.log(b)  // {}
  }
  • new Object(a)得到的b是普通对象,跟对象a一样
  • Object.create(a)得到的b本身是空对象{}, 它的原型对象会指向a

实现多继承

    function SuperClass() {
        this.type = 'super';
    }
    SuperClass.prototype.sayType=function () {
        console.log('type');
    }
    function OtherSuperClass() {
        this.props = 'other';
    }
    OtherSuperClass.prototype.sayProps=function () {
      console.log('other');
    }
    function MyClass() {
        SuperClass.call(this);
        OtherSuperClass.call(this);
    }
	// 继承一个类
    MyClass.prototype = Object.create(OtherSuperClass.prototype);
    // 混合其它
    Object.assign(MyClass.prototype, SuperClass.prototype);
    // 重新指定constructor
    MyClass.prototype.constructor = MyClass;

    let m = new MyClass();
    m.sayProps();
    m.sayType();

    let o = new OtherSuperClass();
    console.log(o);

Object.assign 会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。Object.assign 是在 ES2015 引入的,且可用 polyfilled。要支持旧浏览器的话,可用使用 jQuery.extend() 或者 _.assign()。

developer.mozilla.org/zh-CN/docs/…