ES5实现继承与 ES6实现继承并通过 Babel 编译后的代码对比

263 阅读3分钟

ES5实现继承

熟悉了解 ES5 继承方式的同学可能都已经比较熟悉以下寄生组合式继承的方式(未考虑兼容性):

(function() {
    // 原型式继承。也可以通过 Object.create() 来实现
    function object(prototype) {
        function F() {}
        F.prototype = prototype;
        return new F();
    }
    // 继承原型并修改 constructor 指向
    function inheritProtoType(source, target) {
        const temp = object(target.prototype)
        temp.constructor = source;
        source.prototype = temp;
    }
    // 父类:Car
    function Car(name, type, date) {
        this.name = name;
        this.type = type;
        this.productionDate = date;
    }
    Car.prototype.run = function() {
        console.log(`${this.name} is runing`);
    }
    // 子类:Benz
    function Benz(name, type, date) {
        Car.call(this, name, type, date)
    }
    // 实现继承
    inheritProtoType(Benz, Car);
    Benz.prototype.drive = function() {
        console.log(`${this.name} is by drived`)
    }
    // 子类:Benz
    function BMW(name, type, date) {
        Car.call(this, name, type, date)
    }
    // 实现继承
    inheritProtoType(BMW, Car);
  })()

ES6实现继承

如果使用 ES6 来实现以上的继承,代码大致修改如下:

class Car {
    constructor(name, type, date) {
        this.name = name;
        this.type = type;
        this.productionDate = date;
    }
    run() {
        console.log(`${this.name} is runing`);
    }
}
class Benz extends Car {
    constructor(name, type, date) {
        super(name, type, date);
    }
    drive() {
        console.log(`${this.name} is by drived`)
    }
}
class BMW extends Car {
    constructor(name, type, date) {
        super(name, type, date);
    }
}

这其中有几个关键字:classexpendsconstructorsuper。ES6的写法简洁方便了很多。那如果通过 Babel 编译之后,编译出来的代码跟我们自己实现的 ES5 继承的代码相差多少呢?

Babel 编译过后的 ES6 实现继承的代码

本来想隐藏代码结果的。但是隐藏之后代码无法高亮。ES6 实现继承的方式通过 Babel 编译之后的结果明显比我们自己实现的多了很多代码。我们自己实现的代码50行,编译出来的有130行左右。因为我们自己写的代码未考虑兼容性、健壮性的问题。很多地方的实现值得学习,其中代码注释写了一些自己理解存疑的疑问点。

(function (factory) {
  typeof define === 'function' && define.amd ? define(factory) :
  factory();
}((function () { 'use strict';
  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }
  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
  }
  /**
   * 跟我们实现的继承方式相差无几,只是使用了 Object.create 来实现。
   * 同时还考虑了浏览器兼容性问题
   * @param {*} subClass 
   * @param {*} superClass 
   */
  function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
      throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
      constructor: {
        value: subClass,
        writable: true,
        configurable: true
      }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
  }
  function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }
  function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };
    return _setPrototypeOf(o, p);
  }
  function _isNativeReflectConstruct() {
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;
    try {
      Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
      return true;
    } catch (e) {
      return false;
    }
  }
  function _assertThisInitialized(self) {
    if (self === void 0) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
  }
  function _possibleConstructorReturn(self, call) {
    if (call && (typeof call === "object" || typeof call === "function")) {
      return call;
    }
    return _assertThisInitialized(self);
  }
  function _createSuper(Derived) {
    var hasNativeReflectConstruct = _isNativeReflectConstruct();
    return function _createSuperInternal() {
      var Super = _getPrototypeOf(Derived),
          result;
      if (hasNativeReflectConstruct) {
        var NewTarget = _getPrototypeOf(this).constructor;
        result = Reflect.construct(Super, arguments, NewTarget);
      } else {
        result = Super.apply(this, arguments);
      }
      return _possibleConstructorReturn(this, result);
    };
  }
  var Car = /*#__PURE__*/function () {
    function Car(name, type, date) {
      _classCallCheck(this, Car);
      this.name = name;
      this.type = type;
      this.productionDate = date;
    }
    /*
      ES5 里的写法  Car.prototype.run = function() {}; 
      本质是往原型上添加run方法。那为何不直接使用 Car.prototype.run 呢?
    */
    _createClass(Car, [{
      key: "run",
      value: function run() {
        console.log("".concat(this.name, " is runing"));
      }
    }]);
    return Car;
  }();
  var Benz = /*#__PURE__*/function (_Car) {
    // 实现继承
    _inherits(Benz, _Car);
    var _super = _createSuper(Benz);
    function Benz(name, type, date) {
      _classCallCheck(this, Benz);
      // 调用父类的构造函数,传递参数.为何不直接调用 _Car.call()?
      return _super.call(this, name, type, date);
    }
    _createClass(Benz, [{
      key: "drive",
      value: function drive() {
        console.log("".concat(this.name, " is by drived"));
      }
    }]);
    return Benz;
  }(Car);

  var BMW = /*#__PURE__*/function (_Car2) {
    _inherits(BMW, _Car2);
    var _super2 = _createSuper(BMW);
    function BMW(name, type, date) {
      _classCallCheck(this, BMW);
      return _super2.call(this, name, type, date);
    }
    return BMW;
  }(Car);

  var benz = new Benz('Benz', '4drive', '20201010');
  var bmw = new BMW('BMW', '2drive', '20201111');
  benz.run();
  bmw.run();
  benz.drive();
})));