和许多其他编程语言一样,JavaScript 不断发展。每年,这种语言都会通过新的功能变得更加强大,让开发人员编写更加具有表现力和简洁性的代码。
让我们探讨 ECMAScript 2022(ES13)中添加的最新功能,并查看其用法示例,以更好地理解它们。
1. 类字段声明
在 ES13 之前,类字段只能在构造函数中声明。与许多其他语言不同,我们不能在类的最外层范围中声明或定义它们。
ES13 取消了这个限制。现在我们可以编写这样的代码:
class MyClass {
myField = 42;
}
2. 私有方法和字段
以前,无法在类中声明私有成员。传统上,成员前缀带有下划线(_
)表示它是私有的,但仍然可以从类外部访问和修改它。
使用 ES13,我们现在可以通过在字段或方法前缀中加上井号(#
)来向类添加私有字段和成员。尝试从类外部访问它们将导致错误:
class MyClass {
#myPrivateField = 42;
getMyPrivateField() {
return this.#myPrivateField;
}
}
console.log(new MyClass().getMyPrivateField()); // 42
console.log(new MyClass().#myPrivateField); // SyntaxError
请注意,此处抛出的错误是语法错误,它发生在编译时,因此代码的任何部分都不会运行。编译器不希望你尝试从类的外部访问私有字段,因此它假设你正在声明一个。
3. 顶层的 await 运算符
在 JavaScript 中,await
运算符用于暂停执行,直到 Promise
被解决(成功或拒绝)。
以前,我们只能在 async
函数中使用此运算符 - 一个使用 async
关键字声明的函数。我们不能在全局范围内这样做。
使用 ES13,现在我们可以:
await Promise.resolve('Hello, world!');
4. 静态类字段和静态私有方法
在 ES13 中,我们现在可以为类声明静态字段和静态私有方法。静态方法可以使用 this
关键字访问类中的其他私有/公共静态成员,而实例方法可以使用 this.constructor
访问它们。
class MyClass {
static #myPrivateStaticField = 42;
static getMyPrivateStaticField() {
return this.#myPrivateStaticField;
}
}
console.log(MyClass.getMyPrivateStaticField()); // 42
console.log(MyClass.#myPrivateStaticField); // SyntaxError
5. 类静态块
ES13 允许定义仅在创建 类 时执行的 static
块。这类似于其他支持面向对象编程的语言(如 C# 和 Java)中的静态构造函数。
一个类可以在其类体中拥有任意数量的 static {}
初始化块。它们将按照它们被声明的顺序执行,以及任何交错的静态字段初始化器。我们可以在 static
块中使用 super
属性来访问超类的属性。
class MyClass extends MySuperClass {
static {
console.log('Static block');
}
constructor() {
super();
console.log('Constructor');
}
}
6. 用于私有字段的人性化品牌检查
我们可以使用这个新功能来检查对象是否具有特定的私有字段,使用 in
运算符。
in
运算符可以正确地区分来自不同类的相同名称的私有字段:
class MyClass1 {
#myPrivateField = 42;
}
class MyClass2 {
#myPrivateField = 'Hello, world!';
}
console.log(#myPrivateField in new MyClass1()); // false
console.log(#myPrivateField in new MyClass2()); // false
7. 用于索引的 at() 方法
我们通常使用方括号([]
)在 JavaScript 中访问数组的第 N
个元素,这通常是一个简单的过程。我们只需访问数组的第 N - 1
个属性即可。
但是,如果我们想使用方括号访问数组末尾的第 N
个项,则必须使用索引 arr.length - N
。
新的 at()
方法让我们更简洁、更有表现力地实现这一点。要访问数组末尾的第 N
个元素,我们只需将 -N
的负值传递给 at()
。
除了数组之外,字符串和 TypedArray
对象现在也具有 at()
方法。
8. 正则表达式匹配索引
这个新功能允许我们指定我们想要在给定字符串中获取 RegExp
对象匹配项的起始和结束索引。
以前,我们只能在字符串中获取正则表达式匹配的起始索引。
我们现在可以指定一个 d
正则表达式标志,以获取匹配开始和结束的两个索引。
const string = 'Hello, world!';
const regexp = /world/;
regexp.lastIndex = 7;
console.log(string.match(regexp)); // ["world", index: 7, input: "Hello, world!"]
console.log(string.match(regexp, 'd')); // ["world", indices: [7, 12], input: "Hello, world!"]
设置 d
标志后,返回的对象将具有一个 indices
属性,其中包含起始和结束索引。
9. Object.hasOwn() 方法
在 JavaScript 中,我们可以使用 Object.prototype.hasOwnProperty()
方法来检查对象是否具有给定属性。
但是,使用这种方法存在某些问题。首先,Object.prototype.hasOwnProperty()
方法没有受到保护 - 它可以通过为类定义自定义 hasOwnProperty()
方法来覆盖,这可能与 Object.prototype.hasOwnProperty()
完全不同的行为。
另一个问题是,对于使用 Object.create(null)
创建的 null
原型对象,尝试在其上调用此方法将导致错误。
解决这些问题的一种方法是使用 call()
方法调用 Object.prototype.hasOwnProperty
Function
属性,例如:
Object.prototype.hasOwnProperty.call(obj, 'myProperty');
这不是很方便。我们可以编写可重用的函数以避免重复:
function hasOwn(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
不需要这样,因为我们可以使用新的内置 Object.hasOwn()
方法。与我们的可重用函数类似,它接受对象和属性作为参数,并在指定属性是对象的直接属性时返回 true
。否则,它返回 false
。
Object.hasOwn(obj, 'myProperty');
10. 错误原因
错误对象现在具有一个 cause
属性,用于指定导致要抛出的错误的原始错误。这有助于为错误添加其他上下文信息,并帮助诊断意外行为。我们可以通过在传递给 Error()
构造函数的第二个参数的对象上设置 cause
属性来指定错误的原因。# 11. 从数组末尾查找元素
在 JavaScript 中,我们已经可以使用 Array
的 find()
方法来查找一个在数组中通过指定测试条件的元素。类似地,我们可以使用 findIndex()
来查找这样一个元素的索引。虽然 find()
和 findIndex()
都从数组的第一个元素开始搜索,但有时从最后一个元素开始搜索会更好。
有些情况下,我们知道从最后一个元素开始查找可能会获得更好的性能。例如,在这里我们尝试获取数组中 value
属性等于 y
的项。使用 find()
和 findIndex()
:
const letters = [
{ value: 'v' },
{ value: 'w' },
{ value: 'x' },
{ value: 'y' },
{ value: 'z' },
];const found = letters.find((item) => item.value === 'y');
const foundIndex = letters.findIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3
这可以工作,但由于目标对象更靠近数组的尾部,如果我们使用 findLast()
和 findLastIndex()
方法从尾部搜索数组,则可以使程序运行得更快。
const letters = [
{ value: 'v' },
{ value: 'w' },
{ value: 'x' },
{ value: 'y' },
{ value: 'z' },
];const found = letters.findLast((item) => item.value === 'y');
const foundIndex = letters.findLastIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3
另一个用例可能要求我们特别从末尾搜索数组以获取正确的项。例如,如果我们想在数字列表中查找最后一个偶数,则 find()
和 findIndex()
将产生错误的结果:
const nums = [7, 14, 3, 8, 10, 9];// gives 14, instead of 10
const lastEven = nums.find((value) => value % 2 === 0);// gives 1, instead of 4
const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);console.log(lastEven); // 14
console.log(lastEvenIndex); // 1
我们可以在调用 find()
和 findIndex()
之前在数组上调用 reverse()
方法来反转元素的顺序。但这种方法会导致数组不必要的变异,因为 reverse()
会原地反转数组的元素。避免这种变异的唯一方法是复制整个数组的副本,这可能会对大型数组产生性能问题。
此外,findIndex()
仍然无法在反转后的数组上工作,因为反转元素也意味着更改它们在原始数组中的索引。为了获取原始索引,我们需要执行额外的计算,这意味着编写更多的代码。
const nums = [7, 14, 3, 8, 10, 9];// Copying the entire array with the spread syntax before
// calling reverse()
const reversed = [...nums].reverse();// correctly gives 10
const lastEven = reversed.find((value) => value % 2 === 0);// gives 1, instead of 4
const reversedIndex = reversed.findIndex((value) => value % 2 === 0);// Need to re-calculate to get original index
const lastEvenIndex = reversed.length - 1 - reversedIndex;console.log(lastEven); // 10
console.log(reversedIndex); // 1
console.log(lastEvenIndex); // 4
这就是 findLast()
和 findLastIndex()
方法派上用场的情况。
const nums = [7, 14, 3, 8, 10, 9];const lastEven = nums.findLast((num) => num % 2 === 0);
const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);console.log(lastEven); // 10
console.log(lastEvenIndex); // 4
这段代码更短、更易读。最重要的是,它产生了正确的结果。
结论
所以我们已经看到了 ES13 带给 JavaScript 的最新功能。使用它们来提高开发人员的生产力,并用更简洁、更清晰的方式编写更干净的代码。
评论(0)