关于前端:ES13的11个超赞的新属性

1次阅读

共计 8774 个字符,预计需要花费 22 分钟才能阅读完成。

申明:本文为翻译文章,原文为 11 Amazing New JavaScript Features in ES13

像其余语言一样,JavaScript 也在一直迭代和进化。JS 每年都会退出很多新的性能来让本人变得越发弱小,也正是这样,咱们开发者能力写出更加表意和精确的代码。

在这篇文章中咱们会通过一些例子来看一下最新的 ECMAScript 2022(ES13)给咱们开发者带来的 11 个超赞的新性能。

1. 类成员申明

在 ES13 之前,咱们只能在构造函数外面申明类的成员,而不能像其余大多数语言一样在类的最外层作用域外面申明成员:

class Car {constructor() {
    this.color = 'blue';
    this.age = 2;
  }
}

const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

诚实说这么写起来挺不不便的,不过 ES13 进去之后,这都不算什么事儿了。当初咱们终于能够冲破这个限度,写上面这样的代码了:

class Car {
  color = 'blue';
  age = 2;
}

const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

2. 给类定义公有办法和成员变量

ES13 之前,咱们是不可能给类定义公有成员的。所以一些程序员为了示意某个成员变量是一个公有属性,会给该属性名增加一个下划线 (_) 作为后缀。可是这只能作为程序员之间的小人之约来应用,因为这些变量其实还是能够被外界拜访到的。

class Person {
  _firstName = 'Joseph';
  _lastName = 'Stevens';

  get name() {return `${this._firstName} ${this._lastName}`;
  }
}

const person = new Person();
console.log(person.name); // Joseph Stevens

// 这些所谓的公有属性其实还是能够被外界拜访到的
console.log(person._firstName); // Joseph
console.log(person._lastName); // Stevens

// 而且还能被改来改去
person._firstName = 'Robert';
person._lastName = 'Becker';

console.log(person.name); // Robert Becker

不过 ES13 进去后,妈妈再也不必怕咱们的公有属性会被他人拜访和更改了。在 ES13 中,咱们只须要给咱们的属性名增加一个 hashtag(#)前缀,这个属性就变成公有的了。当咱们的属性变为公有后,任何外界对其的拜访都会出错哦。

class Person {
  #firstName = 'Joseph';
  #lastName = 'Stevens';

  get name() {return `${this.#firstName} ${this.#lastName}`;
  }
}

const person = new Person();
console.log(person.name);

// SyntaxError: Private field '#firstName' must be
// declared in an enclosing class
console.log(person.#firstName);
console.log(person.#lastName);

这里值得一提的是,下面说的 SyntaxError 是一个编译时抛出的谬误,所以你不会等你的代码运行后才晓得这个属性被非法拜访了。

3. 反对在最外层写 await

咱们都晓得在 JS 中,await操作符的作用就是当咱们碰到一个 promise 的时候,咱们能够应用 await 来暂停以后代码的执行,等到这个 promise 被 settled(fulfilled 或者 rejected)了,咱们才持续以后代码的执行。

可是之前应用 await 的时候有个很头疼的中央就是肯定要在一个 async 的函数外面应用而不能在全局作用域外面应用,像上面这么写就会报错:

function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();
    }, timeout);
  });
}

// SyntaxError: await is only valid in async functions
await setTimeoutAsync(3000);

ES13 进去后,就难受多了,咱们终于能够这么写代码了:

function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();
    }, timeout);
  })
}

// 缓缓地等工夫流逝吧
await setTimeoutAsync(3000);

4. 类反对定义动态成员和动态公有办法

在 ES13 中,咱们还能够给类定义动态成员和动态公有函数。类的静态方法能够应用 this 关键字拜访其余的公有或者私有动态成员,而对于类的实例办法则能够通过 this.constructor 来拜访这些动态属性.

class Person {
  static #count = 0;

  static getCount() {return this.#count;}

  constructor() {this.constructor.#incrementCount();
  }

  static #incrementCount() {this.#count++;}
}

const person1 = new Person();
const person2 = new Person();

console.log(Person.getCount()); // 2

5. 类反对定义动态代码块

ES13 容许在类中通过 static 关键字定义一系列动态代码块,这些代码块只会在类被发明的时候 执行一次。这其实有点像一些其余的如 C# 和 Java 等面向对象的编程语言的动态构造函数的用法。

一个类能够定义任意多的动态代码块,这些代码块会和穿插在它们之间的动态成员变量一起依照定义的程序在类初始化的时候执行一次。咱们还能够应用 super 关键字来拜访父类的属性。

class Vehicle {static defaultColor = 'blue';}

class Car extends Vehicle {static colors = [];

  static {this.colors.push(super.defaultColor, 'red');
  }

  static {this.colors.push('green');
  }

  console.log(Car.colors); ['blue', 'red', 'green']
}

6. 应用 in 来判断某个对象是否领有某个公有属性

这个新属性的名字其实叫做 Ergonomic Brand Checks for Private Fields,原谅我满腹经纶,我切实不晓得怎么翻译,所以大略将它的作用表白了进去。总的来说,它就是用来判断某个对象是否领有某个特定的公有属性,是通过in 操作符来判断的。

class Car {
  #color;

  hasColor() {return #color in this;}
}

const car = new Car();
console.log(car.hasColor()); // true

这个 in 操作符甚至还能够辨别不同类的同名公有属性:

class Car {
  #color;

  hasColor() {return #color in this;}
}

class House {
  #color;

  hasColor() {return #color in this;}
}

const car = new Car();
const house = new House();

console.log(car.hasColor()); // true
console.log(car.hasColor.call(house)); // false
console.log(house.hasColor()); // true
console.log(house.hasColor.call(car)); // false

7. 应用 at 函数来索引元素

一般来说如果咱们想要拜访数组的第 N 个元素,咱们会应用方括号[N - 1]:

const arr = ['a', 'b', 'c', 'd'];
console.log(arr[1]); //b

这样写大多数时候都是没有什么问题的,其余语言也是这么做的,可是当咱们须要拜访数组倒数第 N 个元素的时候,咱们的代码就变得有点奇怪了:

const arr = ['a', 'b', 'c', 'd'];

// 倒数第一个元素
console.log(arr[arr.length - 1]); // d

// 倒数第二个元素
console.log(arr[arr.length - 2]); // c

这么看你的代码是不是一下子变丑了?不必怕,ES13 的 at() 函数帮你写出更优雅的代码!应用新的 new() 办法,当咱们想要拜访倒数第 N 个元素时,咱们只须要传入 -Nat()即可:

const arr = ['a', 'b', 'c', 'd'];

// 倒数第一个元素
console.log(arr.at(-1)); // d
// 倒数第二个元素
console.log(arr.at(-2)); // c

你看,你的代码是不是一下子表意多了!除了数组,stringTypedArray 对象也反对 at() 函数哦!

const str = 'Coding Beauty';
console.log(str.at(-1)); // y
console.log(str.at(-2)); // t

const typedArray = new Uint8Array([16, 32, 48, 64]);
console.log(typedArray.at(-1)); // 64
console.log(typedArray.at(-2)); // 48

8. 正则表达式匹配字符串的时候反对返回开始和完结索引

简略来说这个新属性就是容许咱们通知 RegExp 在返回 match 对象的时候,给咱们返回匹配到的子字符串的开始和完结索引。

ES13 之前,咱们只能获取正则表达式匹配到的子字符串的开始索引:

const str = 'sun and moon';
const regex = /and/;
const matchObj = regex.exec(str);

// ['and', index: 4, input: 'sun and moon', groups: undefined]
console.log(matchObj);

ES13 后,咱们就能够给正则表达式增加一个 d 的标记来让它在匹配的时候给咱们既返回匹配到的子字符串的起始地位还返回其完结地位:

const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
/**
[
  'and',
  index: 4,
  input: 'sun and moon',
  groups: undefined,
  indices: [[ 4, 7], groups: undefined ]
]
 */
console.log(matchObj);

你看,设置完 d 标记后,多了一个 indices 的数组,外面就是匹配到的子字符串的范畴了!

9. Object.hasOwn()办法

在 JS 中,咱们能够应用 Object.prototype.hasOwnProperty() 来查看某个对象本身是否领有某个属性:

class Car {
  color = 'green';
  age = 2;
}

const car = new Car();

console.log(car.hasOwnProperty('age')); // true
console.log(car.hasOwnProperty('name')); // false

下面的写法其实是有两个问题的。第一个问题是:Object.prototype.hasOwnProperty()这个办法是不受爱护的,换句话来说就是它能够被某个类自定义的 hasOwnProperty() 办法笼罩掉,而自定义办法做的事件可能和 Object.prototype.hasOwnProperty() 做的事件齐全不一样:

class Car {
  color = 'green';
  age = 2;
  
  // 你看这个办法就没有通知咱们这个类的对象是不是有某个属性
  hasOwnProperty() {return false;}
}

const car = new Car();

console.log(car.hasOwnProperty('age')); // false
console.log(car.hasOwnProperty('name')); // false

下面的写法第二个问题就是:当一个对象是通过 Object.create(null) 创立进去的具备 null 原型的对象时,你想在这个对象下面调用 hasOwnProperty 这个办法是会报错的:

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;

// TypeError: obj.hasOwnProperty is not a function
console.log(obj.hasOwnProperty('color'));

解决这个问题的一种方法就是调用 Object.prototype.hasOwnProperty 这个 Functioncall办法:

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

Object.prototype.hasOwnProperty.call(obj, 'color')

hasOwnProperty 须要被屡次调用的时候,咱们能够通过将这部分逻辑形象成一个办法来缩小反复的代码:

function objHasOwnProp(obj, propertyKey) {return Object.prototype.hasOwnProperty.call(obj, propertyKey);
}

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

console.log(objHasOwnProp(obj, 'color')); // true
console.log(objHasOwnProp(obj, 'name')); // false

封装是封装了,不过看着好麻烦有木有?所以 ES13 诞生了一个全新的 Object.hasOwn() 函数来帮咱们做下面这些反复的工作。这个新的内置函数接管两个参数,一个是对象,一个是属性,如果这个对象自身就有这个属性的话,这个函数就会返回true,否则就返回false

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

console.log(Object.hasOwn(obj, 'color')); // true
console.log(Object.hasOwn(obj, 'name')); // false

10. Error 对象的 Cause 属性

ES13 后,Error 对象多了一个 cause 属性来指明谬误呈现的起因。这个属性能够帮忙咱们为谬误增加更多的上下文信息,从而帮忙使用者们更好地定位谬误。这个属性是咱们在创立 error 对象时传进去的第二个参数对象的 cause 属性:

function userAction() {
  try {apiCallThatCanThrow();
  } catch (err) {throw new Error('New error message', { cause: err});
  }
}

try {userAction();
} catch (err) {console.log(err);
  console.log(`Cause by: ${err.cause}`);
}

11. 数组反对逆序查找

在 JS 中,咱们能够应用数组的 find() 函数来在数组中找到第一个满足某个条件的元素。同样地,咱们还能够通过 findIndex() 函数来返回这个元素的地位。可是,无论是 find() 还是findIndex(),它们都是从数组的头部开始查找元素的,可是在某些状况下,咱们可能有从数组前面开始查找某个元素的须要。例如咱们晓得待查找的元素在比拟靠后的地位,从前面开始寻找的话会有更好的性能,就像上面这个例子:

const letters = [{ value: 'v'},
  {value: 'w'},
  {value: 'x'},
  {value: 'y'},
  {value: 'z'},
];

// 咱们想要找的 y 元素比拟靠后, 程序查找性能不好
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

在这种状况下应用 find()findIndex()也能够,就是性能差点而已。ES13 进去后,咱们终于有方法解决这种状况了,那就是应用新的 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

另外一种应用 findLast()findLastIndex()的场景就是咱们自身就是想要寻找最初一个满足某个条件的元素,例如找到数组外面最初一个偶数,这个时候还用 find()findIndex()的话失去的后果是谬误的:

const nums = [7, 14, 3, 8, 10, 9];

// 返回了 14, 后果应该是 10 才对
const lastEven = nums.find((value) => value % 2 === 0);

// 返回了 1, 后果应该是 4 才对
const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);
console.log(lastEven); // 14
console.log(lastEvenIndex); // 1

试想一下要失去正确的答案,咱们还要应用 find()findIndex()的话,要怎么改呢?首先咱们须要用 reverse() 来反转数组,而后再调用 find()findIndex()函数。不过这个做法有两个问题,一个问题就是须要扭转原数组,这个问题能够通过拷贝数组来解决,占用空间多点而已。另一个问题是 findIndex() 失去索引后咱们还要做一些额定的计算能力失去元素原数组的地位,具体做法是:

const nums = [7, 14, 3, 8, 10, 9];

// 在调用 reverse 之前先拷贝函数,以防扭转原数组
const reversed = [...nums].reverse();
// 这次返回对了,是 10
const lastEven = reversed.find((value) => value % 2 === 0);
// findIndex 失去的后果还是不对的
const reversedIndex = reversed.findIndex((value) => value % 2 === 0);
// 须要多一步计算能力得出正确后果
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 最新的 11 个属性。作为一个开发者,咱们能够应用它们来进步本人的生产效率和编写更加简洁和表意的代码,你还不连忙在我的项目外面实际一把?

集体技术动静

关注我的公众号 – 进击的大葱获取我的最新技术推送

正文完
 0