日常好奇-看看ES6的類如何實現的[二]

Xiaowei發表於2018-07-18

在上一篇中我們看了沒有繼承的實現,這一篇我們看下繼承的實現

繼承的實現

還是用這個繼承的例子:

class Animal {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class Dog extends Animal{
  constructor(name) {
    super(name);
    this.name = name;
  }
}
複製程式碼

我們babel一下,得到如下程式碼:

"use strict";

var _createClass = 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);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }

  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });

  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Animal = function () {
  function Animal(name) {
    _classCallCheck(this, Animal);

    this.name = name;
  }

  _createClass(Animal, [{
    key: "getName",
    value: function getName() {
      return this.name;
    }
  }]);

  return Animal;
}();

var Dog = function (_Animal) {

  _inherits(Dog, _Animal);

  function Dog(name) {
    _classCallCheck(this, Dog);

    var _this = _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).call(this, name));

    _this.name = name;
    return _this;
  }

  return Dog;
}(Animal);
複製程式碼

Animal的程式碼與上節非繼承的方式一致,直接跳過,來看下最後一部分Dog的程式碼:

// 這還是一個高階函式,與沒有繼承的物件相比,這裡多出了兩個函式_inherits和_possibleConstructorReturn
var Dog = function (_Animal) {

  // 繼承函式,繼承Animal的屬性
  _inherits(Dog, _Animal);

  function Dog(name) {
    _classCallCheck(this, Dog);

    // 獲取this
    var _this = _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).call(this, name));

    _this.name = name;
    return _this;
  }

  return Dog;
}(Animal);
複製程式碼

在來看_inherits如何實現的:

// 繼承函式
function _inherits(subClass, superClass) {

  // 異常情況處理
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }

  // 將父函式構造器的prototype“拷貝”(使用原型鏈的方式並不是真正的賦值)一份給子函式構造器的prototype
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });

  // 設定子函式構造器的原型為父函式構造器
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
複製程式碼

這裡面涉及到了subClass.__proto__subClass.prototype,那麼__proto__prototype的區別是什麼?

實際上__proto__是真正查詢時所用的物件,而prototype是當你用new關鍵在來構建物件時被用來建造__proto__的,Object.getPrototypeof(dog) === dog.__proto__ === Dog.prototype

函式__possibleConstructorReturn處理了建構函式有返回值的情況。這種情況下,需要改變this使用該返回值作為this

// 建構函式有返回值的情況
function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
複製程式碼

實際模擬

看了上面的實現,我們模擬這個步驟,為了簡化我們省去錯誤處理和特殊情況。

var Animal = function(name) {
  this.name = name;
}

Animal.prototype.getName = function() {
  return this.name;
}

var Dog = function(name) {

  Animal.call(this.name);
  _this.name = name;
}

Dog.prototype = Animal.prototype;
複製程式碼

實現完成後發現,跟我們上一篇文章結尾,猜想實現的一樣,這就很尷尬,本來覺得這種寫法不太順眼,看官方的支援,現在看起來就順眼多了-_-。與完整實現相比我們缺少了一些原型賦值的步驟Dog.__proto__ = Animal,但總體來說原理是一樣的。

相關文章