ES6 class实现解析

class是ES6的语法糖,如下的class写法,会被babel转为ES5函数实现类的方式:

// es6 class 写法
class Person{
   constructor(name, age){
     this.name = name
     this.age = age
    }

  sayName (){
    return this.name
  }

}

class Men extends Person{
    constructor(name, age){
      super()
      this.gender = 'male'
    }
}

经过babel转换的代码很长,我这里就不贴了,需要查看的请自行到 babel在线转换网站看一下。

父类的创建方式

首先 class Person 会被转换成这样

var Person = /*#__PURE__*/ (function () {
  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

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

  return Person;
})();

其实就是非常熟悉的es5创建类的方法:

function P {
    // 判断是不是通过new 调用
	if (this instance of P) return Error('.......')
	this.x = 1
}

这里的_classCallCheck 就是判断构造函数是不是通过new调用

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

_createClass(class, properties)这个函数将properties以键值对数组的形式将属性挂到Person.prototype上,挂在prototype上的变量/函数在继承时多个子实例公用父实例prototype上的变量,节省开销。具体可以参考我的另外一篇博文 Javascript ES5实现继承的N种方式

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);
  }
}

核心就是 Object.defineProperty(target, descriptor.key, descriptor); 至此,第一个类Person创建好了。

子类继承父类

子类Men被Babel转换成如下代码

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

  var _super = _createSuper(Men);

  function Men(name, age) {
    var _this;

    _classCallCheck(this, Men);

    _this = _super.call(this);
    _this.gender = "male";
    return _this;
  }

  return Men;
})(Person);

先来看 _inherits这个函数,这个函数完成了子类继承父类prototype,并将Men.prototype.constructor设置为自身构造函数。下面看一下怎么做到的。

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);
}

这里比较核心的是

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

Object.create(proto,[propertiesObject]) 会先创建一个空对象o,o.__proto__指向proto,o对象的属性为后面的属性数组制定的值。详情见 Object.create-MDN 现在Men.prototype被设置为superClass.prototype 也就是Person这个类函数。Men.prototype.constructor被设置为Men.

接着执行 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);
}

使得 Men.__proto__ = Person 完成原型链拼接。

接下来就是

var _super = _createSuper(Men);

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);
  };
}

这里返回了一个函数fn,并通过闭包保存在Super变量。它的大致的逻辑如下:

  1. var Super = _getPrototypeOf(Derived), 从Men 这个类函数的prototype取出Person的构造函数。

  2. var NewTarget = _getPrototypeOf(this).constructor; 获取Men构造函数Men;

  3. result = Reflect.construct(Super, arguments, NewTarget); Reflect.construct相当于执行 new 操作符, 前两个参数是new 的构造函数和参数,相当于执行了new Person(arguments),第三个参数NewTarget是让这个新创建的对象成为NewTarget的实例(Men实例)。关于 Reflect.construct的参考Reflect.construct参考-stack-overflow 这里result 为:

    Men() {
     this.name: undefined,
     this.age: undefined
    }
    
  4. return _possibleConstructorReturn(this, result); 检测result类型,没有问题直接返回result。

这个Super的作用是在后续通过new Men创建子类实例时,通过 _this = _super.call(this);将父类通过this.xxx = xxx设置的属性挂到子类Men上。最后把Men的构造函数返回,其余的部分和Person类并无二致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值