在使用es6语法class的时候,babel到底做了什么?

更新日期: 2018-07-16阅读: 2.4k标签: es6

自从有了webpack之后,我们这些jscoder似乎得到了前所未有的解放,箭头函数,对象解构,let,const关键字,以及class、extends等等关键字使用得不亦乐乎,反正,webpack会帮我们把这些es6代码转换成浏览器能够识别的es5代码,那么,我们有多少人真正的看过,babel转换之后的代码呢?今天,我就来看一下,当我们使用关键词class的时候,babel到底做了什么?


1、打开网址:https://babeljs.io/repl

我推荐打开网址:https://babeljs.io/repl,这里我们左边写es6代码,马上右边就能转译出es5代码,然后,我在左边输入了如下代码:

class A {
  constructor(name) {
      this.name = name
  }
  
  getName() {
      return this.name
  }
}

这是一个最简单的类,一个属性,一个方法。

这时候,右边框已经给我转译出了浏览器可识别的es5代码了,格式化之后是这样的:

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

好,现在来分析一下这段代码。


2、es6里面的类,本质上其实就是一个函数

// 自执行函数
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里面的类其实就是一个构造函数。


3、_classCallCheck函数

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)。
该函数的目的是防止构造函数被当做普通函数执行。


4、_createClass函数

//该函数也是一个自执行的函数,其返回值是一个函数
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的原型上去。


4、使用关键词extends,发生了什么?

我在https://babeljs.io/repl 左侧输入框上加了下面这行代码:

class B extends A {}

这时候,右侧多出了以下几行代码:


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


5、_inherits函数

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


6、_possibleConstructorReturn函数

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)的返回值就是一个对象,其实也就是对象。

原文:https://segmentfault.com/a/1190000015125847  


链接: https://fly63.com/article/detial/930

es6 箭头函数的使用总结,带你深入理解js中的箭头函数

箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。

详解JavaScript模块化开发require.js

js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD

js解构赋值,关于es6中的解构赋值的用途总结

ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...

ES6中let变量的特点,使用let声明总汇

ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明

ES6的7个实用技巧

ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构

ES6 Decorator_js中的装饰器函数

ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。

基于ES6的tinyJquery

Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...

ES6 中的一些技巧,使你的代码更清晰,更简短,更易读!

ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。

Rest/Spread 属性_探索 ES2018 和 ES2019

Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。

使用ES6让你的React代码提升到一个新档次

ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!