ECMAScript 2020 新特性 (#7069)
* 用JavaScript中的蹦床函数实现安全递归 Safe Recursion with Trampoline in JavaScript * 更新校对 感谢@cyz980908指出问题,已修改~ * ECMAScript 2020 新特性 The Latest Features Added to JavaScript in ECMAScript 2020 * 校对更新 ECMAScript 2020 新特性 感谢 @Chorer 的建议,非常仔细,已做修改~ * 修改第二次校对完成 感谢建议,已做修改~ @CoolRice @fanyijihua * 更新 ECMAScript 2020 新特性 更新一些格式问题。 * Update safe-recursion-with-trampoline-in-javascript.md Co-authored-by: lsvih <lsvih@qq.com>
This commit is contained in:
@@ -2,26 +2,26 @@
|
||||
> * 原文作者:[Thomas Findlay](https://www.telerik.com/blogs/author/thomas-findlay)
|
||||
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
|
||||
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/article/2020/latest-features-javascript-ecmascript-2020.md](https://github.com/xitu/gold-miner/blob/master/article/2020/latest-features-javascript-ecmascript-2020.md)
|
||||
> * 译者:
|
||||
> * 校对者:
|
||||
> * 译者:[Gesj-yean](https://github.com/Gesj-yean)
|
||||
> * 校对者:[Chorer](https://github.com/Chorer),[CoolRice](https://github.com/CoolRice)
|
||||
|
||||
# The Latest Features Added to JavaScript in ECMAScript 2020
|
||||
# ECMAScript 2020 新特性
|
||||
|
||||

|
||||
|
||||
JavaScript is one of the most popular programming languages, and features are now added to the language every year. This article covers new features added in ECMAScript 2020, also known as ES11.
|
||||
JavaScript 是最流行的编程语言之一,每年都会添加新的特性。本文介绍了添加在 ECMAScript 2020(又称ES11)中的新特性。
|
||||
|
||||
Before the introduction of ECMAScript 2015, also known as ES6, JavaScript language development had a very slow pace. Fortunately, since then, new features are added every year. Be aware that not all features might be supported in all modern browsers yet, but thanks to transpilers like [Babel](https://babeljs.io/), we can use them already today. This article will cover some of the latest additions to JavaScript — ECMAScript 2020 (ES11).
|
||||
在引入 ECMAScript 2015(又称 ES6)之前,JavaScript 发展的非常缓慢。但自 2015 年起,每年都有新特性添加进来。需要注意的是,不是所有特性都被现代浏览器支持,但是由于 JavaScript 编译器 [Babel](https://babeljs.io/) 的存在,我们已经可以使用新特性了。本文将介绍 ECMAScript 2020(ES11)的一些最新特性。
|
||||
|
||||
## Optional Chaining
|
||||
## Optional Chaining 可选链式调用
|
||||
|
||||
I’m sure that most developers are familiar with an error of this kind:
|
||||
大部分开发者都遇到过这个问题:
|
||||
|
||||
`TypeError: Cannot read property ‘x’ of undefined`
|
||||
|
||||
This error basically means that we tried to access a property on something that is not an object.
|
||||
这个错误表示我们正在访问一个不属于对象的属性。
|
||||
|
||||
### Accessing an Object Property
|
||||
### 访问对象的属性
|
||||
|
||||
```javascript
|
||||
const flower = {
|
||||
@@ -30,78 +30,78 @@ const flower = {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(flower.colors.red) // this will work
|
||||
console.log(flower.colors.red) // 正常运行
|
||||
|
||||
console.log(flower.species.lily) // TypeError: Cannot read property 'lily' of undefined
|
||||
console.log(flower.species.lily) // 抛出错误:TypeError: Cannot read property 'lily' of undefined
|
||||
```
|
||||
|
||||
JavaScript engine will always throw an error in a scenario like this. However, there are cases in which it doesn't matter that the value isn't there yet, because we might know it will be. Fortunately, here is the optional chaining feature to the rescue!
|
||||
在这种情况下,JavaScript 引擎会像这样抛出错误。但是某些情况下值是否存在并不重要,因为我们知道它会存在。于是,可选链式调用就派上用场了!
|
||||
|
||||
We can use the optional chaining operator, which consists of a question mark and a dot: **`?.`**, to indicate that an error should not be thrown. Instead, if there is no value, **undefined** will be returned.
|
||||
我们可以使用由一个问号和一个点组成的可选链式操作符,去表示不应该引发错误。如果没有值,应该返回 **undefined**。
|
||||
|
||||
```javascript
|
||||
console.log(flower.species?.lily) // undefined
|
||||
console.log(flower.species?.lily) // 输出 undefined
|
||||
```
|
||||
|
||||
Optional chaining can also be used when accessing array values or calling a function.
|
||||
当访问数组或调用函数时,也可以使用可选链式调用。
|
||||
|
||||
### Accessing an Array
|
||||
### 访问数组
|
||||
|
||||
```javascript
|
||||
let flowers = ['lily', 'daisy', 'rose']
|
||||
|
||||
console.log(flowers[1]) // daisy
|
||||
console.log(flowers[1]) // 输出:daisy
|
||||
|
||||
flowers = null
|
||||
|
||||
console.log(flowers[1]) // TypeError: Cannot read property '1' of null
|
||||
console.log(flowers?.[1]) // undefined
|
||||
console.log(flowers[1]) // 抛出错误:TypeError: Cannot read property '1' of null
|
||||
console.log(flowers?.[1]) // 输出:undefined
|
||||
```
|
||||
|
||||
### Calling a Function
|
||||
### 调用函数
|
||||
|
||||
```javascript
|
||||
let plantFlowers = () => {
|
||||
return 'orchids'
|
||||
}
|
||||
|
||||
console.log(plantFlowers()) // orchids
|
||||
console.log(plantFlowers()) // 输出:orchids
|
||||
|
||||
plantFlowers = null
|
||||
|
||||
console.log(plantFlowers()) // TypeError: plantFlowers is not a function
|
||||
console.log(plantFlowers()) // 抛出错误:TypeError: plantFlowers is not a function
|
||||
|
||||
console.log(plantFlowers?.()) // undefined
|
||||
console.log(plantFlowers?.()) // 输出:undefined
|
||||
```
|
||||
|
||||
## Nullish Coalescing
|
||||
## Nullish Coalescing 空值合并
|
||||
|
||||
Until recently, whenever there was a need to provide a fallback value, the logical operator **`||`** had to be used. It works in most cases, but it can't be applied in some scenarios. For instance, if the initial value is a Boolean or a number. Let's take a look at an example below, where we want to assign a number to a variable, or default it to 7 if the initial value is not a number:
|
||||
目前,要为变量提供回退值,逻辑操作符 **`||`** 还是必须的。它适用于很多情况,但不能应用在一些特殊的场景。例如,初始值是布尔值或数字的情况。举例说明,我们要把数字赋值给一个变量,当变量的初始值不是数字时,就默认其为 7 :
|
||||
|
||||
```javascript
|
||||
let number = 1
|
||||
let myNumber = number || 7
|
||||
```
|
||||
|
||||
The **myNumber** variable is equal to 1, because the left-hand value (**number**) is a [**truthy**](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) value, as 1 is a positive number. However, what if the **number** variable is not 1, but 0?
|
||||
变量 **myNumber** 等于 1,因为左边的(**number**)是一个 [**真**](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) 值 1。但是,当变量 **number** 不是 1 而是 0 呢?
|
||||
|
||||
```javascript
|
||||
let number = 0
|
||||
let myNumber = number || 7
|
||||
```
|
||||
|
||||
0 is a [**falsy**](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value, and even though it is a number, the **myNumber** variable will have the right-hand value assigned to it. Therefore, **myNumber** is now equal to 7. However, that's not really what we want. Fortunately, instead of writing additional code and checks to confirm if the **number** variable is indeed a number, we can use the nullish coalescing operator. It consists of two question marks: **`??`**.
|
||||
0 是 [**假**](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) 值,所以即使 0 是数字。变量 **myNumber** 将会被赋值为右边的 7。但结果并不是我们想要的。幸好,由两个问号组成:**`??`** 的合并操作符就可以检查变量 **number** 是否是一个数字,而不用写额外的代码了。
|
||||
|
||||
```javascript
|
||||
let number = 0
|
||||
let myNumber = number ?? 7
|
||||
```
|
||||
|
||||
The right-hand side value will only be assigned if the left-hand value is equal to **null** or **undefined**. Therefore, in the example above, the **myNumber** variable is equal to 0.
|
||||
操作符右边的值仅在左边的值等于 **null** 或 **undefined** 时有效,因此,例子中的变量 **myNumber** 现在的值等于 0 了。
|
||||
|
||||
## Private Fields
|
||||
## Private Fields 私有字段
|
||||
|
||||
Many programming languages that have **classes** allow defining class properties as public, protected, or private. **Public** properties can be accessed from outside of a class and by its subclasses, while **protected** classes can only be accessed by subclasses. However, **private** properties can only be accessed from inside of a class. JavaScript supports class syntax since **ES6**, but only now were private fields introduced. To define a private property, it has to be prefixed with the hash symbol: **`#`**.
|
||||
许多具有 **classes** 的编程语言允许定义类作为公共的,受保护的或私有的属性。**Public** 属性可以从类的外部或者子类访问,**protected** 属性只能被子类访问,**private** 属性只能被类内部访问。JavaScript 从 **ES6** 开始支持类语法,但直到现在才引入了私有字段。要定义私有属性,必须在其前面加上散列符号:**`#`**。
|
||||
|
||||
```javascript
|
||||
class Flower {
|
||||
@@ -117,15 +117,15 @@ class Flower {
|
||||
|
||||
const orchid = new Flower("orchid");
|
||||
|
||||
console.log(orchid.get_color()); // green
|
||||
console.log(orchid.#leaf_color) // Private name #leaf_color is not defined
|
||||
console.log(orchid.get_color()); // 输出:green
|
||||
console.log(orchid.#leaf_color) // 报错:SyntaxError: Private field '#leaf_color' must be declared in an enclosing class
|
||||
```
|
||||
|
||||
If we try to access a private property from outside, an error will be thrown.
|
||||
如果我们从外部访问类的私有属性,势必会报错。
|
||||
|
||||
## Static Fields
|
||||
## Static Fields 静态字段
|
||||
|
||||
To use a class method, a class had to be instantiated first, as shown below.
|
||||
如果想使用类的方法,首先必须实例化一个类,如下所示:
|
||||
|
||||
```javascript
|
||||
class Flower {
|
||||
@@ -137,10 +137,10 @@ class Flower {
|
||||
const rose = new Flower();
|
||||
rose.add_leaves();
|
||||
|
||||
Flower.add_leaves() // TypeError: Flower.add_leaves is not a function
|
||||
Flower.add_leaves() // 抛出错误:TypeError: Flower.add_leaves is not a function
|
||||
```
|
||||
|
||||
Trying to access a method without instantiating the **Flower** class would result in an error. Thanks to **static** fields, a class method can now be declared with the **static** keyword and called from outside of a class.
|
||||
试图去访问没有实例化的 **Flower** 类的方法将会抛出一个错误。但由于 **static** 字段,类方法可以被 **static** 关键词声明然后从外部调用。
|
||||
|
||||
```javascript
|
||||
class Flower {
|
||||
@@ -152,12 +152,12 @@ class Flower {
|
||||
}
|
||||
}
|
||||
|
||||
const rose = Flower.create_flower("rose"); // Works fine
|
||||
const rose = Flower.create_flower("rose"); // 正常运行
|
||||
```
|
||||
|
||||
## Top Level Await
|
||||
## Top Level Await 顶级 Await
|
||||
|
||||
So far, to **await** for a promise to finish, a function in which **await** is used would need to be defined with the **async** keyword.
|
||||
目前,如果用 **await** 获取 promise 函数的结果,那使用 **await** 的函数必须用 **async** 关键字定义。
|
||||
|
||||
```javascript
|
||||
const func = async () => {
|
||||
@@ -165,7 +165,7 @@ const func = async () => {
|
||||
}
|
||||
```
|
||||
|
||||
Unfortunately, if there was a need to await for something in a global scope, it would not be possible, and usually required an **immediately invoked function expression (IIFE)**.
|
||||
头疼的是,在全局作用域中去等待某些结果基本上是不可能的。除非使用 **立即调用的函数表达式(IIFE)**。
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
@@ -173,13 +173,13 @@ Unfortunately, if there was a need to await for something in a global scope, it
|
||||
})()
|
||||
```
|
||||
|
||||
Thanks to **Top Level Await**, there is no need for wrapping code in an async function anymore, and this code will work.
|
||||
但引入了 **顶级 Await** 后,不需要再把代码包裹在一个 async 函数中了,如下即可:
|
||||
|
||||
```javascript
|
||||
const response = await fetch(url)
|
||||
```
|
||||
|
||||
This feature could be useful for resolving module dependencies or using a fallback source if the initial one failed.
|
||||
这个特性对于解决模块依赖或当初始源无法使用而需要备用源的时候是非常有用的。
|
||||
|
||||
```javascript
|
||||
let Vue
|
||||
@@ -190,48 +190,48 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
## Promise.allSettled
|
||||
## Promise.allSettled 方法
|
||||
|
||||
To wait for multiple promises to finish, **Promise.all(\[promise\_1, promise\_2\])** can be used. The problem is that if one of them fails, then an error will be thrown. Nevertheless, there are cases in which it is ok for one of the promises to fail, and the rest should still resolve. To achieve that, **ES11** introduced **Promise.allSettled**.
|
||||
等待多个 promise 返回结果时,我们可以用 **Promise.all(\[promise\_1, promise\_2\])**。但问题是,如果其中一个请求失败了,就会抛出错误。然而,有时候我们希望某个请求失败后,其他请求的结果能够正常返回。针对这种情况 **ES11** 引入了 **Promise.allSettled** 。
|
||||
|
||||
```javascript
|
||||
promise_1 = Promise.resolve('hello')
|
||||
primise_2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'problem'))
|
||||
promise_2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'problem'))
|
||||
|
||||
Promise.allSettled([promise_1, promise_2])
|
||||
.then(([promise_1_result, promise_2_result]) => {
|
||||
console.log(promise_1_result) // {status: 'fulfilled', value: 'hello'}
|
||||
console.log(promise_2_result) // {status: 'rejected', reason: 'problem'}
|
||||
console.log(promise_1_result) // 输出:{status: 'fulfilled', value: 'hello'}
|
||||
console.log(promise_2_result) // 输出:{status: 'rejected', reason: 'problem'}
|
||||
})
|
||||
```
|
||||
|
||||
A resolved promise will return an object with **status** and **value** properties, while rejected ones will have **status** and **reason**.
|
||||
成功的 promise 将返回一个包含 **status** 和 **value** 的对象,失败的 promise 将返回一个包含 **status** 和 **reason** 的对象。
|
||||
|
||||
## Dynamic Import
|
||||
## Dynamic Import 动态引入
|
||||
|
||||
You might have used dynamic imports when using **webpack** for module bundling. Finally, native support for this feature is here.
|
||||
你也许在 **webpack** 的模块绑定中已经使用过动态引入。但对于该特性的原生支持已经到来:
|
||||
|
||||
```javascript
|
||||
// Alert.js file
|
||||
// Alert.js
|
||||
export default {
|
||||
show() {
|
||||
// Your alert
|
||||
// 代码
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Some other file
|
||||
// 使用 Alert.js 的文件
|
||||
import('/components/Alert.js')
|
||||
.then(Alert => {
|
||||
Alert.show()
|
||||
})
|
||||
```
|
||||
|
||||
Considering the fact that a lot applications use module bundlers like webpack for transpiling and optimizing code, this feature isn't such a big deal right now.
|
||||
考虑到许多应用程序使用诸如 webpack 之类的模块打包器来进行代码的转译和优化,这个特性现在还没什么大作用。
|
||||
|
||||
## MatchAll
|
||||
## MatchAll 匹配所有项
|
||||
|
||||
MatchAll is useful for applying the same regular expression to a string if you need to find all matches and get their positions. The **match** method only returns items that were matched.
|
||||
如果你想要查找字符串中所有正则表达式的匹配项和它们的位置,MatchAll 非常有用。
|
||||
|
||||
```javascript
|
||||
const regex = /\b(apple)+\b/;
|
||||
@@ -241,20 +241,20 @@ const fruits = "pear, apple, banana, apple, orange, apple";
|
||||
for (const match of fruits.match(regex)) {
|
||||
console.log(match);
|
||||
}
|
||||
// Output
|
||||
// 输出
|
||||
//
|
||||
// 'apple'
|
||||
// 'apple'
|
||||
```
|
||||
|
||||
**matchAll** in contrast, returns a bit more information, including index of the string found.
|
||||
相比之下,**matchAll** 返回更多的信息,包括找到匹配项的索引。
|
||||
|
||||
```javascript
|
||||
for (const match of fruits.matchAll(regex)) {
|
||||
console.log(match);
|
||||
}
|
||||
|
||||
// Output
|
||||
// 输出
|
||||
//
|
||||
// [
|
||||
// 'apple',
|
||||
@@ -279,30 +279,30 @@ for (const match of fruits.matchAll(regex)) {
|
||||
// ]
|
||||
```
|
||||
|
||||
## globalThis
|
||||
## globalThis 全局对象
|
||||
|
||||
JavaScript can run in different environments like browsers or Node.js. A global object in browsers is available under **window** variable, but in Node it is an object called **global**. To make it easier to use a global object no matter in which environment code is running, **globalThis** was introduced.
|
||||
JavaScript 可以在不同环境中运行,比如浏览器或者 Node.js。浏览器中可用的全局对象是变量 **window**,但在 Node.js 中是一个叫做 **global** 的对象。为了在不同环境中都使用统一的全局对象,引入了 **globalThis** 。
|
||||
|
||||
```javascript
|
||||
// In a browser
|
||||
// 浏览器
|
||||
window == globalThis // true
|
||||
|
||||
// In node.js
|
||||
// node.js
|
||||
global == globalThis // true
|
||||
```
|
||||
|
||||
## BigInt
|
||||
|
||||
The maximum number that can be reliably represented in JavaScript is 2^53 - 1. BigInt will allow creation of numbers even bigger than that.
|
||||
JavaScript 中能够精确表达的最大数字是 2^53 - 1。而 BigInt 可以用来创建更大的数字。
|
||||
|
||||
```javascript
|
||||
const theBiggerNumber = 9007199254740991n
|
||||
const evenBiggerNumber = BigInt(9007199254740991)
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
## 结论
|
||||
|
||||
I hope you found this article useful and are as excited as I am about the new features that are coming to JavaScript. If you would like to know more about different features you can check the official GitHub repository of the ES committee [here](https://github.com/tc39/proposals/blob/master/finished-proposals.md).
|
||||
我希望这篇文章对您有用,并像我一样期待 JavaScript 即将到来的新特性。如果想了解更多,可以看看 tc39 委员会的[官方Github仓库](https://github.com/tc39/proposals/blob/master/finished-proposals.md)。
|
||||
|
||||
> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user