es6使用class实现继承,用babel转换后是什么呢

65 阅读5分钟

前言

我们都知道,es6class实现了类,class关键字呢仅仅就是一个语法糖而已,本质上还是利用的函数原型。但是babel给我们进行转化后的代码肯定没有那么简单,还是有很多值得我们学习的地方,所以今天我们就来看看class实现的继承,用babel进行转化后的代码到底是什么。

class

先看看一个简单的代码

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  playing() {
    console.log(this.name + " playing")
  }
}

这段简单的代码,如果我们自己用函数原型方式进行实现的话,一定是这样的

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.playing = function () {
  console.log(this.name + ' playing')
}

但是babel给我们做了很多很多的edge case,下面我们就来看看吧(我附上详细的注释)

//这么简单的代码,用babel转化之后这么大一堆,是不是想吐了
//先看最后!!!(纯函数那里)
"use strict";

function _classCallCheck(instance, Constructor) {
//如果Constructor.prototype没有出现在instance的原型链,这报错
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
      //循环props,拿到每个对象
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    /**
    设置之后变为
            {
      key: "palying",
      value: function palying() {
        console.log(this.name + " eating~");
      },
      enumerable: false,
      configurable: true,
      writable: true
    }
    */
    //调用defineProperty,传入descriptor(数据属性描述符)设置这个key
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
//调用_defineProperties,Constructor就是下面定义的Person函数, protoProps下面就是那个数组
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  //我们如果在Person类中写static run = function() {}这种代码就会传入作为staticProps,
  //也就是转化为类的静态函数,转为es5就会变成Person.run = function () {}
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

// /*#__PURE__*/ 纯函数,纯函数概念大家都懂吧,就是幂等性(确定的输入必定产生确定的输出),
//纯函数执行过程中是不会产生副作用的
// /*#__PURE__*/这种注释大家应该也懂,魔法注释,意味着webpack会压缩 tree-shaking
var Person = /*#__PURE__*/ (function () {
  function Person(name, age) {
  //这个函数我在前面做了注释,也就意味着我们只能通过new Person进行调用(逻辑太缜密了)
    _classCallCheck(this, Person);
    this.name = name;
    this.age = age;
  }
    //前面坐了详细的解析
  _createClass(Person, [
    {
      key: "palying",
      value: function palying() {
        console.log(this.name + " eating~");
      }
    }
  ]);
   //最后返回 
  return Person;
})();//立即执行

相信在看了我的注释之后,是不是觉得babel还是有点意思哈!

class实现继承

下面来看重头戏,先看看es6的代码

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {
    console.log(this.name + " running~")
  }

  static staticMethod() {

  }
}

class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

  studying() {
    console.log(this.name + " studying~")
  }
}

我们知道es6实现继承呢,不过是大家耳熟能详的寄生组合式继承,下面就是我们大概率会写的寄生组合式继承,然后我们再看看与babel转化生成的有什么区别

function inheritPrototype(SubType, SuperType) {
  SubType.prototype = Object.create(SuperType.prototype)
  Object.defineProperty(SubType.prototype, 'constructor', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: SubType
  })
}

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.running = function () {
  console.log(this.name + ' running~')
}

Person.staticMethod = function () {}

function Student(name, age, sno) {
  Person.call(this, name, age)
  this.sno = sno
}

inheritPrototype(Student, Person)
Object.setPrototypeOf(Student, Person)//实现静态方法继承


Student.prototype.studying = function () {
  console.log(this.name + ' studying~')
}

const stu = new Student('zdy', 18, 111)
stu.studying()
stu.running()
//执行结果
//zdy studying~
//zdy running~

我们自己写的话,很简单,但是来看看babel写的,你会感叹真的有点东西!!(我附上详细注释)

"use strict";
var Person = /*#__PURE__*/ (function () {
  function Person(name, age) {
  //这个函数限制我们只能对其new调用
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }
    //上一段源码解释过了,这里不再注释
  _createClass(Person, [
    {
      key: "running",
      value: function running() {
        console.log(this.name + " running~");
      }
    }
  ]);

  return Person;
})();

var Student = /*#__PURE__*/ (function (_Person) {
  // 实现之前的寄生式继承的方法(静态方法的继承)
  _inherits(Student, _Person);

  var _super = _createSuper(Student);

  function Student(name, age, sno) {
    var _this;

    _classCallCheck(this, Student);

    // Person不能被当成一个函数去调用
    //意味着不能Person.call(this, name, age)
    //所以只能耍手段拿到super,super其实本来是Person,但是他的constuctor指向的Student,
    //而this = stu;所以stu instanceof Student =true,及可以调用super
   
    _this = _super.call(this, name, age);
     // 创建出来的对象 {name: , age: }
    _this.sno = sno;
    // {name: , age:, sno: }
    return _this;
  }

//请看上一段代码注释
  _createClass(Student, [
    {
      key: "studying",
      value: function studying() {
        console.log(this.name + " studying~");
      }
    }
  ]);

  return Student;
})(Person);

//symbol是es6出现的,我们要转为es5代码,就需要对他做支持(_typeof就是对symbol的支持)
function _typeof(obj) {
  "@babel/helpers - typeof";
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj &&
        typeof Symbol === "function" &&
        obj.constructor === Symbol &&
        obj !== Symbol.prototype
        ? "symbol"
        : typeof obj;
    };
  }
  return _typeof(obj);
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  //寄生组合式继承的核心,做完这一步,subClass.prototype.__proto__ = superClass.prototype
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true }
  });
  // 实现静态方法的继承
  // Student.__proto__ = Person
  if (superClass) _setPrototypeOf(subClass, superClass);
}

// o: Student
// p: Person
// 静态方法的继承
function _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };//这里本质就是Student.__proto__ = Person,让Student也能调用Person的静态方法
  return _setPrototypeOf(o, p);
}

//Derived = Student
//Derived = Student
function _createSuper(Derived) {
  //_isNativeReflectConstruct判断支不支持Reflect
  var hasNativeReflectConstruct = _isNativeReflectConstruct()
  return function _createSuperInternal() {
    //Super = Person(看我下面注释)
    var Super = _getPrototypeOf(Derived),
      result
    if (hasNativeReflectConstruct) {
      console.log(this)
      //这里的this,是我们new调用的创建出的stu对象
      //所以NewTarget = Student
      var NewTarget = _getPrototypeOf(this).constructor

      //Reflect.construct会通过Super创建出来一个函数, 
      //但是这个实例的原型constructor指向的是NewTarget
      //为什么这么做,是因为我们限制了不能通过除了new调用的方式来调用Perosn或者Student
      //本来我们要Person.call(this, name, age)来借用构造函数的,但是不能这么用,只能耍一点手段
      result = Reflect.construct(Super, arguments, NewTarget)
    } else {
      result = Super.apply(this, arguments)
    }
    return _possibleConstructorReturn(this, result)
    //_possibleConstructorReturn就是容错判断,最后返回的result
  }
}
function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  } else if (call !== void 0) {
    throw new TypeError(
      "Derived constructors may only return object or undefined"
    );
  }
  return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return self;
}

//判断支不支持Reflect
function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  if (Reflect.construct.sham) return false;
  if (typeof Proxy === "function") return true;
  try {
    Boolean.prototype.valueOf.call(
      Reflect.construct(Boolean, [], function () {})
    );
    return true;
  } catch (e) {
    return false;
  }
}

//做了一些edge case判断,本质就是拿到o这个对象的对象原型,也就是返回o.__proto__
function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

//请看上一段代码的注释
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a 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);
  }
}
//请看上一段代码的注释
function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}


是不是绕来绕去的,如果我们仔细看的话,一定会有很多收获!babel原来偷偷的为我们做了这么多edgecase,babel真是一个良心插件!!!