hookex
4/20/2020 - 6:45 AM

JS继承的6种写法

JS继承的6种写法

{
  /**
   * 1. 原型链继承
   */
  function SuperType() {
    this.property = true;
  }

  SuperType.prototype.getSuperValue = function() {
    return this.property;
  };

  function SubType() {
    this.subproperty = false;
  }

  SubType.prototype = new SuperType();

  SubType.prototype.getSubValue = function() {
    return this.subproperty;
  };

  var instance = new SubType();

  console.log(instance.getSuperValue());

  /**
   * 关键问题
   * 1.引用类型值的原型属性会被所有实例共享
   * 2. 不能向超类型的构造函数中传递参数
   */
}

{
  /**
   * 2. 借用构造数据
   */

  function SuperType(name) {
    this.name = name;
  }

  function SubType() {
    // 继承了SuperType, 同时传递了参数
    SuperType.call(this, "hooke");

    this.age = 29;
  }

  const instance = new SubType();

  /**
   * 关键问题:
   * 没有函数复用
   */
}

{
  /**
   * 3. 组合继承
   */

  function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
  }

  SubType.prototype.sayName = function() {
    console.log(this.name);
  };

  function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name);
    this.age = age;
  }

  // 继承方法
  SubType.prototype = new SubType();

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

  // 原来Nicholas写这本书的时候,跟我一样29
  const instance = new SubType("Nicholas", 29);
  console.log(instance.sayAge());

  /**
   * 融合了原型链和借用构造函数,
   * 避免了二者的缺陷,融合了他们的优势,
   * 是JS中最常用的继承模式
   * 能够使用instanceof和isPrototypeOf()识别对象
   *
   * 关键问题:
   * 任何情况下,都会调用两次超类型构造函数
   * 1. 创建子类型原型的时候
   * 2. 子类型构造函数内部
   */
}

{
  /**
   * 4. 原型式继承
   */

  function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

  // ES5引入了Object.create()来规范化了原型式继承

  const person = {
    name: "hooke",
    friends: ["zhao", "qian", "sun"]
  };

  const anotherPerson = Object.create(person, {
    name: {
      value: "huxiaohui"
    }
  });

  /**
   * 优点:
   * 让一个对象和另一个对象保持相似的简单实现,
   * 省去了创建构造函数的麻烦
   *
   * 关键问题:
   * 包含引用类型值的树形始终会钟祥相应的值,就像使用原型模式一样
   */
}

{
  /**
   * 5. 寄生式继承
   */

  function createAnother(original) {
    // 调用一个函数克隆一个新对象
    const clone = object(original);

    clone.sayHi = function() {
      console.log("hi");
    };
    return clone;
  }

  const person = {
    name: "hooke",
    friends: ["zhao", "qian", "sun"]
  };

  // 新对象不仅有person的所有属性和方法,还有自己的sayHi方法
  const anotherPerson = createAnother(person);
  console.log(anotherPerson.sayHi());

  /**
   * 关键问题:
   * 没有函数复用
   */
}

{
  /**
   * 6. 寄生组合式继承(实现基于类型继承的最有效方式)
   * 通过借用构造函数来继承属性。
   * 通过原型链的混成形式来继承方法
   */

  function inheritPrototype(subType, superType) {
    const prototype = object(superType.prototype); // 创建对象
    // 弥补因重写原型而失去的默认的constructor属性
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 指定对象
  }

  function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
  }

  SuperType.prototype.sayName = function() {
    console.log(this.name);
  };

  function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
  }

  inheritPrototype(SubType, SuperType);

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

  /**
   * 仅调用了一次SuperType构造函数
   * 避免了在SubType.prototype上面创建不必要的、多余的树形
   * 原型链能够保持不变
   * 能够使用instanceOf、isPrototypeOf()
   */
}

{
  /**
   * TS的继承模式: 寄生组合
   */

  /**
   * 编译前
   */

  {
    class SuperType {
      constructor(public name) {}
      sayName() {
        console.log(this.name);
      }
    }

    class SubType extends SuperType {
      constructor(public name, public age) {
        super(name);
      }

      sayName() {
        console.log("subName", this.name);
      }
    }
  }

  /**
   * 编译后
   */

  {
    var __extends =
      (this && this.__extends) ||
      (function() {
        var extendStatics = function(d, b) {
          extendStatics =
            Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array &&
              function(d, b) {
                d.__proto__ = b;
              }) ||
            function(d, b) {
              for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
            };
          return extendStatics(d, b);
        };
        return function(d, b) {
          extendStatics(d, b);
          function __() {
            this.constructor = d;
          }
          d.prototype =
            b === null
              ? Object.create(b)
              : ((__.prototype = b.prototype), new __());
        };
      })();
    var SuperType = /** @class */ (function() {
      function SuperType(name) {
        this.name = name;
      }
      SuperType.prototype.sayName = function() {
        console.log(this.name);
      };
      return SuperType;
    })();
    var SubType = /** @class */ (function(_super) {
      __extends(SubType, _super);
      function SubType(name, age) {
        var _this = _super.call(this, name) || this;
        _this.name = name;
        _this.age = age;
        return _this;
      }
      SubType.prototype.sayName = function() {
        console.log("subName", this.name);
      };
      return SubType;
    })(SuperType);
  }
}