構(gòu)造函數(shù)與class實(shí)現(xiàn)類的區(qū)別,以及ES5實(shí)現(xiàn)ES6的class關(guān)鍵字

構(gòu)造函數(shù)與class實(shí)現(xiàn)類的區(qū)別

首先聊聊ES6 class定義的類和用構(gòu)造函數(shù)new出來(lái)的類的一些不同之處

  • class聲明提升
  • class聲明內(nèi)部會(huì)啟用嚴(yán)格模式
  • class的所有方法都是不可枚舉的
  • class的所有方法都沒(méi)有原型對(duì)象prototype
  • class定義的類不能被當(dāng)做函數(shù)調(diào)用

ES6的class 關(guān)鍵字的實(shí)現(xiàn)原理

/**
  *ES6的類
  **/
class Parent{
    
    constructor(a){
        this.a = a;
    }

    print(){
        console.log('parent')
    }

}
---------------------------------------Babel 2 ES5------------------------------------------------------
/**
  *ES5來(lái)實(shí)現(xiàn)ES6的類
  **/
var Parent = /*#__PURE__*/function (a) {
  function Parent() {
    _classCallCheck(this, Parent);
    this.a = a;
  }

  _createClass(Parent, [{
    key: "print",
    value: function print() {
      console.log('parent');
    }
  }]);

  return Parent;
}();

class的轉(zhuǎn)化

轉(zhuǎn)化之后的Parent,是一個(gè)變成自執(zhí)行函數(shù)。其中Person這個(gè)構(gòu)造函數(shù)包含了一些變量的定義,和_classCallCheck函數(shù)

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

_classCallCheck的作用是為了防止直接將class 定義的類當(dāng)做函數(shù)來(lái)使用,這也就很好的解釋了,ES6的class定義的類不能當(dāng)做函數(shù)來(lái)調(diào)用。

在這后是進(jìn)行了_createClass(Parent,{xxx})的調(diào)用,將定義的屬性掛在Person的原型鏈上

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

_createClass通過(guò)調(diào)用_defineProperties將定義在類的方法和靜態(tài)方法分別掛載在prototype和類函數(shù)上

因?yàn)槟J(rèn)設(shè)置了enumerable為false,所以在class中定義的方法都不會(huì)被枚舉出來(lái)。

繼承

class Child extends Parent{
    
    constructor(){}

    childPrint(){
        console.log('child')
    }

}

---------------------------------------Babel 2 ES5------------------------------------------------------
var Child = /*#__PURE__*/function (_Parent) {
  _inherits(Child, _Parent);

  function Child() {
    var _this;

    _classCallCheck(this, Child);

    return _possibleConstructorReturn(_this);
  }

  _createClass(Child, [{
    key: "childPrint",
    value: function childPrint() {
      console.log('child');
    }
  }]);

  return Child;
}(Parent);

Child中調(diào)用了_inherits函數(shù)來(lái)實(shí)現(xiàn)了繼承

        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 _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };
  return _setPrototypeOf(o, p);
}

將這里轉(zhuǎn)換一下就是

function F(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();

subClass.prototypte.constructor = subClass
subClass._proto = superClass;

納尼為啥這是什么鬼

Object.createnew

Object.create = function(o){
    var F = function(){};
    F.prototype = o;
    return new F();
}
//new關(guān)鍵字的對(duì)象的實(shí)現(xiàn)
var o = new Object();
o.prototype = Base.prototype;
//調(diào)用構(gòu)造函數(shù)
Base.call(o);

new和Object.create都是將相應(yīng)參數(shù)的prototype掛載在了新對(duì)象的prototype上

所以new Obj() 與 Object.create(Obj.prototype)最大的區(qū)別就是Object.create()并不會(huì)去調(diào)用Obj這個(gè)構(gòu)造函數(shù)

經(jīng)過(guò)上面的一系列調(diào)用就完成了原型鏈之間的各種連接,這里就不多說(shuō)啦,小紅書上講的很清楚了!

但是有個(gè)問(wèn)題,從上面這個(gè)代碼,從頭到尾都沒(méi)有對(duì)父類進(jìn)行實(shí)例化,那么怎么能從子類訪問(wèn)到父類的實(shí)例變量呢?

super

那么我們稍微改一改代碼呢

class Person {
  constructor() {
    this.a = 1;
  }
}

class Child extends Person {
  print(){
      console.log(this.a);
  }
}

轉(zhuǎn)換后

var Child = /*#__PURE__*/function (_Person) {
  _inherits(Child, _Person);

  function Child() {
    _classCallCheck(this, Child);

    return _possibleConstructorReturn(this, _getPrototypeOf(Child).apply(this, arguments));
  }

  _createClass(Child, [{
    key: "print",
    value: function print() {
      console.log(this.a);
    }
  }]);

  return Child;
}(Person);

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

這里通過(guò)_getPrototypeOf獲取到父類的構(gòu)造函數(shù),傳入當(dāng)前的Child的this指針進(jìn)行了調(diào)用,于是乎就把值給父類中實(shí)例變量掛載在了Child的this指針上了。
_getPrototypeOf(Child).apply(this, arguments)也就實(shí)現(xiàn)了super關(guān)鍵字啦

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容