首页
Preview

ES13 中的 11 个令人惊叹的新 JavaScript 特性

和许多其他编程语言一样,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 中,我们已经可以使用 Arrayfind() 方法来查找一个在数组中通过指定测试条件的元素。类似地,我们可以使用 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 的最新功能。使用它们来提高开发人员的生产力,并用更简洁、更清晰的方式编写更干净的代码。

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
一个人玩
先找到想要的,然后出发

评论(0)

添加评论