{"action":"created","issue":{"url":"https://api.github.com/repos/lcxfs1991/blog/issues/9","repository_url":"https://api.github.com/repos/lcxfs1991/blog","labels_url":"https://api.github.com/repos/lcxfs1991/blog/issues/9/labels{/name}","comments_url":"https://api.github.com/repos/lcxfs1991/blog/issues/9/comments","events_url":"https://api.github.com/repos/lcxfs1991/blog/issues/9/events","html_url":"https://github.com/lcxfs1991/blog/issues/9","id":154375237,"number":9,"title":"babel到底将代码转换成什么鸟样？","user":{"login":"lcxfs1991","id":3348398,"avatar_url":"https://avatars.githubusercontent.com/u/3348398?v=3","gravatar_id":"","url":"https://api.github.com/users/lcxfs1991","html_url":"https://github.com/lcxfs1991","followers_url":"https://api.github.com/users/lcxfs1991/followers","following_url":"https://api.github.com/users/lcxfs1991/following{/other_user}","gists_url":"https://api.github.com/users/lcxfs1991/gists{/gist_id}","starred_url":"https://api.github.com/users/lcxfs1991/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lcxfs1991/subscriptions","organizations_url":"https://api.github.com/users/lcxfs1991/orgs","repos_url":"https://api.github.com/users/lcxfs1991/repos","events_url":"https://api.github.com/users/lcxfs1991/events{/privacy}","received_events_url":"https://api.github.com/users/lcxfs1991/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":13,"created_at":"2016-05-12T00:59:56Z","updated_at":"2016-12-15T05:10:48Z","closed_at":null,"body":"[原文链接](https://github.com/lcxfs1991/blog/issues/9)\n## 前言\n\n将babel捧作前端一个划时代的工具一定也不为过，它的出现让许多程序员幸福地用上了es6新语法。但你就这么放心地让babel跑在外网？反正我是不放心，我就曾经过被坑过，于是萌生了研究babel代码转换的想法。本文不是分析babel源码，仅仅是看看babel转换的最终产物。\n\nes6在babel中又称为es2015。由于es2015语法众多，本文仅挑选了较为常用的一些语法点，而且主要是分析babel-preset-2015这个插件（react开发的时候，常在webpack中用到这个preset）。\n## babel-preset-2015\n\n打开babel-preset2015插件一看，一共20个插件。熟悉es2015语法的同志一看，多多少少能从字面意思知道某个插件是用于哪种语法的转换\n- babel-plugin-transform-es2015-template-literals  => es2015模板\n- babel-plugin-transform-es2015-literals\n- babel-plugin-transform-es2015-function-name => 函数name属性\n- babel-plugin-transform-es2015-arrow-functions => 箭头函数\n- babel-plugin-transform-es2015-block-scoped-functions => 函数块级作用域\n- babel-plugin-transform-es2015-classes => class类\n- babel-plugin-transform-es2015-object-super => super提供了调用prototype的方式\n- babel-plugin-transform-es2015-shorthand-properties => 对象属性的快捷定义，如obj = { x, y }\n- babel-plugin-transform-es2015-computed-properties => 对象中括号属性，如obj = {['x]: 1}\n- babel-plugin-transform-es2015-for-of => 对象for of遍历\n- babel-plugin-transform-es2015-sticky-regex\n- babel-plugin-transform-es2015-unicode-regex\n- babel-plugin-check-es2015-constants => const常量\n- babel-plugin-transform-es2015-spread => 对象扩展运算符属性，如...foobar\n- babel-plugin-transform-es2015-parameters => 函数参数默认值及扩展运算符\n- babel-plugin-transform-es2015-destructuring => 赋值解构\n- babel-plugin-transform-es2015-block-scoping => let和const块级作用域\n- babel-plugin-transform-es2015-typeof-symbol => symbol特性\n- babel-plugin-transform-es2015-modules-commonjs => commonjs模块加载\n- babel-plugin-transform-regenerator => generator特性\n## var, const and let\n\nconst和let现在一律转换成var。那const到底如何保证不变呢？如果你在源码中第二次修改const常量的值，babel编译会直接报错。\n转换前\n\n```\nvar a = 1;\nlet b = 2;\nconst c = 3;\n```\n\n转换后：\n\n```\nvar a = 1;\nvar b = 2;\nvar c = 3;\n```\n\n那let的块级作用怎么体现呢？来看看下面例子，实质就是在块级作用改变一下变量名，使之与外层不同。\n转换前：\n\n```\nlet a1 = 1;\nlet a2 = 6;\n\n{\n    let a1 = 2;\n    let a2 = 5;\n\n    {\n        let a1 = 4;\n        let a2 = 5;\n    }\n}\na1 = 3;\n```\n\n转换后：\n\n```\nvar a1 = 1;\nvar a2 = 6;\n\n{\n    var _a = 2;\n    var _a2 = 5;\n\n    {\n        var _a3 = 4;\n        var _a4 = 5;\n    }\n}\na1 = 3;\n```\n## 赋值解构\n\n写react的时候，我们使用负值解构去取对象的值，用起来非常爽，像这样：\n\n```\nvar props = {\n    name: \"heyli\",\n    getName: function() {\n\n    },\n    setName: function() {\n\n    }\n};\n\nlet { name, getName, setName } = this.props;\n```\n\n我们来看看转换的结果：\n\n```\nvar props = {\n    name: \"heyli\",\n    getName: function getName() {},\n    setName: function setName() {}\n};\n\nvar name = props.name;\nvar getName = props.getName;\nvar setName = props.setName;\n```\n\n至于数组呢？如果是一个匿名数组，则babel会帮你先定义一个变量存放这个数组，然后再对需要赋值的变量进行赋值。\n转换前：\n\n```\nvar [ a1, a2 ] = [1, 2, 3];\n```\n\n转换后：\n\n```\nvar _ref = [1, 2, 3];\nvar a1 = _ref[0];\nvar a2 = _ref[1];\n```\n\n看到这个，感觉转换结果跟我们想的还蛮一致。哈哈，使用的噩梦还没开始。\n\n如果使用匿名对象直接进行赋值解构会怎样呢？如下。babel为了使接收的变量唯一，直接就将匿名对象里的属性拼在一起，组成接收这个匿名对象的变量，吓得我赶紧检查一下项目里有没有这种写法。\n转换前：\n\n```\nvar { abc, bcd, cde, def } = { \"abc\": \"abc\", \"bcd\": \"bcd\", \"cde\": \"cde\", \"def\": \"def\", \"efg\": \"efg\", \"fgh\": \"fgh\" };\n```\n\n转换后：\n\n```\nvar _abc$bcd$cde$def$efg$ = { \"abc\": \"abc\", \"bcd\": \"bcd\", \"cde\": \"cde\", \"def\": \"def\", \"efg\": \"efg\", \"fgh\": \"fgh\" };\nvar abc = _abc$bcd$cde$def$efg$.abc;\nvar bcd = _abc$bcd$cde$def$efg$.bcd;\nvar cde = _abc$bcd$cde$def$efg$.cde;\nvar def = _abc$bcd$cde$def$efg$.def;\n```\n\n还有一种对象深层次的解构赋值：\n转换前：\n\n```\nvar obj = {\n    p1: [\n        \"Hello\",\n        { p2: \"World\" }\n    ]\n};\n\nvar { p1: [s1, { p2 }] } = obj;\n```\n\n转换后：\n\n```\n// 为解释本人将代码美化了\nvar _slicedToArray = (function() {\n    function sliceIterator(arr, i) {\n        var _arr = [];\n        var _n = true;\n        var _d = false;\n        var _e = undefined;\n        try {\n           // 用Symbol.iterator造了一个可遍历对象，然后进去遍历。\n            for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {\n                _arr.push(_s.value);\n                if (i && _arr.length === i) break;\n            }\n        } catch (err) {\n            _d = true;\n            _e = err;\n        } finally {\n            try {\n                if (!_n && _i[\"return\"]) _i[\"return\"]();\n            } finally {\n                if (_d) throw _e;\n            }\n        }\n        return _arr;\n    }\n    return function(arr, i) {\n        if (Array.isArray(arr)) {\n            return arr;\n        } else if (Symbol.iterator in Object(arr)) {\n            return sliceIterator(arr, i);\n        } else {\n            throw new TypeError(\"Invalid attempt to destructure non-iterable instance\");\n        }\n    };\n})();\n\nvar obj = {\n   p1: [\"Hello\", { p2: \"World\" }]\n};\n\nvar _obj$p = _slicedToArray(obj.p1, 2);\n\nvar s1 = _obj$p[0];\nvar p2 = _obj$p[1].p2;\n```\n\nbabel在代码顶部生产了一个公共的代码_slicedToArray。大概就是将对象里面的一些属性转换成数组，方便解构赋值的进行。但Symbol.iterator的兼容性并不好（如下图），还是谨慎使用为妙。\n<img width=\"615\" alt=\"symbol\" src=\"https://cloud.githubusercontent.com/assets/3348398/15202371/3a6370a2-182c-11e6-8124-cefe6edb331a.png\">\n\n另外，下面这种对字符串进行赋值解构也同样使用到_slicedToArray方法：\n\n```\nconst [a, b, c, d, e] = 'hello';\n```\n## 函数参数默认值及扩展运算符\n\n在es5的年代，一般我们写参数的默认值都会这么写：\n\n```\nfunction func(x, y) {\n    var x = x || 1;\n    var y = y || 2;\n}\n```\n\n我们来看看babel的转换办法:\n\n```\nfunction func({x, y} = { x: 0, y: 0 }) {\n  return [x, y];\n}\n\nfunction func1(x = 1, y = 2) {\n    return [x, y];\n}\n```\n\n```\nfunction func() {\n  var _ref = arguments.length <= 0 || arguments[0] === undefined ? { x: 0, y: 0 } : arguments[0];\n\n  var x = _ref.x;\n  var y = _ref.y;\n\n  return [x, y];\n}\n\nfunction func1() {\n  var x = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0];\n  var y = arguments.length <= 1 || arguments[1] === undefined ? 2 : arguments[1];\n\n  return [x, y];\n}\n```\n\nbabel这里使有了arguments来做判。第一种情况涉及解构赋值，因此x和y的值还是有可能是undefined的。至于第二种情况，则会保证2个参数的默认值分别是1和2.\n\n再来看一种。...y代表它接收了剩下的参数。也就是arguments除了第一个标号的参数之外剩余的参数。\n转换前：\n\n```\nfunction func(x, ...y) {\n    console.log(x);\n    console.log(y);\n    return x * y.length;\n}\n```\n\n转换后：\n\n```\nfunction func(x) {\n    console.log(x);\n\n    for (var _len = arguments.length, y = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n        y[_key - 1] = arguments[_key];\n    }\n\n    console.log(y);\n    return x * y.length;\n}\n```\n## 箭头函数\n\n剪头函数其实主要是省了写函数的代码，同时能够直接用使外层的this而不用担心context切换的问题。以前我们一般都要在外层多写一个_this/self直向this。babel的转换办法其实跟我们的处理无异。\n转换前：\n\n```\nvar obj = {\n    prop: 1,\n    func: function() {\n        var _this = this;\n\n        var innerFunc = () => {\n            this.prop = 1;\n        };\n\n        var innerFunc1 = function() {\n            this.prop = 1;\n        };\n    },\n\n};\n```\n\n转换后：\n\n```\nvar obj = {\n    prop: 1,\n    func: function func() {\n        var _this2 = this;\n\n        var _this = this;\n\n        var innerFunc = function innerFunc() {\n            _this2.prop = 1;\n        };\n\n        var innerFunc1 = function innerFunc1() {\n            this.prop = 1;\n        };\n    }\n\n};\n```\n## 对象的能力增强\n### 对象属性的快捷定义\n\n转换前：\n\n```\nvar a = 1,\n    b = \"2\",\n    c = function() {\n        console.log('c');\n    };\n\nvar obj = {a, b, c};\n```\n\n转换后：\n\n```\nvar a = 1,\n    b = \"2\",\n    c = function c() {\n    console.log('c');\n};\n\nvar obj = { a: a, b: b, c: c };\n```\n### 对象中括号属性\n\nes2015开始新增了在对象中用中括号解释属性的功能，这对变量、常量等当对象属性尤其有用。\n转换前：\n\n```\nconst prop2 = \"PROP2\";\nvar obj = {\n    ['prop']: 1,\n    ['func']: function() {\n        console.log('func');\n    },\n        [prop2]: 3\n};\n```\n\n转换后：\n\n```\nvar _obj;\n// 已美化\nfunction _defineProperty(obj, key, value) {\n    if (key in obj) {\n        Object.defineProperty(obj, key, {\n            value: value,\n            enumerable: true,\n            configurable: true,\n            writable: true\n        });\n    } else {\n        obj[key] = value;\n    }\n    return obj;\n}\n\nvar prop2 = \"PROP2\";\nvar obj = (_obj = {}, _defineProperty(_obj, 'prop', 1), _defineProperty(_obj, 'func', function func() {\n    console.log('func');\n}), _defineProperty(_obj, prop2, 3), _obj);\n```\n\n看似简单的属性，babel却大动干戈。新增了一个_defineProperty函数，给新建的_obj = {}进行属性定义。除此之外使用小括号包住一系列从左到右的运算使整个定义更简洁。\n### 使用super去调用prototype\n\n以前我们一般都用obj.prototype或者尝试用this去往上寻找prototype上面的方法。而babel则自己写了一套在prototype链上寻找方法/属性的算法。\n转换前\n\n```\nvar obj = {\n    toString() {\n     // Super calls\n     return \"d \" + super.toString();\n    },\n};\n```\n\n转换后：\n\n```\nvar _obj;\n// 已美化\nvar _get = function get(object, property, receiver) {\n   // 如果prototype为空，则往Function的prototype上寻找\n    if (object === null) object = Function.prototype;\n    var desc = Object.getOwnPropertyDescriptor(object, property);\n    if (desc === undefined) {\n        var parent = Object.getPrototypeOf(object);\n        // 如果在本层prototype找不到，再往更深层的prototype上找\n        if (parent === null) {\n            return undefined;\n        } else {\n            return get(parent, property, receiver);\n        }\n    }\n    // 如果是属性，则直接返回\n    else if (\"value\" in desc) {\n        return desc.value;\n    }\n    // 如果是方法，则用call来调用，receiver是调用的对象 \n    else {\n        var getter = desc.get;  // getOwnPropertyDescriptor返回的getter方法\n        if (getter === undefined) {\n            return undefined;\n        }\n        return getter.call(receiver);\n    }\n};\n\nvar obj = _obj = {\n  toString: function toString() {\n    // Super calls\n    return \"d \" + _get(Object.getPrototypeOf(_obj), \"toString\", this).call(this);\n  }\n};\n```\n### Object.assign 和 Object.is\n\nes6新增的Object.assign极大方便了对象的克隆复制。但babel的es2015 preset并不支持，所以没对其进入转换，这会使得一些移动端机子遇到这种写法会报错。所以一般开发者都会使用object-assign这个npm的库做兼容。\n\nObject.is用于比较对象的值与类型，es2015 preset同样不支持编译。\n## es6模板\n### 多行字符串\n\n转换前：\n\n```\nconsole.log(`string text line 1\nstring text line 2`);\n```\n\n转换后：\n\n```\nconsole.log(\"string text line 1\\nstring text line 2\");\n```\n### 字符中变量运算\n\n转换前：\n\n```\nvar a = 5;\nvar b = 10;\nconsole.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);\n```\n\n转换后：\n\n```\nvar a = 5;\nvar b = 10;\nconsole.log(\"Fifteen is \" + (a + b) + \" and not \" + (2 * a + b) + \".\");\n```\n### 标签模板\n\nes6的这种新特性给模板处理赋予更强大的功能，一改以往对模板进行各种replace的处理办法，用一个统一的handler去处理。babel的转换主要是添加了2个属性，因此看起来也并不算比较工程浩大的编译。\n转换前：\n\n```\nvar a = 5;\nvar b = 10;\n\nfunction tag(strings, ...values) {\n  console.log(strings[0]); // \"Hello \"\n  console.log(strings[1]); // \" world \"\n  console.log(values[0]);  // 15\n  console.log(values[1]);  // 50\n\n  return \"Bazinga!\";\n}\n\ntag`Hello ${ a + b } world ${ a * b }`;\n```\n\n转换后：\n\n```\nvar _templateObject = _taggedTemplateLiteral([\"Hello \", \" world \", \"\"], [\"Hello \", \" world \", \"\"]);\n// 已美化\nfunction _taggedTemplateLiteral(strings, raw) {\n    return Object.freeze(Object.defineProperties(strings, {\n        raw: {\n            value: Object.freeze(raw)\n        }\n    }));\n}\n// 给传入的object定义strings和raw两个不可变的属性。\n\nvar a = 5;\nvar b = 10;\n\nfunction tag(strings) {\n  console.log(strings[0]); // \"Hello \"\n  console.log(strings[1]); // \" world \"\n  console.log(arguments.length <= 1 ? undefined : arguments[1]); // 15\n  console.log(arguments.length <= 2 ? undefined : arguments[2]); // 50\n\n  return \"Bazinga!\";\n}\n\ntag(_templateObject, a + b, a * b);\n```\n## 模块化与类\n### 类class\n\njavascript实现oo一直是非常热门的话题。从最原始时代需要手动维护在构造函数里调用父类构造函数,到后来封装好函数进行extend继承，再到babel出现之后可以像其它面向对象的语言一样直接写class。es2015的类方案仍然算是过渡方案，它所支持的特性仍然没有涵盖类的所有特性。目前主要支持的有：\n- constructor\n- static方法\n- get 方法\n- set 方法\n- 类继承\n- super调用父类方法。\n\n转换前：\n\n```\nclass Animal {\n\n    constructor(name, type) {\n        this.name = name;\n        this.type = type;\n    }\n\n    walk() {\n        console.log('walk');\n    }\n\n    run() {\n        console.log('run')\n    }\n\n    static getType() {\n        return this.type;\n    }\n\n    get getName() {\n        return this.name;\n    }\n\n    set setName(name) {\n        this.name = name;\n    }\n\n\n}\n\nclass Dog extends Animal {\n    constructor(name, type) {\n        super(name, type);\n    }\n\n    get getName() {\n        return super.getName();\n    }\n}\n```\n\n转换后（由于代码太长，先省略辅助的方法）：\n\n```\n/**\n......一堆辅助方法，后文详述\n**/\nvar Animal = (function () {\n    function Animal(name, type) {\n                // 此处是constructor的实现，用_classCallCheck来判定constructor正确与否\n        _classCallCheck(this, Animal);\n\n        this.name = name;\n        this.type = type;\n    }\n        // _creatClass用于创建类及其对应的方法\n    _createClass(Animal, [{\n        key: 'walk',\n        value: function walk() {\n            console.log('walk');\n        }\n    }, {\n        key: 'run',\n        value: function run() {\n            console.log('run');\n        }\n    }, {\n        key: 'getName',\n        get: function get() {\n            return this.name;\n        }\n    }, {\n        key: 'setName',\n        set: function set(name) {\n            this.name = name;\n        }\n    }], [{\n        key: 'getType',\n        value: function getType() {\n            return this.type;\n        }\n    }]);\n\n    return Animal;\n})();\n\nvar Dog = (function (_Animal) {\n        // 子类继承父类\n    _inherits(Dog, _Animal);\n\n    function Dog(name, type) {\n        _classCallCheck(this, Dog);\n                // 子类实现constructor\n                // babel会强制子类在constructor中使用super，否则编译会报错\n        return _possibleConstructorReturn(this, Object.getPrototypeOf(Dog).call(this, name, type));\n    }\n\n    _createClass(Dog, [{\n        key: 'getName',\n        get: function get() {\n                       // 跟上文使用super调用原型链的super编译解析的方法一致，\n                       // 也是自己写了一个回溯prototype原型链\n            return _get(Object.getPrototypeOf(Dog.prototype), 'getName', this).call(this);\n        }\n    }]);\n\n    return Dog;\n})(Animal);\n```\n\n```\n// 检测constructor正确与否\nfunction _classCallCheck(instance, Constructor) {\n    if (!(instance instanceof Constructor)) {\n        throw new TypeError(\"Cannot call a class as a function\");\n    }\n}\n```\n\n```\n// 创建类\nvar _createClass = (function() {\n    function defineProperties(target, props) {\n        for (var i = 0; i < props.length; i++) {\n            var descriptor = props[i];\n            // es6规范要求类方法为non-enumerable\n            descriptor.enumerable = descriptor.enumerable || false;\n            descriptor.configurable = true;\n            // 对于setter和getter方法，writable为false\n            if (\"value\" in descriptor) descriptor.writable = true;\n            Object.defineProperty(target, descriptor.key, descriptor);\n        }\n    }\n    return function(Constructor, protoProps, staticProps) {\n        // 非静态方法定义在原型链上\n        if (protoProps) defineProperties(Constructor.prototype, protoProps);\n        // 静态方法直接定义在constructor函数上\n        if (staticProps) defineProperties(Constructor, staticProps);\n        return Constructor;\n    };\n})();\n```\n\n```\n// 继承类\nfunction _inherits(subClass, superClass) {\n   // 父类一定要是function类型\n    if (typeof superClass !== \"function\" && superClass !== null) {\n        throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass);\n    }\n    // 使原型链subClass.prototype.__proto__指向父类superClass，同时保证constructor是subClass自己\n    subClass.prototype = Object.create(superClass && superClass.prototype, {\n        constructor: {\n            value: subClass,\n            enumerable: false,\n            writable: true,\n            configurable: true\n        }\n    });\n    // 保证subClass.__proto__指向父类superClass\n    if (superClass) \n        Object.setPrototypeOf ? \n        Object.setPrototypeOf(subClass, superClass) :    subClass.__proto__ = superClass;\n}\n```\n\n```\n// 子类实现constructor\nfunction _possibleConstructorReturn(self, call) {\n    if (!self) {\n        throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n    }\n    // 若call是函数/对象则返回\n    return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self;\n}\n```\n\n先前在用react重构项目的时候，所有的react组件都已经摒弃了es5的写法，一律采用了es6。用类的好处写继续更加方便，但无法用mixin，需要借助更新的es7语法中的decorator才能够实现类mixin的功能(例如pureRender）。但这次分析完babel源码之后，才发现原来babel在实现class特性的时候，定义了许多方法，尽管看起来并不太优雅。\n### 模块化\n\n在开发react的时候，我们往往用webpack搭配babel的es2015和react两个preset进行构建。之前看了一篇文章对babel此处的模块加载有些启发（《分析 Babel 转换 ES6 module 的原理》）。\n\n示例：\n\n```\n// test.js\nimport { Animal as Ani, catwalk } from \"./t1\";\nimport * as All from \"./t2\";\n\n\nclass Cat extends Ani {\n\n    constructor() {\n        super();\n    }\n}\n\nclass Dog extends Ani {\n    constructor() {\n        super();\n    }\n}\n```\n\n```\n// t1.js\nexport class Animal {\n\n    constructor() {\n\n    }\n\n}\n\nexport function catwal() {\n    console.log('cat walk');\n};\n```\n\n```\n// t2.js\nexport class Person {\n    constructor() {\n\n    }\n\n}\n\nexport class Plane {\n    constructor() {\n\n    }\n\n}\n```\n\n通过webpack与babel编译后：\n\n```\n// t1.js的模块\nObject.defineProperty(exports, \"__esModule\", {\n    value: true\n});\nexports.catwal = catwal;\n\n// 省略一些类继承的方法\n\nvar Animal = exports.Animal = function Animal() {\n    _classCallCheck(this, Animal);\n};\n\nfunction catwal() {\n    console.log('cat walk');\n};\n\n// t2.js的模块\nObject.defineProperty(exports, \"__esModule\", {\n    value: true\n});\n\n// 省略一些类继承的方法\n\nvar Person = exports.Person = function Person() {\n    _classCallCheck(this, Person);\n};\n\nvar Plane = exports.Plane = function Plane() {\n    _classCallCheck(this, Plane);\n};\n\n// test.js的模块\nvar _t = __webpack_require__(1);\n\nvar _t2 = __webpack_require__(3); // 返回的都是exports上返回的对象属性\n\nvar All = _interopRequireWildcard(_t2);\n\nfunction _interopRequireWildcard(obj) {\n    // 发现是babel编译的， 直接返回\n    if (obj && obj.__esModule) {\n        return obj;\n    }\n   // 非babel编译， 猜测可能是第三方模块，为了不报错，让default指向它自己\n    else {\n        var newObj = {};\n        if (obj != null) {\n            for (var key in obj) {\n                if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];\n            }\n        }\n        newObj.default = obj;\n        return newObj;\n    }\n}\n\n// 省略一些类继承的方法\n\nvar Cat = (function (_Ani) {\n    _inherits(Cat, _Ani);\n\n    function Cat() {\n        _classCallCheck(this, Cat);\n\n        return _possibleConstructorReturn(this, Object.getPrototypeOf(Cat).call(this));\n    }\n\n    return Cat;\n})(_t.Animal);\n\nvar Dog = (function (_Ani2) {\n    _inherits(Dog, _Ani2);\n\n    function Dog() {\n        _classCallCheck(this, Dog);\n\n        return _possibleConstructorReturn(this, Object.getPrototypeOf(Dog).call(this));\n    }\n\n    return Dog;\n})(_t.Animal);\n```\n\nes6的模块加载是属于多对象多加载，而commonjs则属于单对象单加载。babel需要做一些手脚才能将es6的模块写法写成commonjs的写法。主要是通过定义__esModule这个属性来判断这个模块是否经过babel的编译。然后通过_interopRequireWildcard对各个模块的引用进行相应的处理。\n\n另一个发现是，通过webpack打包babel编译后的代码，每一个模块里面都包含了相同的类继承帮助方法，这是开发时忽略的。由此可看，在开发react的时候用es5的语法可能会比使用es6的class能使js bundle更小。\n## babel es2015 loose mode\n\n开发家校群的时候，在android4.0下面报esModule错误的问题，如下：\n\n```\nUncaught TypeError: Cannot assign to read only property '__esModule' of #<Object>。\n```\n\n经查证，发现是构建中babel-es2015 loader的模式问题，会导致Android4.0的用户有报错。只需要使用loose mode就可以解决问题。下面是相关的stackoverflow issue以及对应解决问题的npm包。\n- [stackoverflow](http://stackoverflow.com/questions/33446318/is-there-a-way-to-use-loose-modules-when-using-es2015-preset-in-babel-6)\n- [babel-preset-est2015-loose npm package](https://www.npmjs.com/package/babel-preset-es2015-loose)\n\n那么es2015和normal mode和loose mode有什么区别呢，这个出名的博客略有介绍:[Babel 6: loose mode](http://www.2ality.com/2015/12/babel6-loose-mode.html)。\n\n实质就是（作者总结）normal mode的转换更贴近es6的写法，许多的property都是通过Object.defineProperty进行的。而loose mode则更贴近es5的写法，性能更好一些，兼容性更好一些，但将这部份代码再转换成native es6的话会比较麻烦一些（感觉这一点并不是缺点，有源码就可以了）。\n\n上面esModule解决的办法，实质就是将\n\n```\nObject.defineProperty(exports, \"__esModule\", {\n    value: true\n});\n```\n\n改成 \n`exports.__esModule = true;`\n\n再举个例子，如下面的Cat类定义：\n\n```\nclass Cat extends Ani {\n\n    constructor() {\n        super();\n    }\n\n    miao() {\n        console.log('miao');\n    }\n}\n```\n\n正常模式会编译为：\n\n```\nvar Cat = (function (_Ani) {\n    _inherits(Cat, _Ani);\n\n    function Cat() {\n        _classCallCheck(this, Cat);\n\n        return _possibleConstructorReturn(this, Object.getPrototypeOf(Cat).call(this));\n    }\n\n    _createClass(Cat, [{\n        key: \"miao\",\n        value: function miao() {\n            console.log('miao');\n        }\n    }]);\n\n    return Cat;\n})(_t.Animal);\n```\n\nloose mode模式会编译为：\n\n```\nvar Cat = (function (_Ani) {\n    _inherits(Cat, _Ani);\n\n    function Cat() {\n        _classCallCheck(this, Cat);\n\n        return _possibleConstructorReturn(this, _Ani.call(this));\n    }\n\n    Cat.prototype.miao = function miao() {\n        console.log('miao');\n    };\n\n    return Cat;\n})(_t.Animal);\n```\n\nbabel es2015中loose模式主要是针对下面几个plugin：\n- transform-es2015-template-literals\n- transform-es2015-classes\n- transform-es2015-computed-properties\n- transform-es2015-for-of\n- transform-es2015-spread\n- transform-es2015-destructuring\n- transform-es2015-modules-commonjs\n\n每一种的转换方式在此就不再赘述了，大家可以回家自己试。\n\n如有错误，恳请斧正！\n## 参考文章：\n#### babel try out\n- https://babeljs.io/repl/\n#### template literals\n- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n#### block-scoped functions\n- https://blogs.msdn.microsoft.com/cdndevs/2015/09/16/future-of-javascript-ecmascript-6-es2015-block-scoping/\n- http://www.programmerinterview.com/index.php/javascript/javascript-block-scope/\n- http://www.2ality.com/2015/02/es6-scoping.html\n#### classes\n- http://purplebamboo.github.io/2014/07/13/javascript-oo-class/\n- http://blog.rainy.im/2015/07/20/prototype-chain-in-js/\n#### objects\n- http://fourkitchens.com/blog/article/practical-introduction-es2015-objects\n#### commonjs and es6 module\n- http://ryerh.com/javascript/2016/03/27/babel-module-implementation.html\n- http://www.2ality.com/2015/12/babel6-loose-mode.html\n- http://www.2ality.com/2015/02/es6-classes-final.html\n"},"comment":{"url":"https://api.github.com/repos/lcxfs1991/blog/issues/comments/267240486","html_url":"https://github.com/lcxfs1991/blog/issues/9#issuecomment-267240486","issue_url":"https://api.github.com/repos/lcxfs1991/blog/issues/9","id":267240486,"user":{"login":"WJsjtu","id":3365866,"avatar_url":"https://avatars.githubusercontent.com/u/3365866?v=3","gravatar_id":"","url":"https://api.github.com/users/WJsjtu","html_url":"https://github.com/WJsjtu","followers_url":"https://api.github.com/users/WJsjtu/followers","following_url":"https://api.github.com/users/WJsjtu/following{/other_user}","gists_url":"https://api.github.com/users/WJsjtu/gists{/gist_id}","starred_url":"https://api.github.com/users/WJsjtu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WJsjtu/subscriptions","organizations_url":"https://api.github.com/users/WJsjtu/orgs","repos_url":"https://api.github.com/users/WJsjtu/repos","events_url":"https://api.github.com/users/WJsjtu/events{/privacy}","received_events_url":"https://api.github.com/users/WJsjtu/received_events","type":"User","site_admin":false},"created_at":"2016-12-15T05:10:48Z","updated_at":"2016-12-15T05:10:48Z","body":"@lcxfs1991 有个问题想问一下，就是`// 保证subClass.__proto__指向父类superClass`这个注释所指的代码，如果我没有搞错的话，subClass, superClass都是函数类型，函数的`__proto__`应该是其父类`Function`的`prototype`，那么这里对`subClass.__proto__`操作的意义何在？我觉得这句没有的话，应该是不影响`subClass`的实例去用`instanceof`来检查的，因为原型链已经被建立了。"}}
{"actor":{"display_login":"WJsjtu"}}