【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

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:063060

评论

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

MyBatis详解(一),实战java高并发程序设计

Java 程序员 后端

MyCat教程【分库分表】,java教程pdf百度网盘

Java 程序员 后端

MySQL 数据库开发入门(四),java高薪训练营百度网盘

Java 程序员 后端

MySql数据引擎简介与选择方法,和字节跳动大佬的技术面谈

Java 程序员 后端

markdown编辑器的使用教程,Java成长路线图

Java 程序员 后端

Maven的核心概念,java面试高频知识点

Java 程序员 后端

MySQL入门篇,java三层架构登录功能实现

Java 程序员 后端

Maven相关知识点整理,java基础语法的格式

Java 程序员 后端

mybatis-plus代码生成器,一键生成代码,springcloud教程百度云

Java 程序员 后端

mybatis常用注解(绝对经典),java程序设计教程张延军课后答案

Java 程序员 后端

MySQL最全整理(面试题+笔记,华为算法工程师面试经历汇总

Java 程序员 后端

MyBatis详解(二),java开发岗高频面试题全解析祖国的花朵

Java 程序员 后端

MySQL 事务隔离级别,java面试常问的设计模式

Java 程序员 后端

MySQL数据库--几种数据迁移的方法详解都在这了!看完必懂

Java 程序员 后端

MySQL:第一次看到有人把MYSQL主从复制讲解的这么清楚,mysql教程菜鸟

Java 程序员 后端

Log4j使用指南,java入门视频教程

Java 程序员 后端

MyBatis详解(二)(1),轻松拿到了阿里Java高级开发工程师的offer

Java 程序员 后端

Mybatis 传参的各种姿势,看这一篇就足够,java使用教程下载

Java 程序员 后端

MyBatis的jdbcType和javaType详解,spring框架入门教程

Java 程序员 后端

mybatis-plus使用教程,java面试题基础篇

Java 程序员 后端

MySQL 5,java架构师成长直通车

Java 程序员 后端

mybatis开发要点-insert主键ID获取和多参数传递,mybatis原理详解

Java 程序员 后端

MyCat:第四章:Mycat中的概念,linux内核编译教程

Java 程序员 后端

Myabtis源码分析六-代理对象Mapper的创建流程分析,死锁全详解,一文带你搞定

Java 程序员 后端

Mybatis一二级缓存实现原理与使用指南,java后端技术梳理

Java 程序员 后端

MyBatis(四):参数处理器,linux运维架构师

Java 程序员 后端

Mybatis学习笔记--Mybatis实现CRUD,springboot注解面试题

Java 程序员 后端

MyBatis详解:spring和mybatis整合,linux视频格式转换

Java 程序员 后端

MySQL 千万数据量深分页优化,mybatis从入门到精通pdf

Java 程序员 后端

MongoDB :第六章:Java程序操作MongoDB,mysql复合索引面试题

Java 程序员 后端

Mybatis Generator最完整配置详解,Java面试

Java 程序员 后端

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