用Babel学class

105 阅读3分钟

我正在参与掘金创作者训练营第5期,点击了解活动详情

首先简单介绍一下Babel:

Balbel 一词来自于希伯来语,直译为巴别塔,象征着统一的国度,统一的语言。JavaScript 相比于其他语言来说发展的较晚,随着互联网技术的发展,Javascript这门开发语言也逐渐的迭代,版本也越来越多,就引发了一系列兼容性问题。因此Belbel的出现就是为了将我们的JavaScript代码统一化。

Class转换

在class 里面定义属性

示例代码:

class A {
    prop1 = 1;
}

转换结果:

"use strict";
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);
    Object.defineProperty(Constructor, "prototype", { writable: false });
    return Constructor;
}

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

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
    } else {
        obj[key] = value;
    }
    return obj;
}

var A = /*#__PURE__*/_createClass(function A() {

    _classCallCheck(this, A);

    _defineProperty(this, "prop1", 1);
});

class是在严格模式环境下执行,如果在class里面调用this则是undefined

首先我们从 var A 开始看,它是方法_createClass传入一个函数A执行后返回的结果。另外两个参数protoProps, staticProps先不用看。
方法_createClass里面通过 Object.defineProperty来定义prototype属性,最后返回构造函数A

const a = new A();
console.log(a);

创建对象a,查看返回的结果为:A { prop1: 1 }构造函数A执行了两个方法_classCallCheck, _defineProperty
方法_classCallCheck的作用是通过传入的this判断是否用 new关键字创建对象。 得出一个结论:

class 不能当作普通函数调用,只能用 new 创建对象。

通过分析方法_defineProperty得出:属性是通过Object.defineProperty挂载到构造函数上,如果重复赋值属性就会被覆盖。 最后返回对象A { prop1: 1 }

constructor

示例代码:

class A {
    constroctor(b){
        this.prop2 = b;
    }
}

转换结果:

var A = /*#__PURE__*/_createClass(function A(b) {
  "use strict";

  _classCallCheck(this, A);

  this.prop2 = b;
});

可以看出属性 prop2 直接挂载到this上面的。

原型方法

示例代码:

 class A {
     f1(){
         console.log(this, "f1")
     }
     
     f2 = ()=>{
         console.log(this, "f2")
     }
 }

转换结果:

var A = /*#__PURE__*/function () {

  function A() {
    var _this = this;

    _classCallCheck(this, A);

    _defineProperty(this, "f2", function () {
      console.log(_this, "f2");
    });
  }

  _createClass(A, [{
    key: "f1",
    value: function f1() {
      console.log(this, "f1");
    }
  }]);

  return A;
}();

示例中使用了两种函数声明的方式,在方法A中首先保存了this,然后调用检查,通过Object.defineProperty挂载到构造函数上。f2的声明方式跟prop1的做法是一样的。 结论:

箭头函数中的this永远都指向构造函数A

方法f1则是通过_createClass方法挂载到了构造函数原型上。_createClass方法第二个参数protoProps对应的就是常规书写方式的原型方法。

静态方法

示例代码:

 class A {
     static f3(){
         console.log(this, "f3")
     }
     
     static f4 = ()=>{
         console.log(this, "f4")
     }
 }

转换结果:

var A = /*#__PURE__*/ (function () {
  function A() {
    _classCallCheck(this, A);
  }

  _createClass(A, null, [
    {
      key: "f3",
      value: function f3() {
        console.log(this, "f3");
      }
    }
  ]);

  return A;
})();

_defineProperty(A, "f4", function () {
  console.log(A, "f4");
});

方法f3通过_createClass方法挂载到了构造函数上。_createClass方法第三个参数staticProps对应的就是常规书写方式的静态方法。

方法f4则是通过_defineProperty直接挂载到构造函数上,函数里面的this都直接转换为构造函数A

总结

通过ES5对class进行转译分析,我们可以更加清晰的理解到class相比于普通的构造函数之间的差异,模拟ES6的底层实现也能帮助我们在日后写代码的时候能够准确的定位到那些意想不到的Bug。