关于es8:ECMAScript-2017ES8新特性简介

44次阅读

共计 6787 个字符,预计需要花费 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 shared
const sharedBuffer = new SharedArrayBuffer(// (A)
    10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements

// Share sharedBuffer with the worker
worker.postMessage({sharedBuffer}); // clone

// Local only
const 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) : T
Atomics.store(ta : TypedArray<T>, index, value : T) : T
Atomics.exchange(ta : TypedArray<T>, index, value : T) : T
Atomics.compareExchange(ta : TypedArray<T>, index, expectedValue, replacementValue) : T

load 和 store 能够将 ta 作为一个整体来操作。

看下应用例子:

// main.js
console.log('notifying...');
Atomics.store(sharedArray, 0, 123);

// worker.js
while (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) : T
Atomics.sub(ta : TypedArray<T>, index, value) : T
Atomics.and(ta : TypedArray<T>, index, value) : T
Atomics.or(ta : TypedArray<T>, index, value) : T
Atomics.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 的博客

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

正文完
 0