ECMAScript 6 (ES6) 新特性介绍 三
原文地址 ECMAScript 6 — New Features: Overview & Comparison
本文介绍 ECMAScript 6中的新特性并通过具体代码演示与ECMAScript 5的差别。译者额外增加了一些例子和说明 .
ECMAScript 6 是2015年推出的第6版本的ECMAScript标准(即Javascript标准),同时也被成为ECMAScript 2015.
ECMAScript 6 中定了了许多新的Javascript特性,包括新的类和模块,类似python的generators和generators表达式, 箭头函数, 二进制数据, 类型数组, 集合类型(maps, set & 弱maps), promises, reflection, proxy等。它也被称作 ES6 Harmony. (和谐ES6)
本文是该系列文章的第三篇
Symbol 类型
Symbol类型
作为对象的一个唯一而且不可修改的属性. Symbol是个可选的描述,只是用在调试中。
ECMAScript 6的实现
Symbol("foo") !== Symbol("foo")
const foo = Symbol()
const bar = Symbol()
typeof foo === "symbol"
typeof bar === "symbol"
let obj = {}
obj[foo] = "foo"
obj[bar] = "bar"
JSON.stringify(obj) // {}
Object.keys(obj) // []
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [ foo, bar ]
对比ECMAScript 5的实现
// ES5中无该实现
全局Symbol
全局的Symbol是通过唯一的键来标志的
ECMAScript 6的实现
Symbol.for("app.foo") === Symbol.for("app.foo")
const foo = Symbol.for("app.foo")
const bar = Symbol.for("app.bar")
Symbol.keyFor(foo) === "app.foo"
Symbol.keyFor(bar) === "app.bar"
typeof foo === "symbol"
typeof bar === "symbol"
let obj = {}
obj[foo] = "foo"
obj[bar] = "bar"
JSON.stringify(obj) // {}
Object.keys(obj) // []
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [ foo, bar ]
对比ECMAScript 5的实现
// ES5中无该实现
迭代器 (Iterators)
迭代器和For-Of 操作符
通过“迭代”协议支持对象自定义它们的迭代行为。
同时支持通过“迭代”协议来生成一些列的结果值 (无论有限还是无限的)
最后,提供了方便的操作符(For-of)遍历一个迭代对象的所有值.
ECMAScript 6的实现
let fibonacci = {
[Symbol.iterator]() { // 声明迭代器
let pre = 0, cur = 1
return {
next () { // 迭代器每次迭代的函数
[ pre, cur ] = [ cur, pre + cur ]
return { done: false, value: cur } // 每次迭代值返回
}
}
}
}
for (let n of fibonacci) { // 这里n的值是在 二 中介绍的释放赋值
if (n > 1000)
break
console.log(n)
}
对比ECMAScript 5的实现
var fibonacci = { // 译者注: 还是觉得ES5这种写法更直观,迭代器是ES6中才引入的,之后怎么变化还未知
next: (function () {
var pre = 0, cur = 1;
return function () {
tmp = pre;
pre = cur;
cur += tmp;
return cur;
};
})()
};
var n;
for (;;) {
n = fibonacci.next();
if (n > 1000)
break;
console.log(n);
}
生产者
生产者模式,迭代协议
支持迭代形式的生产函数,通过控制流程的暂停和恢复来产生一些列的值 (无论有限还是无限)
ECMAScript 6的实现
let fibonacci = {
*[Symbol.iterator]() { // * 来标示生产者
let pre = 0, cur = 1
for (;;) {
[ pre, cur ] = [ cur, pre + cur ]
yield cur
}
}
}
for (let n of fibonacci) {
if (n > 1000)
break
console.log(n)
}
对比ECMAScript 5的实现
var fibonacci = {
next: (function () {
var pre = 0, cur = 1;
return function () {
tmp = pre;
pre = cur;
cur += tmp;
return cur;
};
})()
};
var n;
for (;;) {
n = fibonacci.next();
if (n > 1000)
break;
console.log(n);
}
直接使用生产者函数
支持生产者函数,一种特别的函数, 通过控制流程的暂停和恢复来产生一些列的值 (无论有限还是无限)
ECMAScript 6的实现
function* range (start, end, step) { // * 标示这是一个生产者函数
while (start < end) {
yield start // 通过yieldl来输出中间结果并暂停
start += step // 恢复生产者的执行
}
}
for (let i of range(0, 10, 2)) { // 对生产者函数的使用
console.log(i) // 0, 2, 4, 6, 8
}
对比ECMAScript 5的实现
// ES5中无相对应的实现
生产者匹配
支持生产者函数产生的值分配到各种可能的情况中去
ECMAScript 6的实现
let fibonacci = function* (numbers) { // 定义生产者函数
let pre = 0, cur = 1
while (numbers-- > 0) {
[ pre, cur ] = [ cur, pre + cur ]
yield cur
}
}
for (let n of fibonacci(1000)) // 使用在数组遍历中
console.log(n)
let numbers = [ ...fibonacci(1000) ] // 使用在数组赋值中
let [ n1, n2, n3, ...others ] = fibonacci(1000) // 使用在数组释放赋值中
对比ECMAScript 5的实现
// 无对应的ES5实现
生产者流程控制
支持生产者, 一种可以控制流程暂停/恢复的特殊迭代器, 以协作支持和Promises这样的异步编程. [请注意,以下的async函数通常使用已经存在的库,例如co 或者Bluebird ]
ECMAScript 6的实现
// 生成异步的流程控制驱动函数
function async (proc, ...params) {
var iterator = proc(...params) // proc 是一个生产者函数 (带*)
return new Promise((resolve, reject) => {
let loop = (value) => {
let result
try {
result = iterator.next(value)
}
catch (err) {
reject(err)
}
if (result.done)
resolve(result.value)
else if ( typeof result.value === "object"
&& typeof result.value.then === "function")
result.value.then((value) => {
loop(value)
}, (err) => {
reject(err)
})
else
loop(result.value)
}
loop()
})
}
// application-specific asynchronous builder
function makeAsync (text, after) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(text), after)
})
}
// application-specific asynchronous procedure
async(function* (greeting) {
let foo = yield makeAsync("foo", 300)
let bar = yield makeAsync("bar", 200)
let baz = yield makeAsync("baz", 100)
return `${greeting} ${foo} ${bar} ${baz}`
}, "Hello").then((msg) => {
console.log("RESULT:", msg) // "Hello foo bar baz"
})
对比ECMAScript 5的实现
// ES5中无相对应的实现
生产者方法
类或者对象中也支持生产者的方法
ECMAScript 6的实现
class Clz {
* bar () { // 类中的生产者方法
…
}
}
let Obj = {
* foo () { // 对象中的生产者方法
…
}
}
对比ECMAScript 5的实现
// ES5中无相对应的实现
Map/Set 和WeakMap WeakSet
Set 数据格式
新增Set数据格式, 更清晰/方便的操作集合
ECMAScript 6的实现
let s = new Set()
s.add("hello").add("goodbye").add("hello") // 添加成员
s.size === 2 // 成员数量
s.has("hello") === true // 成员存在
for (let key of s.values()) // 遍历成员 - 通过添加的顺序(FIFO)
console.log(key)
对比ECMAScript 5的实现
// set的出现的确让之前这类的操作便利性增加不少!
var s = {};
s["hello"] = true; s["goodbye"] = true; s["hello"] = true;
Object.keys(s).length === 2;
s["hello"] === true;
for (var key in s) // arbitrary order
if (s.hasOwnProperty(key))
console.log(s[key]);
Map数据格式
新增Map数据格式, 更清晰/方便的操作数据映射
ECMAScript 6的实现
// 增加了几个方案的map操作,但总体上和之前的对象属性并无太大差别
let m = new Map()
let s = Symbol()
m.set("hello", 42)
m.set(s, 34)
m.get(s) === 34
m.size === 2
for (let [ key, val ] of m.entries())
console.log(key + " = " + val)
对比ECMAScript 5的实现
var m = {};
// ES5中无
m["hello"] = 42;
// ES5中无
// ES5中无
Object.keys(m).length === 2;
for (key in m) {
if (m.hasOwnProperty(key)) {
var val = m[key];
console.log(key + " = " + val);
}
}
弱指向的数据格式
对 Set和Map而言可以在前面增加关键字Weak, 这样能保证其中的对象只是引用关系,对象在WeakSet和WeakMap外发送的变化都会体现在其中。 能够保证无内存泄漏问题。
ECMAScript 6的实现
let isMarked = new WeakSet()
let attachedData = new WeakMap()
export class Node {
constructor (id) { this.id = id }
mark () { isMarked.add(this) }
unmark () { isMarked.delete(this) }
marked () { return isMarked.has(this) }
set data (data) { attachedData.set(this, data) }
get data () { return attachedData.get(this) }
}
let foo = new Node("foo")
JSON.stringify(foo) === '{"id":"foo"}'
foo.mark()
foo.data = "bar"
foo.data === "bar"
JSON.stringify(foo) === '{"id":"foo"}'
isMarked.has(foo) === true
attachedData.has(foo) === true
foo = null /* 释放foo 同时也释放了WeakSet和WeakMap中的foo */
attachedData.has(foo) === false
isMarked.has(foo) === false
对比ECMAScript 5的实现
// ES5中无
类型数组
类型数组
类型数组是一种从二进制的数组空间里构建的数组,这可以方便的用来实现各种网络协议,加密算法和文件格式的操作。
ECMAScript 6的实现
// ES6 下面例子中的Example类 同等于 这个c 的结构:
// struct Example { unsigned long id; char username[16]; float amountDue }
class Example {
constructor (buffer = new ArrayBuffer(24)) {
this.buffer = buffer
}
set buffer (buffer) {
this._buffer = buffer
this._id = new Uint32Array (this._buffer, 0, 1) // 取该buffer从第0字节开始,1个uint32长作为id
this._username = new Uint8Array (this._buffer, 4, 16) // 取该buffer的第4字节开始,开始共16个uint8长作为 username (字符数组)的空间
this._amountDue = new Float32Array(this._buffer, 20, 1) // 取该buffer中的第20字节开始,共1个float32长作为amountDue的空间。
}
get buffer () { return this._buffer }
set id (v) { this._id[0] = v } // 为什么不是._id = v? 因为_id指向的是数组,但数组真正存储的起点是在第一个元素处, 即 ._id[0] 下面其他的元素同理
get id () { return this._id[0] }
set username (v) { this._username[0] = v }
get username () { return this._username[0] }
set amountDue (v) { this._amountDue[0] = v }
get amountDue () { return this._amountDue[0] }
}
let example = new Example()
example.id = 7 //
example.username = "John Doe"
example.amountDue = 42.0
// 这是buffer内存的状况:
// 以16进制标示 每两位标示一个byte
// 0000 0007 (id)
// 4a6f 686e 2044 6f65 0000 0000 0000 0000 (John Doe)
// 4228 0000 (42.0)
对比ECMAScript 5的实现
// ES5中无