写点什么

2019 谷歌 I/O 大会上提出的 JavaScript 新特性,你错过了吗?

  • 2019-08-06
  • 本文字数:10518 字

    阅读完需:约 35 分钟

2019谷歌I/O大会上提出的JavaScript新特性,你错过了吗?

本文总结了 2019 年谷歌 I/O 大会上 Mathias 和 Sathya 提出来的 JavaScript 新规范功能。


本文从对线程认识的简要刷新、事件循环、关于事件循环的常见问题和错误认识三个不同层面进行说明,进一步挖掘了 Node 的核心。

正则表达式 lookbehind

正则表达式(RegEx 正则表达式)在任何语言里都是强大的功能。在字符串中搜索复杂的模式时正则表达式就能大显身手了。之前 JavaScript 中的正则表达式实现已经非常齐全了,唯一缺少的就是 lookbehind

Lookahead

首先我们来了解一下正则表达式中的 lookahead 是什么含义,JavaScript 已经支持 lookahead 了。正则表达式中的 lookahead 语法允许你在字符串中选出的模式具有以下属性:另一个已知模式正好紧靠这个模式或不与其相邻,或者在这个模式之后。例如在字符串“MangoJuice,VanillaShake,GrapeJuice”中,我们可以使用正向 lookahead 语法来查找旁边有 J​​uice 的单词,即 Mango Grape


有两种类型的 lookahead,分别是正向 lookahead 和负向或否定的 lookahead。

正向 lookahead

正向 lookahead 选出的模式具有以下属性:另一个已知模式位于选出模式之后。正向 lookahead 的语法如下。


/[a-zA-Z]+(?=Juice)/
复制代码


上面的模式选出了大写或小写字母的单词,单词旁边都会有 Juice。不要把它和正则表达式中的捕获组混淆。Lookahead 和 Lookbehind 都是写在括号里的,但它们没有被捕获。下面看一个正向 lookahead 的实际例子。


const testString = "MangoJuice, VanillaShake, GrapeJuice";const testRegExp = /[a-zA-Z]+(?=Juice)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Mango", "Grape"]
复制代码

负向 lookahead

类似地,上面的例子中如果使用负向 lookahead,就是选出所有后面没有 Juice 的单词。负向 lookahead 的语法与正向 lookahead 的语法类似,但有一处不同。我们需要把=符号换成!符号。


/[a-zA-Z]+(?!Juice)/
复制代码


上面的正向表达式模式会选出所有后面不跟着 Juice 的单词。但上面的模式会选出给定字符串中的所有单词,因为给定字符串中的所有单词都不以 Juice 结尾,因此我们需要更具体些的规则。


/(Mango|Vanilla|Grape)(?!Juice)/
复制代码


这种模式将选出 MangoVanilla Grape 几个单词,它们后面没有跟着 Juice。来看具体的代码。


const testString = "MangoJuice, VanillaShake, GrapeJuice";const testRegExp= /(Mango|Vanilla|Grape)(?!Juice)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Vanilla"]
复制代码

Lookbehind

与 lookahead 类似,Lookbehind 也是正则表达式中的一种语法,用它选出的模式具有以下属性:字符串中的某个已知模式位于或不在它的前面。例如,在字符串“FrozenBananas,DriedApples,FrozenFish”中,我们可以使用正向 lookbehind 来找到前面有 Frozen 的单词,比比如 Bananas Fish


与 lookahead 类似,这里也有一个正向 lookbehind 和负向或否定 lookbehind。

正向 lookbehind

正向 lookbehind 选出的模式具有以下属性:另一个已知模式位于它的前面。正向 lookbehind 的语法如下。


/(?<=Frozen)[a-zA-Z]+/
复制代码


lookbehind 的模式类似于 lookahead,但带有额外的<符号,表示在前。上面的模式会选出所有以 Frozen 开头的单词或者在前面有 Frozen 的单词。来看具体的操作。


const testString = "FrozenBananas, DriedApples, FrozenFish";const testRegExp = /(?<=Frozen)[a-zA-Z]+/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Bananas", "Fish"]
复制代码

负向 lookbehind

负向 lookbehind 选出的模式具有以下属性:另一个已知模式不在它的前面。例如要选出“FrozenBananas,DriedApples,FrozenFish”字符串中前面没有 Frozen 的单词,我们将使用以下语法。


/(?<!Frozen)[a-zA-Z]+/
复制代码


但上面的模式将选出字符串中的所有单词,因为所有单词前面都没有 Frozen(FrozenBannanas 这样的单词这里会被视为一整个单词),我们需要更具体一些。


/(?<!Frozen)(Bananas|Apples|Fish)/
复制代码


写在代码中:


const testString = "FrozenBananas, DriedApples, FrozenFish";const testRegExp = /(?<!Frozen)(Bananas|Apples|Fish)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Apples"]
复制代码


支持范围——TC39:阶段 4;Chrome:62+;Node:8.10.0+

类字段

类字段(class field)是一种新的语法,用来从类构造函数外部定义实例(对象)的属性。有两种类型的类字段,公共类字段私有类字段

公共类字段

之前我们必须在类构造函数中定义对象上的属性。这些属性是公共的,意味着可以在类(对象)的实例上访问它们。


class Dog {    constructor() {        this.name = 'Tommy';    }}
复制代码


每当我们有一个扩展父类的类时,必须先从构造函数中调用 super,然后才能在子类上添加属性,如下所述。


class Animal {}class Dog extends Animal {    constructor() {        super(); // call super before using `this` in constructor        this.sound = 'Woof! Woof!';    }    makeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound(); // Woof! Woof!
复制代码


现在有了公共类字段语法,我们就可以在类的构造函数之外定义类字段,JavaScript 将隐式调用 super。


class Animal {}class Dog extends Animal {    sound = 'Woof! Woof!'; // public class field    makeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound(); // Woof! Woof!
复制代码


当 JavaScript 隐式调用 super 时,它会在实例化类时传递用户提供的所有参数(这是标准的 JavaScript 行为,与私有类字段无关)。因此,如果你的父构造函数需要定制参数,请确保手动调用 super。


class Animal {    constructor( ...args ) {        console.log( 'Animal args:', args );    }}class Dog extends Animal {    sound = 'Woof! Woof!'; // public class fieldmakeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog( 'Tommy', 'Loves', 'Toys!' );tommy.makeSound(); // Animal args: [ 'Tommy', 'Loves', 'Toys!' ]
复制代码


支持范围——TC39:阶段 3;Chrome:72+;Node:12+

私有类字段

众所周知,JavaScript 没有 publicprivate protected 之类的属性修饰符。默认情况下,对象上的所有属性都是公共的,这意味着任何人都可以访问它们。想要定义一个对外界隐藏的属性,最接近的方法是使用 Symbol 这个属性名称。你可能会使用_前缀来表示应该是私有的属性,但它只是一种表示法,不能解决问题。


现在有了私有类字段,我们可以让类属性只能在类中访问,并防止它们反映在实例(对象)上。来看前面的一个例子,先看一个暴露的属性。


class Dog {    _sound = 'Woof! Woof!'; // this is private        makeSound() {        console.log( this._sound );    }}// create instanceconst tommy = new Dog();console.log( tommy._sound ); // Woof! Woof!
复制代码


添加_前缀并不能解决我们的问题。私有类字段的定义方式与定义公共类字段的方式相同,但我们不必添加下划线前缀,而是添加 #前缀。访问对象上的私有属性将导致 SyntaxError: Undefined private field。


class Dog {    #sound = 'Woof! Woof!'; // this is private    makeSound() {        console.log( this.#sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound() // Woof! Woof!//console.log( tommy.#sound ); // SyntaxError
复制代码


私有属性只能在定义它们的类中访问。因此在父类或子类内无法访问私有属性。


我们还可以使用未定义的值定义私有(和公共)属性。


class Dog {    #name;    constructor( name ) {        this.#name = name;    }    showName() {        console.log( this.#name );    }}// create instanceconst tommy = new Dog( 'Tommy' );tommy.showName(); // Tommy
复制代码


支持范围——TC39:阶段 3;Chrome:74+;Node:12+

string.matchAll

我们在 string 数据类型上有 match 原型方法,它根据给定的正则表达式或关键字返回字符串中的匹配模式。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /([A-Z0-9]+)/g;console.log( colors.match( matchColorRegExp ) );// Output:["EEE", "CCC", "FAFAFA", "F00", "000"]
复制代码


但这种方法不提供其他附加信息,例如字符串中每个匹配的索引。删除 g 标志后可以生成其他信息,但之后我们只能获得第一个匹配项。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/;console.log( colors.match( matchColorRegExp ) );// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]
复制代码


最后,我们需要在正则表达式对象和语法上使用.exec 方法,这样写起来没那么复杂。我们需要使用 while 循环,直到 exec 返回 null 为止。但要注意,exec 不会返回迭代器。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/g;// in strict mode,// Uncaught ReferenceError: match is not definedwhile( match = matchColorRegExp.exec( colors ) ) {  console.log( match );}// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]["#CCC", "CCC", index: 6, input: "<colors>"]["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"]["#F00", "F00", index: 21, input: input: "<colors>"]["#000", "000", index: 27, input: input: "<colors>"]
复制代码


为了解决这个问题,我们现在有了 matchAll 方法,它返回一个迭代器,并且这个迭代器的每次 next()调用都会连续返回匹配的项。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/g;console.log( ...colors.matchAll( matchColorRegExp ) );// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]["#CCC", "CCC", index: 6, input: "<colors>"]["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"]["#F00", "F00", index: 21, input: input: "<colors>"]["#000", "000", index: 27, input: input: "<colors>"]
复制代码


支持范围——TC39:阶段 4;Chrome:73+;Firefox:67+;Node:12+

命名捕获组

与其他语言相比,JavaScript 中捕获或捕获组的概念略有不同。每当我们在括号内放置一个正则表达式模式(lookahead 和 lookbehind 除外)时,它就会变成一个捕获组,所有匹配的模式都会反映在匹配的输出项中。


在前面的示例中,下面数组的第一项是正则表达式模式的完整匹配,而第二项是捕获组的匹配值。


["#EEE", "EEE", index: 0, input: "<colors>"]
复制代码


如果有多个捕获组,它们将连续显示在结果中。我们来看一个例子。


const str = "My name is John Doe.";const matchRegExp = /My name is ([a-z]+) ([a-z]+)/i;const result = str.match( matchRegExp );console.log( result );// error, if result is nullconsole.log( { firstName: result[1], lastName: result[2] } );// Output:["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: undefined]{firstName: "John", lastName: "Doe"}
复制代码


如上所示,输出的第一个元素是完整匹配的字符串,而第二个和第三个元素是捕获组的结果。


现在有了命名捕获组后,我们可以使用标签将单个捕获组结果保存在 groups 对象中。定义命名捕获组的语法是(?$pattern)。


const str = "My name is John Doe.";const matchRegExp = /My name is (?<firstName>[a-z]+) (?<lastName>[a-z]+)/i;const result = str.match( matchRegExp );console.log( result );console.log( result.groups );// Output:["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: {firstName: "John", lastName: "Doe"}]{firstName: "John", lastName: "Doe"}
复制代码


命名捕获组也适用于 matchAll 方法。


支持范围——TC39:阶段 4;Chrome:64+;Node:10+

数字分隔符

我们写较大的整数或小数时,可读性一直是个大问题。例如,十亿写成数字是 1000000000,但你得数对零的个数才行,很多时候这都很让人头疼。


在较新版本的 JavaScript 中,我们可以使用_ 分隔符来分隔数字的各个部分,以增强可读性。


var billion = 1_000_000_000;console.log( billion ); // 1000000000
复制代码


我们可以随意将_放在数字中,而 JavaScript 只会忽略它。这种方法适用于任何类型的数字,无论是整数、十进制、二进制、十六进制还是八进制数字都行。


console.log( 1_000_000_000.11 ); // 1000000000.11console.log( 1_000_000_000.1_012 ); // 1000000000.1012console.log( 0xFF_00_FF ); // 16711935console.log( 0b1001_0011 ); // 147console.log( 0o11_17 ); // 591
复制代码


支持范围——TC39:阶段 3;Chrome:75+;Node:12.5+

BigInt

JavaScript 中的数字是从 Number 函数(也是构造函数)创建的。一个数字可以可靠表示的最大值是(2⁵³ - 1),也就是 9007199254740991。也可以使用 Number.MAX_SAFE_INTEGER 生成这个数。


当我们写下数字时,JavaScript 用 Number 构造函数包装它,以生成一个在其原型上包含数字方法的对象。所有原始数据类型都会这样处理。参阅 Primitives vs Objects 这篇文章来理解这个理念。


那么如果我们继续加大这个数字会怎么样?


console.log( Number.MAX_SAFE_INTEGER ); // 9007199254740991console.log( Number.MAX_SAFE_INTEGER + 10 ); // 9007199254741000
复制代码


上面代码中的最后一个日志输出返回了错误的结果。发生这种情况是因为 JavaScript 无法计算超过 Number.MAX_SAFE_INTEGER 值的数字。


现在有了 BigInt 就能解决这个问题了。BigInt 能让我们表示一个比 Number.MAX_SAFE_INTEGER 值更高的整数。与 Number 类似,BigInt 同时表现为一个函数和一个构造函数。加入 BigInt 后,JavaScript 有了新的 bigint 内置原始数据类型来表示大整数。


var large = BigInt( 9007199254740991 );console.log( large ); // 9007199254740991nconsole.log( typeof large ); // bigint
复制代码


JavaScript 会在整数的末尾添加 n 下标以表示 BigInt 整数形式。因此我们只需在整数的最末尾附加 n 就能写成 BigInt 了。


现在我们有了 BigInt,就可以安全地对具有 bigint 数据类型的大数字执行数学运算了。


var large = 9007199254740991n;console.log( large + 10n ); // 9007199254741001n
复制代码


使用 number 数据类型的数字与使用 bigint 数据类型的数字不同,因为 bigint 只能表示整数。因此程序不允许 bigint 和 number 数据类型之间的算术运算。


BigInt 函数可以接受任何类型的数字,如整数、二进制、十六进制、八进制等。它会在内部统一转换为十进制。


BigInt 还支持数字分隔符。


var large = 9_007_199_254_741_001n;console.log( large ); // 9007199254741001n
复制代码


支持范围——TC39:阶段 3;Chrome:67+;Firefox:68+;Node:10.4+

数组:flat 和 flatMap

数组对象上的 flat 和 flatMap 原型方法。

Array.flat

我们现在能在一个数组使用一个新的 flat(n)原型方法,它将数组展平到第 n 个深度并返回一个新数组。默认情况下 n 为 1。我们可以将 n 作为 Infinity 传递,以展平所有嵌套数组。


var nums = [1, [2, [3, [4, 5]]]];console.log( nums.flat() ); // [1, 2, [3, [4,5]]]console.log( nums.flat(2) ); // [1, 2, 3, [4,5]]console.log( nums.flat(Infinity) ); // [1, 2, 3, 4, 5]
复制代码


支持范围——TC39:阶段 4;Chrome:69+;Firefox:62+;Node:12+

Array.flatMap

日常编程工作中有时可能会使用 map 变换数组,然后将其展平。例如计算一些整数的平方。


var nums = [1, 2, 3];var squares = nums.map( n => [ n, n*n ] )console.log( squares ); // [[1,1],[2,4],[3,9]]console.log( squares.flat() ); // [1, 1, 2, 4, 3, 9]
复制代码


我们可以使用 flatMap 原型方法,用一个语法同时执行映射和展平。它只能将从回调函数返回的数组展平到 1 的深度。


var nums = [1, 2, 3];var makeSquare = n => [ n, n*n ];console.log( nums.flatMap( makeSquare ) ); // [1, 1, 2, 4, 3, 9]
复制代码


支持范围——TC39:阶段 4;Chrome:69+;Firefox:62+;Node:11+

对象:fromEntries

我们可以使用对象的 entries 静态方法提取对象的 key:value 对,该方法返回一个数组,其中每个元素都是一个数组,后者的第一项是 key,第二项是 value。


var obj = {x:1,y:2,z:3};var objEntries = Object.entries(obj);console.log(objEntries); // [[“x”,1],[“y”,2],[“z”,3]]
复制代码


我们现在可以在对象上使用 fromEntries 静态方法,它会将条目转换回对象。


var entries = [["x", 1],["y", 2],["z", 3]];var obj = Object.fromEntries( entries );console.log( obj ); // {x: 1, y: 2, z: 3}
复制代码


之前我们使用 entries 就能很容易地过滤和映射对象值,但将条目放回到对象表单却很麻烦。这里就可以使用 fromEntries 简化工作了。


var obj = { x: 1, y: 2, z: 3 };// [["x", 1],["y", 2],["z", 3]]var objEntries = Object.entries( obj );// [["x", 1],["z", 3]]var filtered = objEntries.filter( ( [key, value] ) => value % 2 !== 0 // select odd);console.log( Object.fromEntries( filtered ) ); // {x: 1, z: 3}
复制代码


当我们使用 Map 按插入顺序存储键值对时,内部数据结构与条目格式类似。我们可以使用 fromEntries 轻松地从 Map 构造一个对象。


var m = new Map([[“x”,1],[“y”,2],[“z”,3]]);console.log(m); // {“x”=> 1,“y”=> 2,“z”=> 3}console.log(Object.fromEntries(m)); // {x:1,y:2,z:3}
复制代码


支持范围——TC39:阶段 4;Chrome:73+;Firefox:63+;Node:12+

globalThis

之前我们很熟悉 JavaScript 中的 this 关键字。它没有确定的值,其值取决于访问它的上下文。在任何环境中,当从程序的最顶层上下文访问时 this 指向全局对象,这就是所谓的全局 this。


例如,在 JavaScript 中全局 this 是 window 对象,你可以在 JavaScript 文件的顶部(最外层上下文)或 JavaScript 控制台内添加 console.log(this)语句来验证这一点。



this 全局值在 Node.js 内部会指向 global 对象,而在 web worker 内部会指向 web worker 本身。但是要获取全局 this 值不太容易,因为我们不能在所有位置使用 this 关键字;例如,在类构造函数中 this 值指向类实例。


因此其他环境为我们提供了像 self 这样的关键字,与 JavaScript 和 web worker 中的全局 this 一样;而在 Node.js 中使用的是 global。使用这些替代关键字时,我们可以创建一个通用函数来返回全局 this 值。


const getGlobalThis = () => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global `this`');};var globalThis = getGlobalThis();
复制代码


但是用这个 polyfill 获取全局 this 对象会出问题,这篇文章解释了原因。为了解决这个问题,JavaScript 现在提供了 globalThis 关键字,它可以从任何地方返回全局 this 对象。


var obj = { fn: function() {  console.log( 'this', this === obj ); // true  console.log( 'globalThis', globalThis === window ); // true} };obj.fn();
复制代码


支持范围——TC39:阶段 3;Chrome:71+;Firefox:65+;Node:12+

稳定排序

我们对数组排序时,ECMAScript 不会为 JavaScript 引擎提出排序算法,而只会强制执行排序 API 的语法。因此排序性能和/或排序稳定性会随着浏览器或 JavaScript 引擎的不同而变化。


但现在 ECMAScript 强制数组排序算法保持稳定。这个答案介绍了排序稳定性更新。简而言之,如果排序结果(变异数组)中那些不受排序影响的项目顺序不变,与一开始插入的顺序一致,则排序算法就是稳定的。我们来看一个例子吧。


var list = [  { name: 'Anna', age: 21 },  { name: 'Barbra', age: 25 },  { name: 'Zoe', age: 18 },  { name: 'Natasha', age: 25 }];// possible result[  { name: 'Natasha', age: 25 }  { name: 'Barbra', age: 25 },  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 },]
复制代码


如上所示,在 list 数组中,名为 Barbra 的对象位于名为 Natasha 的对象之前。由于这些对象有着相同的年龄,我们希望排序结果中它们保持相同的顺序,但有时结果并非如此。排序算法的结果会取决于你使用的 JavaScript 引擎。


但是现在,所有现代浏览器和 Node.js 默认使用 sort 方法进行稳定排序。这将始终产生以下结果。


// stable sort result[  { name: 'Barbra', age: 25 },  { name: 'Natasha', age: 25 }  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 },]
复制代码


一些 JavaScript 引擎以前支持稳定排序,但仅适用于较小的数组。为了提高大型数组的性能,他们可能会使用更快的算法并牺牲排序稳定性。


支持范围——Chrome:70+;Firefox:62+;Node:12+

国际化 API

国际化 API 是由 JavaScript 中的 ECMAScript 标准提供的 API,用于格式化指定语言中的数字、字符串、日期和时间。此 API 在 Intl 对象上可用。此对象提供构造函数,以便为指定的区域设置创建与区域相关数据的格式化程序。可在此处查看支持的区域设置列表。

Intl.RelativeTimeFormat

在许多应用程序中,我们通常需要以相对格式显示时间,例如 5 分钟前、昨天1 周前等。当我们的网站需要区分不同区域的显示内容时,我们需要在分发包中存放所有可能的相对时间输出组合 。


JavaScript 现在在 Intl 对象上提供了 RelativeTimeFormat9(locale, config)构造函数,它允许你为特定的区域设置创建时间格式化程序。这将创建一个具有 format(value, unit)原型方法的对象来生成时间格式。


// español (spanish)var rtfEspanol= new Intl.RelativeTimeFormat('es', {  numeric: 'auto'});log( rtfEspanol.format( 5, 'day' ) ); // dentro de 5 díaslog( rtfEspanol.format( -5, 'day' ) ); // hace 5 díaslog( rtfEspanol.format( 15, 'minute' ) ); // dentro de 15 minutos
复制代码


支持范围——TC39:阶段 3;Chrome:71+;Firefox:65+;Node:12+

Intl.ListFormat

ListFormat API 允许我们将列表中的项目基于 and 或 or 格式组合在一起。例如,[apples,mangoes,bananas]使用并列格式就是 apples,mangoes and bananas,使用分离格式就是 apples,mangoes or bananas。


首先,我们需要根据区域环境从 ListFormat(locale, config)构造函数创建格式化程序实例,并使用 format(list)原型方法生成特定于区域环境的列表格式。


// español (spanish)var lfEspanol = new Intl.ListFormat('es', {  type: 'disjunction'});var list = [ 'manzanas', 'mangos', 'plátanos' ];log( lfEspanol.format( list ) ); // manzanas, mangos o plátanos
复制代码


支持范围——TC39:阶段 3;Chrome:72+;Node:12+

Intl.Locale

除了语种名称外,区域设置通常还有很多内容,如日历类型、小时制、语言等。Intl.Locale(localeId, config)构造函数用来基于提供的配置生成格式化的语言环境字符串。它创建的对象包含所有区域设置属性,并暴露 toString 原型方法以获取格式化的区域设置字符串。


const krLocale = new Intl.Locale( 'ko', {  script: 'Kore', region: 'KR',  hourCycle: 'h12', calendar: 'gregory'} );log( krLocale.baseName ); // ko-Kore-KRlog( krLocale.toString() ); // ko-Kore-KR-u-ca-gregory-hc-h12
复制代码


在此处(https://unicode.org/reports/tr35/#unicode_locale_id)了解区域设置标识符和 Unicode 区域设置标记。


支持范围——TC39:阶段 3;Chrome:74+;Node:12+

Promise

之前,我们在 Promise 构造函数上有 all 和 race 两种静态方法。Promise.all([… promises])返回一个 promise,它在输入的所有 promises 解析后才解析,输入的任何 promise 被拒绝时它也会被拒绝。Promise.race([… promises])返回一个 promise,输入的任何 promise 解析后它就会解析,输入的任何 promise 被拒绝后它也会被拒绝。


我们迫切需要一个静态方法来返回一个 promise,它要在所有 promise 完成后(解析或拒绝)解析。我们还需要一个类似 race 的方法来返回一个 promise,等输入的任何 promise 解析后它就会解析。

Promise.allSettled

Promise.allSettled 方法获取一组 promise,并在所有 promise 都被解析或拒绝后解析。因此,此方法返回的 promise 不需要 catch 回调,因为它总是会解析。then 回调按照各个 promise 的顺序接收每个 promise 的 status 和 value。


var p1 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val1' ), 2000 )); var p2 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val2' ), 2000 ));  var p3 = () => new Promise(  (resolve, reject) => setTimeout( () => reject( 'err3' ), 2000 ));var p = Promise.allSettled( [p1(), p2(), p3()] ).then(  ( values ) => console.log( values ));// Output[ {status: "fulfilled", value: "val1"}  {status: "fulfilled", value: "val2"}  {status: "rejected", value: "err3"}]
复制代码


支持范围——TC39:阶段 3;Chrome:76+

Promise.any

Promise.any 方法类似于 Promise.race,但是只要任何 promise 被拒绝,后者返回的 promise 就不会执行 catch 块。相比之下,前者等任何 promise 解析后它返回的 promise 也会解析。如果没有解析任何 promise,catch 块将被执行。如果有任何 promise 先解析了,就会执行 then 块。


支持范围——TC39:阶段 1


观看本文视频


英文原文:https://itnext.io/whats-new-in-javascript-google-i-o-2019-summary-d16bd230841


2019-08-06 10:063152

评论

发布
暂无评论
发现更多内容

GoFrame的gmap相比Go原生的map,天然支持排序和有序遍历!?

王中阳Go

Go golang 高效工作 学习方法 11月月更

3层结构+7大特点,带你认识华为云IoTEdge

华为云开发者联盟

云计算 物联网 华为云 企业号十月 PK 榜

京东云开发者|IoT运维 - 如何部署一套高可用K8S集群

京东科技开发者

运维 k8s IoT etcd K8s 多集群管理

为什么要用CSS精灵图

源字节1号

软件开发 前端开发 后端开发 小程序开发

计算机网络:组帧

timerring

计算机网络 11月月更

JavaScript的垃圾回收机制

肥晨

js 垃圾回收机制 11月月更

ModelBox姿态匹配:抖抖手动动脚勤做深呼吸

华为云开发者联盟

人工智能 华为云 企业号十月 PK 榜

微信小程序—制作一个简单的跑步小程序

格斗家不爱在外太空沉思

JavaScript 微信小程序 11月月更

RocksDB 7 终于解决了 Compaction 时性能下降问题

Kvrocks

redis RocksDB kvrocks

知象光电完成过亿元C轮融资,加速发力全球市场

硬科技星球

uniapp多端分享(app,小程序,公众号)

格斗家不爱在外太空沉思

vue.js uniapp 11月月更

前端面经

肥晨

前端面试题 11月月更 前端面筋 超全面试题

河北首家城商行传统核心业务国产化,TDSQL突破三“最”为秦皇岛银行保驾护航

腾讯云数据库

数据库 分布式 tdsql 客户案例 腾讯云数据库

web--文件上传

我是一个茶壶

WEB安全 文件上传 11月月更

uniapp简单入门

格斗家不爱在外太空沉思

vue.js uniapp 11月月更

SOFARegistry | 大规模集群优化实践

SOFAStack

开源 SOFA SOFARegistry'

2022世界互联网大会 | VoneCredit为中小企业纾困解忧

旺链科技

区块链 产业区块链 世界互联网大会 企业号十月PK榜

软件测试面试真题 | 面试时被问到知识盲区,该怎么办呢?

测试人

软件测试 面试题 测试开发

dns-client占用cpu过高的问题解决方案

我是一个茶壶

win10 DNS 11月月更

装了我这 10 个 IDEA 神级插件后,同事也开始情不自禁的嘚瑟了

沉默王二

IntelliJ IDEA

Paddle模型性能分析工具Profiler:定位瓶颈点、优化程序、提升性能

汀丶人工智能

自然语言处理 nlp 性能分析 11月月更

不借助 Fiori client,直接在手机浏览器里调用 SAP UI5 BarcodeScanner 实现条形码扫描的可能性?

汪子熙

JavaScript 前端开发 Fiori SAP UI5 11月月更

云数据库时代,DBA将走向何方?

华为云开发者联盟

数据库 后端 华为云 企业号十月 PK 榜

HummerRisk V0.5:新版云合规报告、资源风险联动、拓扑展示等内容

HummerCloud

云安全 云原生安全 11月月更

EMQ《物联网平台大规模数据接入和处理性能评测方法》成功入选“可信边缘计算推进计划”

EMQ映云科技

物联网 IoT 边缘计算 边云协同 11月月更

从柯里化讲起,一网打尽 JavaScript 重要的高阶函数

掘金安东尼

前端 11月月更

写给关系数据库开发者的 TDengine 入门指南

TDengine

数据库 tdengine 时序数据库

HTTP和HTTPS是什么 二者区别是什么

肥晨

11月月更 http和https http工作原理

【C语言】break 关键字

謓泽

11月月更

DevData Talks | 让效能度量产生真正的价值,要避开多少“坑”?

思码逸研发效能

研发效能 研发管理工具 企业研发管理

Serverless Developer Meetup 杭州站精彩回顾!【附赠PPT】

阿里巴巴云原生

阿里云 Serverless 云原生

2019谷歌I/O大会上提出的JavaScript新特性,你错过了吗?_大前端_Uday Hiwarale_InfoQ精选文章