ES2022(es13) 新特性

115 阅读5分钟

1.类成员声明

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之前给类定义私有成员,会给该属性名添加一个下划线(_)作为后缀,在ES13中只需要给我们的属性名添加一个hashtag(#)前缀,这个属性就变成私有的了。

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

  #getLastName() { // 私有属性和方法只能在内部使用
     return this.#lastName;
  }
  
  get name() {
    return `${this.#firstName} ${this.#getLastName()}`;
  }
}

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

console.log(person.#firstName);
console.log(person.#lastName);

3. 类支持定义静态成员和静态私有方法

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

4. 类支持定义静态代码块

ES13允许在类中通过static关键字定义一系列静态代码块,这些代码块只会在类被创造的时候执行一次

一个类可以定义任意多的静态代码块,这些代码块会和穿插在它们之间的静态成员变量一起按照定义的顺序在类初始化的时候执行一次。我们还可以使用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']

5. 使用in来判断某个对象是否拥有某个私有属性

class Car {
  #color;

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

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

6.支持在最外层写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);

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

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

const arr = ['a', 'b', 'c', 'd']; 
console.log(arr[1]); // b 
// 倒数第一个元素 
console.log(arr[arr.length - 1]); // d 
// 倒数第二个元素 
console.log(arr[arr.length - 2]); // c

使用新的at方法,当我们想要访问倒数第N个元素时,我们只需要传入-Nat()即可(正负数都支持,不传参时为0,正数即为当前下标的项):

const arr = ['a', 'b', 'c', 'd']; 
// 倒数第一个元素 
console.log(arr.at(-1)); // d 
// 倒数第二个元素 
console.log(arr.at(-2)); // c

8.正则表达式匹配字符串的时候支持返回开始和结束索引

简单来说这个新属性就是允许我们告诉RegExp在返回match对象的时候,给我们返回匹配到的子字符串的开始和结束索引。

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

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

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

ES13后,我们就可以给正则表达式添加一个d的标记来让它在匹配的时候给我们既返回匹配到的子字符串的起始位置还返回其结束位置:

const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
console.log(matchObj);
/**
[
  'and',
  index: 4,
  input: 'sun and moon',
  groups: undefined,
  indices: [ [ 4, 7 ], groups: undefined ]
]
 */
// 多了一个indices的数组,里面就是匹配到的子字符串的范围了!

9. Object.hasOwn()方法

在JS中,我们可以使用Object.prototype.hasOwnProperty()来检查某个对象自身是否拥有某个属性:

class Car {
  color = 'green';
  age = 2;
  // 自定义改写hasOwnProperty
  hasOwnProperty() {
    return false;
  }
}
const car = new Car();
console.log(car.hasOwnProperty('age')); // false
console.log(car.hasOwnProperty('name')); // false

// 所以一般使用Object.prototype.hasOwnProperty.call(obj, key);来判断,但是比较繁琐

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 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