lingxiao-Zhu / blog

总结积累,读书笔记
3 stars 0 forks source link

ES6 的类转换成 ES5 是什么样子? #33

Open lingxiao-Zhu opened 3 years ago

lingxiao-Zhu commented 3 years ago

前言

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

今天就来看下隐藏在 Class 背后的技术点。本次解析基于 Babel LOOSE 模式

constructor

class A {
  constructor(){
    this.name = 1;
  }
}

// 对应 

var A = function A() {
  this.name = 1;
};

constructor 的作用就是给实例对象添加属性。

声明方法

class A {
  sayName(){
    console.log(this.name)
  } 
}

// 对应

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

  var _proto = A.prototype;

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

  return A;
}();

可以看到,当我们在 class 上声明一个方法,其实是将方法挂载到原型上。如果我们通过箭头函数的方式声明呢?

class A {
  sayName = () => {}
}
// 对应
var A = function A() {
  this.sayName = function () {};
};

其实是放到了实例上,初始化时进行赋值。

getter、setter

class A {
  get age(){
    return 27
  }
}

// 对应

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

  const props = [
    {
      key: "age",
      get: function get() {
        return 27;
      },
    },
  ];

  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(A.prototype, descriptor.key, descriptor);
  }

  return A;
})();

可以看到,首先把 getter 函数转成一个 descriptor 对象,然后通过 Object.defineProperty 挂载到类的原型上,不能枚举

静态方法

class A {
  static sayName(){}
}
// 对应
var A = /*#__PURE__*/function () {
  function A() {}

  A.sayName = function sayName() {};

  return A;
}();

继承

class B {}

class A extends B {}
// 对应
var A = /*#__PURE__*/ (function (_B) {
  A.prototype = Object.create(_B.prototype);
  A.prototype.constructor = A;
  Object.setPrototypeOf(A, _B);

  function A() {
    var _this;

    _this = _B.call(this) || this;
    _this.name = 1;
    return _this;
  }

  return A;
})(B);

只使用 extends,什么都不做的情况下,会:

super

class B {}

class A extends B{
  constructor(){
    super();
  }
  sayName(){
    super.sayName()
  }
  static sayAge(){
    super.sayAge();
  }
}

// 对应

// 省略继承部分
function A() {
  return _B.call(this) || this;
}

var _proto = A.prototype;

_proto.sayName = function sayName() {
  _B.prototype.sayName.call(this);
};

A.sayAge = function sayAge() {
  _B.sayAge.call(this);
};

可以看到不同阶段的 super 不一样:

总结

可以看出,其实 ES6 的实现和 ES5 中的寄生组合式继承差不多。