自从有了webpack以后,咱们这些jscoder彷佛获得了史无前例的解放,箭头函数,对象解构,let,const关键字,以及class、extends等等关键字使用得不亦乐乎,反正,webpack会帮咱们把这些es6代码转换成浏览器可以识别的es5代码,那么,咱们有多少人真正的看过,babel转换以后的代码呢?今天,我就来看一下,当咱们使用关键词class的时候,babel到底作了什么?webpack
我推荐打开网址:https://babeljs.io/repl,这里咱们左边写es6代码,立刻右边就能转译出es5代码,而后,我在左边输入了以下代码:es6
class A { constructor(name) { this.name = name } getName() { return this.name } }
这是一个最简单的类,一个属性,一个方法。web
这时候,右边框已经给我转译出了浏览器可识别的es5代码了,格式化以后是这样的:express
'use strict'; var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var A = function () { function A(name) { _classCallCheck(this, A); this.name = name; } _createClass(A, [{ key: 'getName', value: function getName() { return this.name; } }]); return A; }();
好,如今来分析一下这段代码。数组
// 自执行函数 var A = function () { function A(name) { // 这个函数的目的实际上是防止这个构造函数被当作普通函数执行 _classCallCheck(this, A); this.name = name; } // 对函数A执行_createClass方法,其实就是给A的原型上绑定方法 _createClass(A, [{ key: 'getName', //方法名 value: function getName() { //函数体 return this.name; } }]); return A; }();
这段代码,变量A是一个自执行函数的返回值,该自执行函数的返回值其实就是咱们熟悉的构造函数,因此,es6里面的类其实就是一个构造函数。浏览器
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
这个函数特别简单,当执行函数A的时候,不容许this不是A的子类实例,好比直接这样调用A(),可是在A的子类B中能够这样调用:A.apply(this, arguments)。
该函数的目的是防止构造函数被当作普通函数执行。babel
//该函数也是一个自执行的函数,其返回值是一个函数 var _createClass = function () { // 把props数组上每个对象,经过Object.defineProperty方法,都定义到目标对象target上去 function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { //这里要确保props[i]是一个对象,而且有key和value两个键 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); } } return function (Constructor, protoProps, staticProps) { // 若是传入了原型属性数组,就把属性所有定义到Constructor的原型上去 if (protoProps) defineProperties(Constructor.prototype, protoProps); // 若是传入了静态属性数组,就把属性所有定义到Constructor对象自身上去 if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
其实_createClass函数作的事情,就是把几个方法拷贝到构造函数A的原型上去。app
我在https://babeljs.io/repl 左侧输入框上加了下面这行代码:函数
class B extends A {}
这时候,右侧多出了如下几行代码:this
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called'); } return call && (typeof call === 'object' || typeof call === 'function') ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var B = function (_A) { _inherits(B, _A); function B() { _classCallCheck(this, B); //这里的重点是第二个参数:(B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments); //这里实际上是将子类的实例对象,调用了父类的构造函数方法,这样父类的属性就均可以拷贝到子类上来 return _possibleConstructorReturn(this, (B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments)); } return B; }(A);
function _inherits(subClass, superClass) { //简单校验 if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } //把子类的原型指向父类的原型建立出来的对象(注意不是直接指向父类原型),而且修正constructor属性为子类本身 subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); // 这一步操做,实际上是想把superClass放到subClass下,至关于subClass.super = superClass,这样后面的代码中,subClass里面能方便的引用到superClass函数 if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called'); } return call && (typeof call === 'object' || typeof call === 'function') ? call : self; }
若是call不是对象或者函数,即该调用:(B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments)的返回值既不是对象,也不是函数,那么,就直接返回当前的self,而self其实就是子类B里面的实例指针this。正常状况,(B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments)的返回值就是一个对象,其实也就是对象。
好了,上面算是基本说清楚了使用es6语法定义类、继承类,到底发生了什么,若是错误,还请指正,谢谢!