ECMAScript2015 新特性

image

1、简介

➤ ECMAScript是什么????

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。

➤ ECMAScript 5 、ECMAScript 6 和 ECMAScript 2015的关系?

ECMAScript 5是ECMAS在2009年推出的标准,简称ES5。 ECMAScript 6是ES5的升级,也被称为ECMAScript 2015,简称ES6。对javascript而言,ES6标准的推出无疑是一个里程碑。目前ES6基本成为业界标准,它的普及速度比 ES5 要快很多,主要原因是现代浏览器对 ES6 的支持友好。如 Chrome 和 Firefox 浏览器,已经支持 ES6 中绝大多数的特性。

ES6新特性

➤ 箭头(Arrows) 和 关键字(This)

箭头(Arrows)是一个函数的简写,使用括号包裹参数,跟随一个 =>,紧接着是函数体。这种写法类似c#java8CoffeeScript。支持表达式及语句体,与function不一样的是:=> 在其作用的代码块中能共享this对象,如果它嵌套在其他function中,它共享父函数的“variable”变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// 可以跟表达式
var odds = evens.map(v => v + 1);
// <----等价于---->
var odds = evens.map(function (v) {
return v + 1;
});

var nums = evens.map((v, i) => v + i);
// <----等价于---->
var nums = evens.map(function (v, i) {
return v + i;
});

//语句
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// <----等价于---->
nums.forEach(function (v) {
if (v % 5 === 0) fives.push(v);
});


// 关键字 this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
// <----等价于---->
var bob = {
_name: "Bob",
_friends: [],
printFriends: function printFriends() {
var _this = this;

this._friends.forEach(function (f) {
return console.log(_this._name + " knows " + f);
});
}
};

//arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}
return numbers;
};
return example();
}
// <----等价于---->
function square() {
var _arguments = arguments;

var example = function example() {
var numbers = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
for (var _iterator = _arguments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var number = _step.value;

numbers.push(number * number);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return numbers;
};
return example();
}

square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]

➤ 类(Classes)

在ES6中,类(classes)是面向对象模式的原型的一个简单的语法糖。用单一的方便的声明形式,使类模式更容易使用 ,类支持基于原型的继承(prototype-based inheritance)、超级调用(super calls)、实例(instance )、静态方法(static methods)和构造函数(constructors)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);

this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}

extends 允许一个子类继承父类,需要注意的是,子类的 constructor 函数中需要执行 super() 函数。

当然,你也可以在子类方法中调用父类的方法,如 super.parentMethodName()。

=> 更详细见 这里

➤ 增强的对象字面量
对象字面量扩展了:构造器中设置prototype值、简写(如:foo: foo)、定义函数以及超级调用等功能,这种设计使得对象字面量与class声明更贴近。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
// Sets the prototype. "__proto__" or '__proto__' would also work.
__proto__: theProtoObj,
// Computed property name does not set prototype or trigger early error for
// duplicate __proto__ properties.
['__proto__']: somethingElse,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ "prop_" + (() => 42)() ]: 42
};

注意: __proto__属性需要native支持, 它是被之前的 ECMAScript版本废弃。绝大部分引擎都支持这个属性(其他见这里).

➤ 模板字符串 (Template Strings)

模板字符串提供了构建字符串的语法糖,这很类似于Perl/Python等语言的字符串插入特性。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 基本的字符串
`This is a pretty little template string.`

// 多行字符串
`In ES5 this is
not legal.`

// 插入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// 非转义的字符串模板
String.raw`In ES5 "\n" is a line-feed.`

// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);

➤ 解构(Destructuring)

解构允许用模式匹配绑定,支持数组和对象。解构类似于标准的对象查找foo["bar"],当找不到时会返回undefined. 可以避免在对象赋值时产生中间变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 列表匹配
var [a, ,b] = [1,2,3];
a === 1;
b === 3;

// 对象匹配
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()

// 对象匹配简写
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()

// 可以用在参数位置
function g({name: x}) {
console.log(x);
}
g({name: 5})

// Fail-soft destructuring
var [a] = [];
a === undefined;

// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;

// 解构 defaults arguments
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23

➤ Default + Rest + Spread 操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15

function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6

function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6

➤ let + const

let 是新的 var,let 允许创建块级作用域,ES6 推荐在函数中使用 let 定义变量,而非 var。 const 声明的常量类似于指针,它指向某个引用,也就是说这个「常量」并非一成不变的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

function f() {
{
let x;
{
// this is ok since it's a block scoped name
const x = "sneaky";
// error, was just defined with `const` above
x = "foo";
}
// this is ok since it was declared with `let`
x = "bar";
// error, already declared above in this block
let x = "inner";
}
}

有几个点需要注意:

  • let 关键词声明的变量不具备变量提升(hoisting)特性
  • letconst 声明只在最靠近的一个块中(花括号内)有效
  • 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
  • const 在声明时必须被赋值

for..of & for..in

for...of用于遍历一个迭代器,如数组:

1
2
3
4
5
6
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {
console.log(nickname);
}
Result: di, boo, punkeye

for...in 用来遍历对象中的属性:

1
2
3
4
5
6
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {
console.log(nickname);
}
Result: 0, 1, 2, size

➤ 迭代器(Iterators)

迭代器允许每次访问数据集合的一个元素,当指针指向数据集合最后一个元素是,迭代器便会退出。它提供了 next() 函数来遍历一个序列,这个方法返回一个包含 donevalue 属性的对象。

ES6 中可以通过 Symbol.iterator 给对象设置默认的遍历器,无论什么时候对象需要被遍历,执行它的 @@iterator 方法便可以返回一个用于获取值的迭代器。

数组默认就是一个迭代器:

1
2
3
4
5
6
7
var arr = [11,12,13];
var itr = arr[Symbol.iterator]();
itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }
itr.next(); // { value: undefined, done: true }
//你可以通过 [Symbol.iterator]() 自定义一个对象的迭代器。

➤ Generators

Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值。
在使用中你会看到 * 语法和一个新的关键词 yield:

1
2
3
4
5
6
7
8
9
10
function *infiniteNumbers() {
var n = 1;
while (true){
yield n++;
}
}
var numbers = infiniteNumbers(); // returns an iterable object
numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }

每次执行 yield 时,返回的值变为迭代器的下一个值。

➤ Promises

ES6Promise 有了原生的支持,一个 Promise 是一个等待被异步执行的对象,当它执行完成后,其状态会变成 resolved 或者 rejected

1
2
3
4
5
6
7
8
9
var p = new Promise(function(resolve, reject) {
if (/* condition */) {
// fulfilled successfully
resolve(/* value */);
} else {
// error, rejected
reject(/* reason */);
}
});

每一个 Promise 都有一个 .then 方法,这个方法接受两个参数,第一个是处理 resolved 状态的回调,一个是处理 rejected 状态的回调:

1
2
p.then((val) => console.log("Promise Resolved", val),
(err) => console.log("Promise Rejected", err));

➤ Unicode

es6 支持所有的unicode, 包括新的unicode形式和 正则u模式,以及新的api来处理字符串在21位代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// same as ES5.1
"𠮷".length == 2

// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2

// new form
"\u{20BB7}" == "𠮷" == "\uD842\uDFB7"

// new String ops
"𠮷".codePointAt(0) == 0x20BB7

// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}

➤ Modules

es6在语言级别支持模块的组建声明。把流行的JavaScript模块加载器进行系统化模式(AMD, CommonJS)。运行时行为由host-defined定义默认加载程序,隐式异步模型——没有代码执行,直到请求模块是可用的和处理。

1
2
3
4
5
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
1
2
3
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
1
2
3
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));

还有一些额外的特性包括export defaultexport *

1
2
3
4
5
6
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
1
2
3
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));

➤ Map + Set + WeakMap + WeakSet

高效的数据结构,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set

➤ Symbol

Symbol 是一种新的数据类型,它的值是唯一的,不可变的。ES6 中提出 symbol 的目的是为了生成一个唯一的标识符,不过你访问不到这个标识符:

1
2
var sym = Symbol( "some optional description" );
console.log(typeof sym); // symbol

注意,这里 Symbol 前面不能使用 new 操作符。
如果它被用作一个对象的属性,那么这个属性会是不可枚举的:

1
2
3
4
5
var o = {
val: 10,
[ Symbol("random") ]: "I'm a symbol",
};
console.log(Object.getOwnPropertyNames(o)); // val

如果要获取对象 symbol 属性,需要使用 Object.getOwnPropertySymbols(o)。

➤ Math + Number + String + Object APIs

es6增加了新的库,包括核心的Math库,ArrayObject.assign

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })
番外篇

underway now.
ES2015 standard
es6features
babeljs