1

Disclaimer: This article is a translated article, the original text is 11 Amazing New JavaScript Features in ES13

Like other languages, JavaScript is constantly iterating and evolving. JS adds many new features every year to make itself more powerful, and it is in this way that we developers can write more expressive and accurate code.

In this article we'll take a look at 11 awesome new features that the latest ECMAScript 2022 (ES13) brings to our developers with some examples.

1. Class member declaration

Before ES13, we could only declare members of a class in the constructor, not in the outermost scope of the class like most other languages:

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

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

Honestly it's kind of inconvenient to write like this, but after ES13 comes out, it's not a big deal. Now we can finally break through this limitation and write code like this:

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

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

2. Define private methods and member variables for the class

Before ES13, it was impossible to define private members for classes. So some programmers add an underscore (_) as a suffix to the property name in order to indicate that a member variable is a private property. But this can only be used as a gentleman's contract between programmers, because these variables can still be accessed by the outside world.

 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

But after ES13 came out, mom no longer has to worry that our private properties will be accessed and changed by others. In ES13, we just need to prefix our property name with a hashtag (#) and the property becomes private. When our property becomes private, any outside access to it will go wrong.

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

It's worth mentioning here that the SyntaxError mentioned above is an error thrown at compile time, so you won't wait until your code runs to know that the property has been illegally accessed.

3. Support writing await in the outermost layer

We all know that in JS, the function of the await operator is that when we encounter a promise, we can use await to suspend the execution of the current code until the promise is settled (fulfilled or rejected) , we continue the execution of the current code.

However, when I used await before, there was a big headache. It must be used in a function of async but not in the global scope. If you write it like this, you will get an error. :

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

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

After ES13 came out, it was much more comfortable, and we could finally write code like this:

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

// 慢慢地等时间流逝吧
await setTimeoutAsync(3000);

4. Class supports defining static members and static private methods

In ES13, we can also define static members and static private functions for classes. Static methods of a class can use the this keyword to access other private or public static members, while instance methods of a class can access these static properties through 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. Class supports defining static code blocks

ES13 allows a series of static code blocks to be defined in a class via the static keyword, which will only be executed once when the class is created. This is actually a bit like the usage of static constructors in some other object-oriented programming languages such as C# and Java.

A class can define any number of static code blocks, and these code blocks will be executed once when the class is initialized, together with the static member variables interspersed between them, in the order in which they are defined. We can also use the super keyword to access the properties of the parent class.

 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. Use in to determine whether an object has a private property

The name of this new attribute is actually called Ergonomic Brand Checks for Private Fields . Forgive me for my lack of knowledge. I really don't know how to translate it, so I probably expressed its function. In general, it is used to judge whether an object has a specific private property, which is judged by the in operator.

 class Car {
  #color;

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

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

This in operator can even distinguish between private properties of the same name of different classes:

 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. Use the at function to index elements

In general if we want to access the N th element of the array, we would use square brackets [N - 1] :

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

This is fine most of the time, and other languages do it too, but when we need to access the Nth last element of the array, our code becomes a little weird:

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

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

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

Does your code look ugly all of a sudden? Don't be afraid, ES13's at() function will help you write more elegant code! Using the new new() method, when we want to access the penultimate N element, we only need to pass -N to at() Just:

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

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

Look, your code is suddenly more expressive! In addition to arrays, string and TypedArray objects also support at() functions!

 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. Supports returning start and end indices when regular expressions match strings

Simply put, this new property allows us to tell RegExp when returning a match object, the start and end indices of the matched substring.

Before ES13, we could only get the start index of the substring matched by the regular expression:

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

After ES13, we can add a d tag to the regular expression to make it return us both the starting position and the ending position of the matched substring when matching:

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

You see, after setting the d mark, there is an array of indices, which is the range of matched substrings!

9. Object.hasOwn() method

In JS, we can use Object.prototype.hasOwnProperty() to check if an object itself has a certain property:

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

const car = new Car();

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

There are actually two problems with the above writing. The first problem is: Object.prototype.hasOwnProperty() This method is unprotected, in other words, it can be overridden by a custom hasOwnProperty() method, and the custom The method may do something completely different from what Object.prototype.hasOwnProperty() does:

 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

The second problem with the above writing method is: when an object is created by Object.create(null) and has an object with null prototype, you want to call hasOwnProperty This method will report an error:

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

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

One solution to this problem is to call the Object.prototype.hasOwnProperty this Function call method:

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

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

When hasOwnProperty needs to be called multiple times, we can reduce the repetitive code by abstracting this part of the logic into a method:

 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

The package is packaged, but it looks so troublesome, is there anything? So ES13 gave birth to a new Object.hasOwn() function to help us do the above repetitive work. This new built-in function accepts two parameters, one is an object and the other is an attribute. If the object itself has this attribute, the function will return true , otherwise it will return 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. Cause property of Error object

After ES13, the Error object has an cause attribute to indicate the reason for the error. This property can help us add more contextual information to the error, thus helping users to better locate the error. This attribute is the cause attribute of the second parameter object we passed in when we created the error object:

 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. Array supports reverse order search

In JS, we can use the find() function of an array to find the first element in an array that satisfies a certain condition. Similarly, we can also return the position of this element through the findIndex() function. However, whether it is find() or findIndex() , they all start looking for elements from the head of the array, but in some cases, we may start looking for a certain element from the back of the array element needs. For example, we know that the element to be searched is at a later position, and it will have better performance to search from the back, as in the following example:

 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

In this case, find() and findIndex() can also be used, but the performance is poor. After ES13 came out, we finally had a way to handle this situation, and that was to use the new findLast() and findLastIndex() functions. Both of these functions start from the end of the array to find an element that satisfies the condition:

 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

Another scenario where we use findLast() and findLastIndex() is that we ourselves want to find the last element that satisfies a certain condition, such as finding the last even number in the array, this time also find() and findIndex() The result is wrong:

 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

Imagine that to get the correct answer, we have to use find() and findIndex() , how to change it? First we need to invert the array with reverse() and then call find() and findIndex() functions. However, there are two problems with this approach. One problem is that the original array needs to be changed. This problem can be solved by copying the array, which takes up more space. Another problem is that findIndex() after getting the index, we have to do some additional calculations to get the position of the original array of elements. The specific method is:

 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

It looks really troublesome. findLast() and findLastIndex() come out, the array supports post-order search elements to achieve the same requirements, and the code is a lot simpler:

 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

You can see that the code is much shorter, and the readability and correctness are improved!

in conclusion

Above we introduced the latest 11 properties of ES13. As a developer, we can use them to improve our productivity and write more concise and expressive code, why don't you hurry up and practice it in your project?

Personal technology trends

Pay attention to my official account - attack on the green onion to get my latest technology push

wechat.jpeg


进击的大葱
222 声望67 粉丝

Bitcoin is Noah's Ark.