写点什么

ECMAScript 2017(ES8) 新特性简介

发布于: 2021 年 03 月 17 日

简介

ES8 是 ECMA 协会在 2017 年 6 月发行的一个版本,因为是 ECMAScript 的第八个版本,所以也称为 ES8.


今天我们讲解一下 ES8 的新特性。


ES8 引入了 2 大特性和 4 个小的特性,我们接下来一一讲解。


Async 函数

我们在 ES6 中提到了 generator,Async 函数的操作和 generator 很类似。


我们看下 Async 的使用:


//Async 函数定义:async function foo() {}
//Async 函数表达式:const foo = async function () {};
//Async 方法定义:let obj = { async foo() {} }
//Async 箭头函数:const foo = async () => {};
复制代码

async 函数返回的是一个封装的 Promise 对象:


async function asyncFunc() {    return 123;}
asyncFunc().then(x => console.log(x)); // 123
复制代码

如果在函数中抛出异常,则会 reject Promise:


async function asyncFunc() {    throw new Error('Problem!');}
asyncFunc().catch(err => console.log(err)); // Error: Problem!
复制代码

上面的例子中我们在 async 函数使用的是同步的代码,如果想要在 async 中执行异步代码,则可以使用 await,注意 await 只能在 async 中使用。


await 后面接的是一个 Promise。如果 Promise 完成了,那么 await 被赋值的结果就是 Promise 的值。


如果 Promise 被 reject 了,那么 await 将会抛出异常。


async function asyncFunc() {    const result = await otherAsyncFunc();    console.log(result);}
// Equivalent to:function asyncFunc() { return otherAsyncFunc() .then(result => { console.log(result); });}
复制代码

我们可以顺序处理异步执行的结果:


async function asyncFunc() {    const result1 = await otherAsyncFunc1();    console.log(result1);    const result2 = await otherAsyncFunc2();    console.log(result2);}
// Equivalent to:function asyncFunc() { return otherAsyncFunc1() .then(result1 => { console.log(result1); return otherAsyncFunc2(); }) .then(result2 => { console.log(result2); });}
复制代码

也可以并行执行异步结果:


async function asyncFunc() {    const [result1, result2] = await Promise.all([        otherAsyncFunc1(),        otherAsyncFunc2(),    ]);    console.log(result1, result2);}
// Equivalent to:function asyncFunc() { return Promise.all([ otherAsyncFunc1(), otherAsyncFunc2(), ]) .then([result1, result2] => { console.log(result1, result2); });}
复制代码

最后看下如何处理异常:


async function asyncFunc() {    try {        await otherAsyncFunc();    } catch (err) {        console.error(err);    }}
// Equivalent to:function asyncFunc() { return otherAsyncFunc() .catch(err => { console.error(err); });}
复制代码

需要注意的是,如果 async 中返回的不是 Promise,那么将会被封装成为 Promise。如果已经是 Promise 对象的话,则不会被再次封装:


async function asyncFunc() {    return Promise.resolve(123);}asyncFunc().then(x => console.log(x)) // 123
复制代码

同样的,如果返回一个 rejected 的 Promise 对象,则和抛出异常一样的结果:


async function asyncFunc() {    return Promise.reject(new Error('Problem!'));}asyncFunc().catch(err => console.error(err)); // Error: Problem!
复制代码

如果你只是想触发异步方法,但是并不想等待它执行完毕,那么不使用 await:


async function asyncFunc() {    const writer = openFile('someFile.txt');    writer.write('hello'); // don’t wait    writer.write('world'); // don’t wait    await writer.close(); // wait for file to close}
复制代码

共享内存和原子操作

ES7 引入了一个新的构造函数 SharedArrayBuffer 和命名空间 Atomics。


在 JS 中,除了主线程之外,我们还可以创建 worker 线程,主线程和 worker 线程之间的通信是通过 postMessage 方法来进行的。


但是这样的通信方式并不高效。于是引入了 SharedArrayBuffer 这样的共享空间,来提升消息传输效率。


// main.js
const worker = new Worker('worker.js');
// To be sharedconst sharedBuffer = new SharedArrayBuffer( // (A) 10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements
// Share sharedBuffer with the workerworker.postMessage({sharedBuffer}); // clone
// Local onlyconst sharedArray = new Int32Array(sharedBuffer); // (B)
复制代码

上面的例子中,我们创建了一个 SharedArrayBuffer,并将这个 SharedArrayBuffer 通过 postMessage 的方式发给 worker。


我们知道 postMessage 是以拷贝的方式来发送消息的,但是这是正确使用共享的方式。


我看下在 worker 中怎么接收这个 Buffer:


// worker.js
self.addEventListener('message', function (event) { const {sharedBuffer} = event.data; const sharedArray = new Int32Array(sharedBuffer); // (A)
// ···});
复制代码

在 worker 中,我们将 sharedBuffer 使用 Int32Array 封装起来,作为 Array 而使用。


那么我们考虑一个问题,在使用 sharedBuffer 的过程中,会出现什么问题呢?


因为是共享的,所以可以在多个 worker 线程中同时被使用。如果在同时被使用的时候就会出现多线程共享数据的问题,也就是并发的问题。


为了解决并发的问题,我们回想一下在 java 中特别有一个 concurrent 包,里面有一些 Atomic 的类,可以执行原子性操作。


在 ES8 中,同样引入了 Atomics,用来进行 SharedArrayBuffer 的原子性操作。同时,使用 Atomics 还可以禁止重排序。


Atomics 实际操作的 Typed Array:Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array or Uint32Array。


注意,这些 Array 都是 SharedArrayBuffer 的封装 Array。并且都是 Int 的 Array(目前只支持 Int 类型)。


首先看下 Atomics 怎么解决数组的并发写入和读取的问题:


Atomics.load(ta : TypedArray<T>, index) : TAtomics.store(ta : TypedArray<T>, index, value : T) : TAtomics.exchange(ta : TypedArray<T>, index, value : T) : TAtomics.compareExchange(ta : TypedArray<T>, index, expectedValue, replacementValue) : T
复制代码

load 和 store 可以将 ta 作为一个整体来操作。


看下使用例子:


// main.jsconsole.log('notifying...');Atomics.store(sharedArray, 0, 123);
// worker.jswhile (Atomics.load(sharedArray, 0) !== 123) ;console.log('notified');
复制代码

Atomics 还提供了 wait 和 notity 功能:


Atomics.wait(ta: Int32Array, index, value, timeout)Atomics.wake(ta : Int32Array, index, count)
复制代码

当 ta[index]的值是 value 的时候,wait 将会使 worker 等待在 ta[index]之上。


而 wake,则是将等待在 ta[index]上的 count 个 worker 唤醒。


Atomics 还提供了一系列的操作:


Atomics.add(ta : TypedArray<T>, index, value) : TAtomics.sub(ta : TypedArray<T>, index, value) : TAtomics.and(ta : TypedArray<T>, index, value) : TAtomics.or(ta : TypedArray<T>, index, value) : TAtomics.xor(ta : TypedArray<T>, index, value) : T
复制代码

它相当于:


ta[index] += value;
复制代码

Atomic 有一个很棒的作用就是构建 lock。我们将会在后面的文章中介绍。


Object 的新方法

Object 提供了两个遍历的新方法 entries 和 values。


Object.entries(value : any) : Array<[string,any]>
复制代码

entries 返回的是一个数组,里面存储的是 key-value 对:


> Object.entries({ one: 1, two: 2 })[ [ 'one', 1 ], [ 'two', 2 ] ]
复制代码

entries 给了我们一个遍历 Object 的方法:


let obj = { one: 1, two: 2 };for (let [k,v] of Object.entries(obj)) {    console.log(`{JSON.stringify(k)}:{JSON.stringify(v)}`);}// Output:// "one": 1// "two": 2
复制代码

我们可以使用 entries 来创建 Map:


let map = new Map(Object.entries({    one: 1,    two: 2,}));console.log(JSON.stringify([...map]));    // [["one",1],["two",2]]
复制代码

同样的,Object 还提供了 values 方法:


Object.values(value : any) : Array<any>
复制代码

返回的是一个数组,数组中存放的是 Object 的 value。


除此之外,Object 还有一个 getOwnPropertyDescriptors 新方法。


这个方法返回的是 Obj 中的属性的描述。所谓属性的描述就是指这个属性是否可写,是否可以数之类:


const obj = {    [Symbol('foo')]: 123,    get bar() { return 'abc' },};console.log(Object.getOwnPropertyDescriptors(obj));
// Output:// { [Symbol('foo')]:// { value: 123,// writable: true,// enumerable: true,// configurable: true },// bar:// { get: [Function: bar],// set: undefined,// enumerable: true,// configurable: true } }
复制代码

key 是 Obj 中的 key,value 就是 PropertyDescriptors。


虽然在 ES6 中,Obj 已经引入了一个 Object.assign()方法用来拷贝 properties,但是这个 assign 只能拷贝具有默认属性值的属性。对于那些具有非默认属性值的属性 getters, setters, non-writable properties 来说,Object.assign 是不能拷贝的。这个时候就需要使用 getOwnPropertyDescriptors 方法了。


const source = {    set foo(value) {        console.log(value);    }};console.log(Object.getOwnPropertyDescriptor(source, 'foo'));// { get: undefined,//   set: [Function: foo],//   enumerable: true,//   configurable: true }
const target1 = {};Object.assign(target1, source);console.log(Object.getOwnPropertyDescriptor(target1, 'foo'));// { value: undefined,// writable: true,// enumerable: true,// configurable: true }
复制代码

可以看到 obj 就有一个 foo 属性,它是一个 setter。所以使用 assign 是不能进行拷贝的。


我们看下怎么使用 defineProperties 和 getOwnPropertyDescriptors 来进行拷贝:


const target2 = {};Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));console.log(Object.getOwnPropertyDescriptor(target2, 'foo'));// { get: undefined,//   set: [Function: foo],//   enumerable: true,//   configurable: true }
复制代码

除了拷贝属性之外,我们还可以拷贝对象:


const clone = Object.create(Object.getPrototypeOf(obj),    Object.getOwnPropertyDescriptors(obj));
复制代码

String 的新方法

String 添加了两个新的方法 padStart 和 padEnd。


pad 就是填充的意思,我们可以从前面填充也可以从后面填充。我们看下 pad 的用法:


String.prototype.padStart(maxLength, fillString=' ') String.prototype.padEnd(maxLength, fillString=' ') 
复制代码

看下具体的使用:


> 'x'.padStart(5, 'ab')'ababx'> 'x'.padEnd(5, 'ab')'xabab'
复制代码

逗号可以添加到函数的参数列表后面了

在 ES8 之前,函数的最后一个参数是不允许添加逗号的,但是在 ES8 中,一切都变得可能。


function foo(    param1,    param2,) {}
复制代码

我们可以在函数的定义中添加逗号。也可以在函数的调用中添加逗号:


foo(    'abc',    'def',);
复制代码

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/ecmascript-8/

本文来源:flydean 的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


发布于: 2021 年 03 月 17 日阅读数: 7
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
ECMAScript 2017(ES8)新特性简介