ES6有很多新的特性,让我们的代码更加简洁方便,下面是一些我觉得会很常用的特性:

众所周知,var的作用域只能用函数进行隔离,而无法像其它语言中大括号进行作用域隔离,所以经常会看到立即函数的身影。`let的出现可以稍微地控制这一情形:

1
2
3
4
{
let val = 34;
}
console.log(val); // Uncaught ReferenceError: val is not defined

一般的编程语言中,常量是可以在语法层面进行控制的,但在ES6以前,我们只能通过约定变量名之类的方法来告诉使用者这是一个常量,但本质上还是无法从语法层面保障这个问题。const可以在语法层面使得变量成为只读变量,弥补var的不足之处。事实上,我们也可以使用ES5来模拟这一特性,使用Object.defineProperty来设置,将 Object.defineProperty中的configurablewritable都设为 false 就可以了。

1
const PI = Math.PI; // 事实上 Math.PI 本身就是可读的

ES6中,提供了箭头函数这个概念:匿名函数现在可以使用箭头函数来替代,一方面使得代码更简洁,另一方面,它保持了this的作用域。

1
2
3
4
5
6
7
8
9
10
11
var odds = evens.map(v => v + 1);
// 保持this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}

通常情况下,如果要遍历函数参数,需要用到arguments这个变量。但是,现在我们可以使用ES6提供的尾参数(rest parameters)来精简这个过程:

1
2
3
4
5
function func(id, ...rest) {
console.log(id, rest)
}
// output: A [ 'B', 'C', 123 ]
func('A', 'B', 'C', 123)

带默认参数的函数对于整洁代码至关重要,可惜之前 JS 并不支持,以至于我们经常会见到param = param !== 'undefined' || 'default-value'这样的代码。好消息是,ES6现在支持默认参数啦:

1
2
3
4
f = (x, y = 12) => {
return x + y;
}
f(3) === 15

解构(destructuring)是一个比较陌生的概念,但非常地实用,它可以将一个数组中的元素按顺序分别赋给不同变量(试想一下多返回值函数):

1
2
3
4
5
// actually send_ajax returns an array contains [Promise, Error]
[promise, err] = send_ajax();
if (err) {
// do something
}

在使用函数参数的时候,我们甚至可以使用如下方式:

1
2
3
4
5
6
function run({name: N}) {
console.log(N);
}
run({
name: 'Jiaxiang Zheng'
})

JS的面向对象最让人诟病的地方就是没有类,它使用原型继承,是可以模拟出类的,但每次都要加一长串的.prototype.难免让人觉得繁琐。ES6支持类的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Bird extends Animal {
constructor(id) {
super(id); // 调用Animal的构造函数
this.type = 'bird';
}
sing() {
console.log('I am a bird and sing a song')
}
static version() {
return '0.0.1';
}
// ES7特性
static VERSION = '0.0.1'
}

构造函数是通过constructor定义的,如果Animal实现了sing方法,我们可以通过super.sing()来调用。如果Animal包含一个Bird并没有重载的方法method,那么Bird中可以通过this.method直接调用。

字符串模板可以让我们将变量嵌入到字符串中,得到动态的字符串:

1
2
let name = 'jiaxzheng';
`Hello ${name}, how are you?`

模块化的概念在前端中讨论得一直比较火热,如AMDCommonJS规范等。ES6从语言层面支持了模块化,具体的运行期行为还需依运行环境而定。但默认情况下, 使用的是异步模块。

1
2
3
4
5
6
7
8
9
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import {sum, pi} from 'lib/math'
export default pi; // 这样在引用的时候可用 import pi from 'XXX'

一共有如下import的方式:

1. import * as util from './util' // 引入除export default以外的内容
2. import util from './util'  // 引入export default中的内容
3. import {A, B} from './util'  // 引入export中名为 A, B 的对象

最后,提一个已经被说烂的概念:Promise的支持。

1
2
3
4
5
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}

上面说了这么多,那我们要怎么开始使用ES6的这些特性呢?在浏览器里直接跑这些代码可是会报错的。

新版的Node已经支持了诸多上述特性,我们可以使用node file --harmony直接体验起来。但是还是有些特性是不支持的,另外浏览器环境也可能会报错。为此,我们可以考虑使用babel转码器进行转换,将这些ES6代码转换成兼容的ES5代码。

下面是一个典型的Gulpfile配,具体的babel选项可以参考官网链接。注意,它仅仅只是输出转码后的结果,并不会对文件进行合并等操作,如果需要作更多的操作,需要使用browserify或webpack进行合并压缩:

1
2
3
4
5
6
7
8
9
10
// gulpfile.js
gulp.task('transpile', function () {
return gulp.src(SRC).
pipe(babel({
sourceMaps: true,
stage: 0,
modules: "common" // amd | umd
})).
pipe(gulp.dest(DEST))
})

2015.10.03 update:
有关更深入的ES6介绍,可以移步:http://ponyfoo.com/articles/tagged/es6-in-depth