ECMAScript 2017(ES8)新特性简介

2024-02-24 16:08

本文主要是介绍ECMAScript 2017(ES8)新特性简介,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 简介
  • Async函数
  • 共享内存和原子操作
  • Object的新方法
  • String的新方法
  • 逗号可以添加到函数的参数列表后面了

简介

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 waitwriter.write('world'); // don’t waitawait writer.close(); // wait for file to close
}

共享内存和原子操作

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

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

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

// main.jsconst 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.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) : 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的博客

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

这篇关于ECMAScript 2017(ES8)新特性简介的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/742628

相关文章

Qt QCustomPlot库简介(最新推荐)

《QtQCustomPlot库简介(最新推荐)》QCustomPlot是一款基于Qt的高性能C++绘图库,专为二维数据可视化设计,它具有轻量级、实时处理百万级数据和多图层支持等特点,适用于科学计算、... 目录核心特性概览核心组件解析1.绘图核心 (QCustomPlot类)2.数据容器 (QCPDataC

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

JDK9到JDK21中值得掌握的29个实用特性分享

《JDK9到JDK21中值得掌握的29个实用特性分享》Java的演进节奏从JDK9开始显著加快,每半年一个新版本的发布节奏为Java带来了大量的新特性,本文整理了29个JDK9到JDK21中值得掌握的... 目录JDK 9 模块化与API增强1. 集合工厂方法:一行代码创建不可变集合2. 私有接口方法:接口

C#特性(Attributes)和反射(Reflection)详解

《C#特性(Attributes)和反射(Reflection)详解》:本文主要介绍C#特性(Attributes)和反射(Reflection),具有很好的参考价值,希望对大家有所帮助,如有错误... 目录特性特性的定义概念目的反射定义概念目的反射的主要功能包括使用反射的基本步骤特性和反射的关系总结特性

PyTorch高级特性与性能优化方式

《PyTorch高级特性与性能优化方式》:本文主要介绍PyTorch高级特性与性能优化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、自动化机制1.自动微分机制2.动态计算图二、性能优化1.内存管理2.GPU加速3.多GPU训练三、分布式训练1.分布式数据

rust 中的 EBNF简介举例

《rust中的EBNF简介举例》:本文主要介绍rust中的EBNF简介举例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 什么是 EBNF?2. 核心概念3. EBNF 语法符号详解4. 如何阅读 EBNF 规则5. 示例示例 1:简单的电子邮件地址

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

SpringBoot3.4配置校验新特性的用法详解

《SpringBoot3.4配置校验新特性的用法详解》SpringBoot3.4对配置校验支持进行了全面升级,这篇文章为大家详细介绍了一下它们的具体使用,文中的示例代码讲解详细,感兴趣的小伙伴可以参考... 目录基本用法示例定义配置类配置 application.yml注入使用嵌套对象与集合元素深度校验开发

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc