ECMAScript 2017(ES8)新特性简介

简介: ECMAScript 2017(ES8)新特性简介

目录



简介


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',
);
相关文章
|
存储 数据可视化 数据挖掘
Python在数据分析中的利器:Pandas库全面解析
【2月更文挑战第7天】 众所周知,Python作为一种简洁、易学且功能强大的编程语言,被广泛运用于数据科学和人工智能领域。而Pandas库作为Python中最受欢迎的数据处理库之一,在数据分析中扮演着举足轻重的角色。本文将全面解析Pandas库的基本功能、高级应用以及实际案例,带您深入了解这个在数据分析领域的利器。
421 1
|
安全 网络安全 Windows
别人ping不通我的ip解决方法
别人ping不通我的ip解决方法
458 0
|
12月前
|
监控 负载均衡 JavaScript
PM2 介绍
【10月更文挑战第11天】
|
12月前
|
供应链 监控 安全
系统安全性的重要性体现在多个方面
【10月更文挑战第9天】系统安全性的重要性体现在多个方面
425 1
|
12月前
|
机器学习/深度学习 运维 监控
构建高效运维体系:从自动化到智能化的演进之路
在当今数字化时代,运维工作的重要性日益凸显。随着企业业务的不断扩展和技术的日新月异,传统的运维方式已难以满足现代企业的需求。因此,构建一个高效、智能的运维体系成为了企业发展的关键。本文将探讨如何从自动化逐步演进到智能化,以实现运维工作的高效化和智能化。
|
12月前
|
索引 Python
【Python篇】NumPy完整指南(上篇):掌握数组、矩阵与高效计算的核心技巧1
【Python篇】NumPy完整指南(上篇):掌握数组、矩阵与高效计算的核心技巧
360 4
|
人工智能
Suno教程篇:音乐小白也能使用Suno AI零门槛创作音乐?从此只听AI写的歌!
本文是一篇Suno AI音乐创作工具的教程,指导音乐小白如何使用Suno AI零门槛创作音乐,包括准备工作、基础使用、歌曲风格的选择、歌词填入技巧,以及通过实例展示如何为不同场景生成背景音乐。
Suno教程篇:音乐小白也能使用Suno AI零门槛创作音乐?从此只听AI写的歌!
|
SQL 数据库
MySQL8的数据库备份导入MySQL5版本中
MySQL8的数据库备份导入MySQL5版本中
957 1
MySQL8的数据库备份导入MySQL5版本中
|
存储 移动开发
An2021软件安装及基本操作(新建文件/导出)
An2021软件安装及基本操作(新建文件/导出)
1024 0
An2021软件安装及基本操作(新建文件/导出)
|
存储 块存储
西门子S7-1200不同存储区的寻址方式
S7-1200 CPU提供了全局存储器数据块和临时存储器等,用于在执行用户程序期间存储数据。全局存储器是指各种专用存储区,如输入映像区I区、输出映像区Q区和位存储器M区,所有块可以无限制地访问该存储器。
西门子S7-1200不同存储区的寻址方式