简介

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.jsconst 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.jsself.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的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!