Graph + AI 中国峰会火热报名中,点击探索图分析更多可能! 了解详情
写点什么

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

2019 年 8 月 06 日

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 年 8 月 06 日 10:062741

评论

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

Web3极客日报#135

谢锐 | Frozen

区块链 独立开发者 技术社区 Rebase Web3 Daily

Golang杂谈 - graceful shutdown为何离奇失效?

星语

Go 后端 平滑重启 服务端

游戏夜读 | 工具游戏的辉煌

game1night

Java并发编程基础--线程

Java收录阁

Java 线程

排序系列之选择/冒泡

脚动两轮男之漂流小王子

基准测试神器JMH —— 详解36个官方例子

捉虫大师

Java 性能 JMH

程序员陪娃漫画系列——当她想吃的时候

孙苏勇

程序员 生活 陪伴 漫画

Centos 7 安装RabbitMQ(RPM)

Kevin Liao

RabbitMQ RPM安装

笔记:《如何系统思考》之因果回路图

wiflish

思维方式

花更多的时间在自己的优势上

Neco.W

创业 自我管理 重新理解创业

为什么厉害的人精力都那么好?

非著名程序员

程序员 程序人生 提升认知 精力管理

Java并发编程系列——分布式锁

孙苏勇

Java zookeeper 并发编程 多线程 分布式锁

冥想与呼吸法之于情绪控制

树上

情绪 冥想 呼吸法 呼吸 自我

Centos 7 使用Firewalld

Kevin Liao

Centos 7 防火墙 Firewalld防火墙 Firewalld

MySQL自增ID以及其他唯一ID方式分析

Bruce Duan

MySQL自增ID 唯一ID

leetcode8. 字符串转换整数 (atoi)

Damien

算法 数学

程序员为什么要提高赚钱能力?程序员如何提高赚钱能力?

陆陆通通

赚钱

排序系列快排/内省

脚动两轮男之漂流小王子

第一次手动编译openjdk11

py ploughman

jdk

人人都要有经营意识

Neco.W

创业 重新理解创业 公司管理

排序系列插入/希尔

脚动两轮男之漂流小王子

排序系列归并/timsort

脚动两轮男之漂流小王子

Redis学习笔记(安装)

编程随想曲

redis

Impala UDTF 功能实现

小鹏

大数据 hadoop cloudera 数据仓库

创投机会诞生在这四个核心变量中 | 2019年在某大学课堂做的一次讲演的实录

邓瑞恒Ryan

创业 管理 投资 行业资讯

排序系列堆/二分插入

脚动两轮男之漂流小王子

NIO看破也说破(一)—— Linux/IO基础

小眼睛聊技术

Linux 架构 后端 Netty nio

排序系列计数/基数

脚动两轮男之漂流小王子

leetcode141. 环形链表

Damien

算法 链表 LeetCode

第三方支付概述

cf

支付 网联 备付金

做一个好产品的初衷:用户值得

池建强

创业 产品 写作

聊一下 Mesh 数据平面 Sidecar 与 Service 通信的那些事儿

聊一下 Mesh 数据平面 Sidecar 与 Service 通信的那些事儿

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