ES2022 的所有新功能
本文最初发布在我的博客上。更多文章和教程,请访问inspiredwebdev.com。查看我在Educative上的JavaScript 课程,学习从基础知识到 ES2022 的所有内容。
自 2015 年以来,每年JavaScript
都会不断更新其规格并添加新的有趣功能。
在本文中,我们将了解即将发生的事情,因为许多功能已经达到第 4 阶段并将包含在规范中。
对于那些不知道的人来说,提案过程分为 4 个阶段,其中第 4 个阶段是最后一个阶段,标志着提案完成。
作为开发者,及时了解语言的最新规范至关重要。如果你觉得自己跟不上JavaScript
过去几年的诸多更新,我推荐你阅读我的书,它涵盖了从语言基础知识到最新的 ES2022 规范的所有内容,甚至还包含一些简介。你可以在GithubTypeScript
上免费阅读,那里还有购买电子书的链接,或者你也可以在Educative上查看我的课程。
现在,让我们开始了解 ES2022 的第一个新功能:
类字段
类公共实例字段和私有实例字段
在 ES2022 之前,我们会像这样定义 a 的class
属性constructor
:
class ButtonToggle extends HTMLElement {
constructor(){
super();
// public field
this.color = 'green'
// private field
this._value = true;
}
toggle(){
this.value = !this.value
}
}
const button = new ButtonToggle();
console.log(button.color);
// green - public fields are accessible from outside classes
button._value = false;
console.log(button._value);
// false - no error thrown, we can access it from outside the class
在 中constructor
,我们定义了两个字段。如您所见,其中一个字段的_
名称前面带有 ,这只是一种JavaScript
命名约定,用于声明该字段private
只能在方法内部访问class
。当然,这只是一种命名约定,而不是语言本身强制要求的,这就是为什么当我们尝试访问它时没有引发任何错误。
在 ES2022 中,我们有一种更简单的方法来声明public
和private
字段。让我们看一下这个更新的示例:
class ButtonToggle extends HTMLElement {
color = 'green';
#value = true;
toggle(){
this.#value = !this.#value;
}
}
const button = new ButtonToggle();
console.log(button.color);
// green - public fields are accessible from outside classes
// SyntaxError - cannot be accessed or modified from outside the class
console.log(button.#value);
button.#value = false;
首先要注意的是,不必在 内部定义它们constructor
。其次,我们还可以通过在字段名称private
前添加前缀来定义字段。#
与前面的例子的主要区别在于,如果我们尝试访问或修改类外的字段,这次将会引发实际错误。
JavaScript 类的私有方法和 getter/setter
与前面的例子类似,我们也可以private
为我们的类定义方法和 getter/setter。
class ButtonToggle extends HTMLElement {
color = 'green'
#value = true;
#toggle(){
this.#value = !this.#value
}
set #setFalseValue(){
this.#value = false;
}
}
const button = new ButtonToggle();
// SyntaxError - cannot be accessed or modified from outside the class
button.#toggle();
// SyntaxError - cannot be accessed or modified from outside the class
button.#setFalseValue;
在上面的例子中,我们toggle()
用替换了#toggle()
,从而使toggle
方法private
只能从 内部访问class
。
静态类字段和私有静态方法
字段static
或方法只能在原型中访问,而不是在每个实例中访问,并且 ES2022 为我们提供了使用关键字定义字段和公共/私有方法的class
方法。static
static
static
以前我们必须在class
身体之外定义它们,例如:
class ButtonToggle extends HTMLElement {
// ... class body
}
ButtonToggle.toggle(){
// static method define outside of the class body
}
现在,我们可以class
使用static
关键字直接在主体内部定义它们:
class ButtonToggle extends HTMLElement {
#value = true;
static toggle(){
this.#value = !this.#value
}
}
// this will work
ButtonToggle.toggle();
// SyntaxError - private static field
const button = new ButtonToggle();
button.toggle();
正如您在上面的示例中看到的,我们可以toggle()
直接访问ButtonToggle
,但不能在它的新实例上执行相同的操作。
我们可以static
在字段和方法(私有和公共)前面使用关键字,并将其与#
(private
)结合使用,我们可以创建private static
只能从原型内部访问的方法class
。
class ButtonToggle extends HTMLElement {
#value = true;
static #toggle(){
this.#value = !this.#value
}
}
// this will error, it's a private static method
ButtonToggle.#toggle();
符合人体工程学的品牌检查私人领域
正如我们在上面的例子中所看到的,如果我们尝试访问private
外部的字段,class
它将引发异常,并且不会undefined
像字段那样返回public
。
我们可以尝试使用一个简单的try/catch
内部class
来检查该字段是否存在:
class ButtonToggle extends HTMLElement {
// initialised as null
#value = null;
get #getValue(){
if(!this.#value){
throw new Error('no value');
}
return this.#value
}
static isButtonToggle(obj){
try {
obj.#getValue;
return true;
} catch {
// could be an error internal to the getter
return false;
}
}
}
在上面的例子中,我们添加了一个private
getter
,如果还没有值,它会抛出一个错误。然后,我们创建了一个static
方法来访问它getter
,并尝试通过检查 来判断它是否存在try/catch
。问题在于,我们不知道 中的代码是catch
因为 不存在而执行getter
的,还是仅仅因为它抛出了错误。
class
ES2022 为我们提供了一种简单的方法,通过使用运算符 来检查所述字段是否属于 a in
。让我们重新编写示例代码:
class ButtonToggle extends HTMLElement {
// initialised as null
value = null;
get #getValue(){
if(!this.#value){
throw new Error('no value');
}
return this.#value;
}
static isButtonToggle(obj){
return #value in obj && #getValue in obj
}
}
我们的方法isButtonToggle
将检查是否class
包含private
字段“value”和“getValue”。
类静态块
这是 ES2022 对字段的又一次升级,static
允许我们static
在类内部使用块。它试图解决的问题在于,我们无法try/catch
在初始化期间执行诸如 a 之类的语句,这意味着我们必须将该代码放在主体之外class
:
class ButtonToggle{
value = false;
get getValue(){
if(!this.#value){
throw new Error('no value');
}
return this.#value
}
}
// this has to sit outside of the class body
try {
const val = ButtonToggle.getValue;
ButtonToggle.value = val
} catch {
ButtonToggle.value = false
}
正如你所见,我们的try/catch
代码必须放在class
body 之外。幸运的是,我们可以用static
下面的代码块来代替它:
// method defined outside of the class body
let initVal;
class ButtonToggle{
#value = false;
get getValue(){
if(!this.#value){
throw new Error('no value');
}
return this.#value
}
static {
initVal = () => {
this.#value = this.getValue;
}
}
}
initVal();
我们static
在 our 内部创建了一个块class
,它定义了一个在 that 上下文之外声明的函数class
。如你所见,该方法将可以访问“#value”,它是private
我们类的一个字段。它们将可以访问private
方法和字段,无论是 them instance-private
(即非static
,private
字段)还是static-private
。
正则表达式匹配索引
这次升级将允许我们使用d
字符来指定我们想要获取 RegExp 匹配的索引(开始和结束)。
我们可以使用Regexp.exec
或String.matchAll
来查找匹配列表,它们之间的主要区别在于,Regexp.exec
一个接一个地返回结果,而 则String.matchAll
返回一个迭代器。让我们在实践中看看它们:
const fruits = 'Fruits: mango, mangosteen, orange'
const regex = /(mango)/g;
// .exec
RegExp(regex).exec(fruits);
// [
// 'mango',
// index: 8,
// input: 'Fruits: mango, mangosteen, orange',
// groups: undefined
// ]
// matchAll
const matches = [...fruits.matchAll(regex)];
matches[0];
// [
// 'mango',
// 'mango',
// index: 8,
// input: 'Fruits: mango, mangosteen, orange',
// groups: undefined
// ]
两者都返回匹配的索引、匹配本身以及初始输入。我们不知道字符串结束的索引,现在可以这样做:
const fruits = 'Fruits: mango, mangosteen, orange'
// /gd instead of the previous /g
const regex = /(mango)/gd;
const matches = [...fruits.matchAll(regex)];
matches[0];
// [
// "mango",
// "mango",
// groups: undefined
// index: 8
// indices:[]
// [8, 13],
// [8, 13]
// ]
// groups: undefined
如您所见,它返回 [8,13] 作为字符串中第一次出现“mango”的索引。]
顶级 await
“await
运算符只能在async
方法中使用”可能是您经常遇到的错误。在 ES2022 中,我们将能够async
在模块中的方法上下文之外使用它。例如,我们可以推迟模块及其父模块的执行,直到导入其他内容。
这在许多情况下都很有用,例如当我们有一个依赖于运行时值的依赖项的动态路径时:
// we need to get the appropriate translation keys based on the language
const translationKeys = await import(`/i18n/${navigator.language}`);
另一个用途是为依赖项提供后备:
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
。在()
在 中,JavaScript
您可以arr[1]
访问 中索引 1 处的值Array
,但无法arr[-1]
从 的末尾倒数Array
。原因是括号语法不仅适用于数组,也适用于对象,在 中obj[-1]
, 仅引用该 的属性 '-1' Object
。
使用 .at()
方法,我们现在可以轻松访问数组和字符串的任何索引(正数或负数):
const arr = [10,20,30,40];
// same -> 10
arr[1];
arr.at(1);
// same -> 40
arr[arr.length -1];
arr.at(-1);
请注意,负值只是意味着:“从数组末尾开始向后计数”。
可访问的 Object.prototype.hasOwnProperty
在 中JavaScript
我们已经有一个Object.prototype.hasOwnProperty
,但是,正如 MDN 文档所建议的那样,最好不要hasOwnProperty
在原型之外使用,因为它不是一个受保护的属性,这意味着可以具有与 无关的object
属性。hasOwnProperty
Object.prototype.hasOwnProperty
例如:
const obj = {
hasOwnProperty:()=> {
return false
}
}
obj.hasOwnProperty('prop'); // false
正如您所见,我们定义了自己的方法hasOwnProperty
,该方法覆盖了原型上的方法,而这个问题并不存在Object.hasOwn()
。
Object.hasOwn()
将我们的Object
作为第一个参数,将我们要检查的属性作为第二个参数:
const student = {
name: 'Mark',
age: 18
}
Object.hasOwn(student,'age'); // true
Object.hasOwn(student,'grade'); // false
你最想尝试的功能是什么?请在下方留言。
如果你想学习 JavaScript 的所有内容,从基础到 ES2022,请查看我的书,可在Github上免费阅读。Educative上也有课程。
鏂囩珷鏉ユ簮锛�https://dev.to/albertomontalesi/everything-new-coming-in-es2022-eil