随着 React 的风靡,配合 Webpack 以及 Babel 等技术,越来越多的前端同学将 ECMAScript 2015(ES6) 的特性运用在项目中,import、export、class、箭头函数、块级作用域等特性屡试不爽。而对于 Node.js 实现的后台代码来说,我们也同样希望使用这些 ES6 特性,下面将以 v4.4.4(LTS version) 长期支持版本为例展开话题,从兼容性以及性能两方面着手分析 Node.js 对 ES6 的支持情况。
兼容性
随着 io.js 的引入,新版的 Node.js 开始原生支持部分 ES6 的特性,既然 ES6 在浏览器端使用需要通过 babel 等编译,在 Node.js 总可以放心使用了吧,然而事实并非如此。
所有的 ES6 特性被划分为三个级别:
- shipping:已经分发并默认开启的特性;
- staged:已经分发,但需要使用 –harmony 参数开启的特性;
- in progress:开发中,还不稳定或者未实现的特性,不推荐使用;
Nodejs 各个版本对 ES6 特性的兼容列表见:http://node.green/
【shipping】
ES6 特性 | Nodejs 兼容性 |
---|---|
let,const, 块 | strict 模式支持 |
class 类 | strict 模式支持 |
Map,Set 和 WeakMap,WeakSet | 支持 |
generators | 支持 |
进制转换 | 支持 |
对象字面量扩展 | 支持 |
promise | 支持 |
String 对象新 API | 支持 |
symbols | 支持 |
字符串模板 | 支持 |
这些都是 Node.js 原生支持的特性,除了前两个特性需要在代码前面添加'use strict'。
【staged】
- Symbol.toStringTag
- Array.prototype.includes
- Rest Parameters
- ......
可通过 node --v8-options | findstr harmony 进行查看。在执行带有这些特性的 js 代码时,需要加上--harmony 参数,例如:node --harmony app.js。
【in progress】
- harmony_modules (enable "harmony modules")
- harmonyarrayincludes (enable "harmony Array.prototype.includes")
- harmony_regexps (enable "harmony regular expression extensions")
- harmonynewtarget (enable "harmony new.target")
- ......
可通过 node --v8-options | findstr "in progress" 进行查看。这些特性是那些将要支持(但具体时间未知)的特性,不建议使用。
Node.js 6.x 已经支持 93% 的 ES6 特性
从上面分析可以看到,Node.js 4.x 版本对 ES6 特性的原生支持并不好,但是好消息是,Node.js 6.x LTS version 将要发布了,带来了性能的大幅提升、更好的测试、更完善的文档、更好的安全性,并广泛支持了 ES6。
在 Node.js 5.0 发布了 6 个月以后,6.0 的时代马上就来了,目标是替换 4.x,计划在 2016 年 10 月成为下一个 LTS version(长期支持的版本),5.0 只是一个过渡版本。考虑到产品的稳定性,建议大家目前还是继续使用 Node.js 4.x,直到 10 月份。
Node.js 6.x 的一项重要改进是使用了 V8 5.0 引擎,支持了 93% 以上的 ES6,包括 destructuring、rest 参数、class 和 super 关键字,ES6 还没有被覆盖到的只剩下一小部分,包括 direct、mutual recursion、iterator closing 等。
让我们一起期待吧!
性能
ES6 是大势所趋,尽管目前 Node 下使用 ES6 仍然存在很多问题,这里还是有必要对 ES6 的原生性能做一下对比测试,好让大家有个量化的概念。
环境描述:
CPU:Intel(R) Core(TM)i7-4790 CPU @ 3.60GHz
RAM:16.0GB
操作系统:64bit
node 版本:v4.4.4
1. 块级作用域
测试代码如下:
ES5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var i = 0; var start = +new Date(), duration; while(i++ < 1000000){ var a = 1; var b = '1'; var c = true; var d = {}; var e = []; } duration = +new Date() - start; console.log(duration); |
输出结果为 45ms。
ES6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
'use strict' let i = 0; let start = +new Date(), duration; while(i++ < 1000000){ const a = 1; const b = '1'; const c = true; const d = {}; const e = []; } duration = +new Date() - start; console.log(duration); |
输出结果为 29ms。
可见,使用 let,const 声明变量的速度比 var 快了约 55% 左右。
2.class
测试代码如下:
ES5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var i = 0; var start = +new Date(), duration; function A(){ this.name = '小强'; } A.prototype.getName = function() { return this.name; }; while(i++ < 100000){ var a = new A(); a.getName(); } duration = +new Date() - start; console.log(duration); |
输出结果为 2ms。
ES6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
'use strict' let i = 0; let start = +new Date(), duration; class A{ constructor() { this.name = '小强'; } getName(){ return this.name; } } while(i++ < 100000){ const a = new A(); a.getName(); } duration = +new Date() - start; console.log(duration); |
输出结果为 974ms。
可见,class 没有丝毫优势,function 真是快的不得了。
3.Map
测试代码如下:
ES5:
1 2 3 4 5 6 7 8 9 10 11 12 |
var i = 0; var start = +new Date(), duration; while(i++ < 1000000){ var map = {}; map['key'] = 'value'; } duration = +new Date() - start; console.log(duration); |
输出结果为 11ms。
ES6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
'use strict' let i = 0; let start = +new Date(), duration; while(i++ < 1000000){ let map = new Map(); map.set('key','value'); } duration = +new Date() - start; console.log(duration); |
输出结果为 151ms。
建议如果不是必须情况,不要使用 Map 等复杂类型。
4. 字符串模板
测试代码如下:
ES5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var i = 0; var start = +new Date(), duration; var vars = { name: 'haha', address: 'tencent' } while(i++ < 1000000){ var str = 'string text ' + vars.name + 'string '+ vars.address; } duration = +new Date() - start; console.log(duration); |
输出结果为 8ms。
ES6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
'use strict' let i = 0; let start = +new Date(), duration; let vars = { name: 'haha', address: 'tencent' }; while(i++ < 1000000){ let str = `string text ${vars.name} string ${vars.address}`; } duration = +new Date() - start; console.log(duration); |
输出结果为 50ms。
字符串模板在执行时由于会首先找出其中的模板变量,所以性能也是大不如 ES5 的字符串拼接。
其他特性有兴趣的同学可以一一做测试。
总结
对于 ES6 来说,我们不仅要了解其特性的兼容性,也要从性能上做到心中有数,从上面的测试结果可以看到,大部分 ES6 新特性相对 ES5 原生方法要慢得多,但是我依然坚信,ES6 是未来的趋势,随着 Node 版本的更新,相信这些兼容性以及性能问题在不久的将来都将迎刃而解。
Garyxt 2017 年 4 月 30 日
2017-04-30
Ubuntu 16.4 LTS 64bit 虚拟机
内存:5.8 GiB
CPU:Intel® Core™ i5-4570 CPU @ 3.20GHz × 4
测试结果如下:
ES5 BlockScope perf:18
ES6 BlockScope perf:18
ES5 Class perf:3
ES6 Class perf:5
ES5 Map perf:16
ES6 Map perf:117
ES5 String perf:6
ES6 String perf:20
Garyxt 2017 年 4 月 30 日
node -v 6.10.2
Garyxt 2017 年 4 月 30 日
将 while 循环换成如下:
// es5
var map = {};
while (i++ < 1000000) {
map[i] = ‘value’;
}
// es6
let map = new Map();
while (i++ < 1000000) {
map.set(i, ‘value’);
}
// 结果
ES5 Map perf:51
ES6 Map perf:460
TAT.李强 2017 年 6 月 22 日
这类文章都有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,当时我测试还是用的 4.x 的 LTS 版本呢,感谢回复。关于 map 实例比较多的情况,单一实例当时对测试没意义,可以根据自己的意愿改变测试用例,看自己需求了,只要保证 es5 和 es6 测试要点一致即可。再次感谢回复
GoIn 2017 年 1 月 22 日
‘use strict’let i = 0;let start = +new Date(), duration; class A{ constructor() { this.name = ‘ 小强’; } getName(){ return this.name; }} while(i++ < 100000){ const a = new A(); a.getName();} duration = +new Date() – start;console.log(duration);Node 6.9 测试为 2-3mm
TAT.李强 2017 年 6 月 22 日
这类文章都有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,当时我测试还是用的 4.x 的 LTS 版本呢,感谢回复。
lizzy 2016 年 12 月 14 日
临记 2016 年 11 月 26 日
map 不应该在循环中 new, map 的测试应该是这样测 function randomString(){ var text = “”; var possible = “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz”; for( var i=0; i < 5; i++ ) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text;}var start = +new Date(), duration;var map2 = new Map(); // var obj2 = {};for (var i=0; i < 100000; i++) { var key = randomString(); var val = randomString(); map2.set(key, val); // obj2[key] = val;}duration = +new Date() – start;console.log(duration); 这样 es6 中 map 的 set 方法要比对 object 的 key 赋值快一倍。
游客 2016 年 11 月 26 日
mac 上除了 Map 之外速度并没有太大差异。
TAT.李强 2017 年 6 月 22 日
node 版本差异,这类文章都有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,当时我测试还是用的 4.x 的 LTS 版本呢,感谢回复。
javascript爱好者 2016 年 11 月 26 日
这样的测试不准确,跟很多评论者一样,我怀疑是楼主的电脑配置环境导致速度差异较大,实际上即使在最低配的 Macbook Air 上运行你的测试,ES5 和 ES6 时间都差不多。
TAT.李强 2017 年 6 月 22 日
主要差异还是在 node 版本。同样环境下验证数据可能会不一致,但是问题都会暴漏出来的。
xiangnaier 2016 年 11 月 17 日
好文章
Cowboy 2016 年 10 月 24 日
Win10 64 Pro Node.js 6.9.11. 块级作用域第 1 次 ES5: 21ms,ES6: 12ms 之後 ES5: 10ms,ES6: 11ms2.ClassES5: 2ms,ES6: 3ms3.MapES5: 7ms,ES6: 75ms4. 字符串模板 ES5: 5ms,ES6: 14ms
TAT.李强 2017 年 6 月 22 日
这类文章有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,当时我测试还是用的 4.x 的 LTS 版本呢,感谢回复。
极乐网 2016 年 9 月 12 日
好文章,赞!我能转到我的网站吗?http://www.dreawer.com
呵呵 2016 年 8 月 19 日
腾讯的就这点水平?这个测试方法,无语!!!
黑白头像的悲哀 2016 年 8 月 27 日
我见过很多人这样测试,有何高招,请指教
TAT.李强 2017 年 6 月 22 日
这里最后给出的数值都是跑出来的平均值,不同的环境数据可能会不一致,但是结论是一致的,取决于 node 版本。
洛丨小嘻 2016 年 8 月 1 日
测试环境在 mac 下,时间都在 10ms 以内,怀疑是否 windows 环境造成巨大差异
Tony_Zixuan 2016 年 8 月 10 日
我也是 mac 环境,差别不是很大,有时候 ES5 快过 ES6 也是有的
TAT.李强 2017 年 6 月 22 日
这类文章时效性比较强,文中也说了随着 node 版本的改进性能和兼容性将不是问题,请确保 node 版本,当时我测试还是用的 4.x 的 LTS 版本呢,文中有特意说明 node 环境,感谢回复。
TAT.李强 2017 年 6 月 22 日
这类文章有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,请确保 node 版本,当时我测试还是用的 4.x 的 LTS 版本呢,文中有特意说明 node 环境,感谢回复。
xingkai 2016 年 7 月 27 日
Map Set 适合处理大量数据,而不是 new 出好多对象…new 的过程当然要慢些
TAT.李强 2017 年 6 月 22 日
测试用力自己按需写就好,我的应用场景有别于你的需求。
WEI 2016 年 7 月 27 日
你们的 aeditor 不开源吗?求源码啊。
Milo 2016 年 7 月 22 日
es6 语法很多都和 java 持平了~一门语言的诞生都这样大同小异么?
luciy 2016 年 7 月 19 日
测试楼主的发布的实例发现 ES5 和 ES6 代码运行时间几乎相同,是否多开几个环境测试下,保证结果准确。你这样有误导群众的嫌疑。
TAT.李强 2017 年 6 月 22 日
这类文章都有时效性的,文中也说了随着 node 版本的改进性能和兼容性将不是问题,请确保 node 版本,当时我测试还是用的 4.x 的 LTS 版本呢,文中有特意说明 node 环境,不同的环境数据可能会不一致,但是结论是一致的,取决于 node 版本。感谢回复。
幻天芒 2016 年 7 月 19 日
这种方式测试性能,并不太可取吧。比如第三条,一般场景是使用 Map,然后使用它的 get 和 set 方法。应该在这里面去比较它的性能更合理些。
TAT.李强 2017 年 6 月 22 日
测试用例自己按需写就好,我的应用场景有别于你的需求。
WEI 2016 年 7 月 17 日
最近发现很多 H5 把资源都压在一个文件里面了,能不能给点思路?
多蒙菜 2016 年 7 月 19 日
webpack
沈联民 2016 年 7 月 15 日
赞
test 2016 年 7 月 5 日
好文章,赞