Devs

ECMAScript 6 (ES6) 新特性介绍 三

2017-12-15  本文已影响4人  乌龟怕铁锤

原文地址 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中无
上一篇下一篇

猜你喜欢

热点阅读