Open lcxfs1991 opened 8 years ago
比对beble的转换结果,babel似乎差很多。
要直接转es5/3的话, ts比babel实用多了
谢谢总结! 👍
很实用的参考
好吧。。。
不错的总结!
收藏
感觉和这个相似的文章应该在 CoffeeScript 风靡起来的时候也出现过,毕竟 CoffeeScript 编译出来的也是这样子。
不过大部分情况下编译后的 ES5 代码直接就混淆压缩投入使用了,也不会直接阅读编译后的代码。
感谢分享。 babel文档写了,它只转新语法,不转新api, Object.assign 和 Object.is 这些是 es6-shim 或者 babel-polyfill 做的事情。 所以如果大家使用babel,就需要引入es6/babel的垫片。
@mishe 请教一下,beble是什么东西?没有搜到这个东西
@Plortinus 拼写错误,是buble 参考:https://gitlab.com/Rich-Harris/buble
mark
中英文求加空格
mark
赞👍
mark
还是没懂怎么保证const不能在赋值的,编译时怎么实现的?
好文
还是没懂怎么保证const不能在赋值的,编译时怎么实现的?
就是直接编译不过去
原文链接
前言
将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法。但你就这么放心地让babel跑在外网?反正我是不放心,我就曾经过被坑过,于是萌生了研究babel代码转换的想法。本文不是分析babel源码,仅仅是看看babel转换的最终产物。
es6在babel中又称为es2015。由于es2015语法众多,本文仅挑选了较为常用的一些语法点,而且主要是分析babel-preset-2015这个插件(react开发的时候,常在webpack中用到这个preset)。
babel-preset-2015
打开babel-preset2015插件一看,一共20个插件。熟悉es2015语法的同志一看,多多少少能从字面意思知道某个插件是用于哪种语法的转换
var, const and let
const和let现在一律转换成var。那const到底如何保证不变呢?如果你在源码中第二次修改const常量的值,babel编译会直接报错。 转换前
转换后:
那let的块级作用怎么体现呢?来看看下面例子,实质就是在块级作用改变一下变量名,使之与外层不同。 转换前:
转换后:
赋值解构
写react的时候,我们使用负值解构去取对象的值,用起来非常爽,像这样:
我们来看看转换的结果:
至于数组呢?如果是一个匿名数组,则babel会帮你先定义一个变量存放这个数组,然后再对需要赋值的变量进行赋值。 转换前:
转换后:
看到这个,感觉转换结果跟我们想的还蛮一致。哈哈,使用的噩梦还没开始。
如果使用匿名对象直接进行赋值解构会怎样呢?如下。babel为了使接收的变量唯一,直接就将匿名对象里的属性拼在一起,组成接收这个匿名对象的变量,吓得我赶紧检查一下项目里有没有这种写法。 转换前:
转换后:
还有一种对象深层次的解构赋值: 转换前:
转换后:
babel在代码顶部生产了一个公共的代码_slicedToArray。大概就是将对象里面的一些属性转换成数组,方便解构赋值的进行。但Symbol.iterator的兼容性并不好(如下图),还是谨慎使用为妙。
另外,下面这种对字符串进行赋值解构也同样使用到_slicedToArray方法:
函数参数默认值及扩展运算符
在es5的年代,一般我们写参数的默认值都会这么写:
我们来看看babel的转换办法:
babel这里使有了arguments来做判。第一种情况涉及解构赋值,因此x和y的值还是有可能是undefined的。至于第二种情况,则会保证2个参数的默认值分别是1和2.
再来看一种。...y代表它接收了剩下的参数。也就是arguments除了第一个标号的参数之外剩余的参数。 转换前:
转换后:
箭头函数
剪头函数其实主要是省了写函数的代码,同时能够直接用使外层的this而不用担心context切换的问题。以前我们一般都要在外层多写一个_this/self直向this。babel的转换办法其实跟我们的处理无异。 转换前:
转换后:
对象的能力增强
对象属性的快捷定义
转换前:
转换后:
对象中括号属性
es2015开始新增了在对象中用中括号解释属性的功能,这对变量、常量等当对象属性尤其有用。 转换前:
转换后:
看似简单的属性,babel却大动干戈。新增了一个_defineProperty函数,给新建的_obj = {}进行属性定义。除此之外使用小括号包住一系列从左到右的运算使整个定义更简洁。
使用super去调用prototype
以前我们一般都用obj.prototype或者尝试用this去往上寻找prototype上面的方法。而babel则自己写了一套在prototype链上寻找方法/属性的算法。 转换前
转换后:
Object.assign 和 Object.is
es6新增的Object.assign极大方便了对象的克隆复制。但babel的es2015 preset并不支持,所以没对其进入转换,这会使得一些移动端机子遇到这种写法会报错。所以一般开发者都会使用object-assign这个npm的库做兼容。
Object.is用于比较对象的值与类型,es2015 preset同样不支持编译。
es6模板
多行字符串
转换前:
转换后:
字符中变量运算
转换前:
转换后:
标签模板
es6的这种新特性给模板处理赋予更强大的功能,一改以往对模板进行各种replace的处理办法,用一个统一的handler去处理。babel的转换主要是添加了2个属性,因此看起来也并不算比较工程浩大的编译。 转换前:
转换后:
模块化与类
类class
javascript实现oo一直是非常热门的话题。从最原始时代需要手动维护在构造函数里调用父类构造函数,到后来封装好函数进行extend继承,再到babel出现之后可以像其它面向对象的语言一样直接写class。es2015的类方案仍然算是过渡方案,它所支持的特性仍然没有涵盖类的所有特性。目前主要支持的有:
转换前:
转换后(由于代码太长,先省略辅助的方法):
先前在用react重构项目的时候,所有的react组件都已经摒弃了es5的写法,一律采用了es6。用类的好处写继续更加方便,但无法用mixin,需要借助更新的es7语法中的decorator才能够实现类mixin的功能(例如pureRender)。但这次分析完babel源码之后,才发现原来babel在实现class特性的时候,定义了许多方法,尽管看起来并不太优雅。
模块化
在开发react的时候,我们往往用webpack搭配babel的es2015和react两个preset进行构建。之前看了一篇文章对babel此处的模块加载有些启发(《分析 Babel 转换 ES6 module 的原理》)。
示例:
通过webpack与babel编译后:
es6的模块加载是属于多对象多加载,而commonjs则属于单对象单加载。babel需要做一些手脚才能将es6的模块写法写成commonjs的写法。主要是通过定义__esModule这个属性来判断这个模块是否经过babel的编译。然后通过_interopRequireWildcard对各个模块的引用进行相应的处理。
另一个发现是,通过webpack打包babel编译后的代码,每一个模块里面都包含了相同的类继承帮助方法,这是开发时忽略的。由此可看,在开发react的时候用es5的语法可能会比使用es6的class能使js bundle更小。
babel es2015 loose mode
开发家校群的时候,在android4.0下面报esModule错误的问题,如下:
经查证,发现是构建中babel-es2015 loader的模式问题,会导致Android4.0的用户有报错。只需要使用loose mode就可以解决问题。下面是相关的stackoverflow issue以及对应解决问题的npm包。
那么es2015和normal mode和loose mode有什么区别呢,这个出名的博客略有介绍:Babel 6: loose mode。
实质就是(作者总结)normal mode的转换更贴近es6的写法,许多的property都是通过Object.defineProperty进行的。而loose mode则更贴近es5的写法,性能更好一些,兼容性更好一些,但将这部份代码再转换成native es6的话会比较麻烦一些(感觉这一点并不是缺点,有源码就可以了)。
上面esModule解决的办法,实质就是将
改成
exports.__esModule = true;
再举个例子,如下面的Cat类定义:
正常模式会编译为:
loose mode模式会编译为:
babel es2015中loose模式主要是针对下面几个plugin:
每一种的转换方式在此就不再赘述了,大家可以回家自己试。
如有错误,恳请斧正!
参考文章:
babel try out
template literals
block-scoped functions
classes
objects
commonjs and es6 module