hasOwn, .at(), indices и другие важные изменения.

Top-level await

Теперь можно использовать await для модулей на верхних уровнях. Раньше await нельзя было использовать вне функций.

const response = await fetch('https://example.com');
const text = await response.text();

Два важнейших преимущества top-level await:

  • Гарантия того, что модули не получат доступ к асинхронному импорту, пока они не будут полностью инициализированы;
  • Прозрачная обработка асинхронности: импортёрам не нужно знать, является ли импортируемый модуль асинхронным или нет.

Также это позволит загружать модули условно:

if(download) {
  await require('/special-code-for-2023-year.js')
}

Или позволит просто указать ссылку до другого ресурса, если с первым произошла ошибка:

let lodash;
try {
  lodash = await import('https://primary.example.com/lodash');
} catch {
  lodash = await import('https://secondary.example.com/lodash');
}

Также можно использовать тот ресурс, который загрузился быстрее:

const resource = await Promise.any([
  fetch('http://example.com/first.txt')
    .then(response => response.text()),
  fetch('http://example.com/second.txt')
    .then(response => response.text()),
]);

Из-за Promise.any(), переменная resource инициализируется через ту загрузку, которая завершится первой.

Элементы класса

Приватные поля и методы.

class ClassName {
    // вместо: constructor() { this.publicNumber = 34; }
    publicNumber = 34; // публичное поле

    // вместо: static get staticPublicField() { return -1 }
    static staticPublicField = 'Публично'; // статическое публичное поле

    static #staticPrivateField = 'Приватно'; // статическое приватное поле

    #privateMethod() {} // приватный метод

    // выполняется при создании класса
    static {
      // статический блок
    }
}

x in obj

class Color {
  #name;
  constructor(name) {
    this.#name = name;
  }
  static check(obj) {
    return #name in obj;
  }
}

Indices

Если добавить флаг /dк регулярному выражению, его использование создаст объекты сопоставления, которые записывают начальный и конечный индексы каждого захвата группы. Indices предоставляет дополнительную информацию о начальных и конечных индексах захваченных подстрок относительно начала входной строки.

const execArray = /(a+)(b+)/d.exec('aaaabb');

execArray[1]; // aaaa
execArray.indices[1]; // [0, 4]

execArray[2]; // bb
execArray.indices[2]; // [4, 6]

error. cause или цепочки ошибок

Errorи его подклассы теперь позволяют узнать, какая ошибка вызвала текущую. Это позволяет облегчить диагностику приложения. Свойство .cause() в Error позволяет указать какая ошибка вызвала другую ошибку.

try {
  // сделать что-нибудь
} catch (otherError) {
  throw new Error('Это другая ошибка', {cause: otherError});
}

function readFiles(filePaths) {
  return filePaths.map(
    (filePath) => {
      try {
        // ···
      } catch (otherError) {
        throw new Error(
          `While processing ${filePath}`,
          {cause: otherError}
        );
      }
    });
}

.at()

Метод .at() позволяет читать элемент по заданному индексу, как оператор [ ], и поддерживает отрицательный индекс, в отличие от оператора [ ]. Работает со всеми индексируемыми классами Array, String и TypedArrays.

Основная задача у .at() иметь возможность выполнять «отрицательную индексацию» и упростить сложившиеся паттерны arr[arr.length -N] и arr.slice(-N)[0]. У arr[arr.length - N] есть очевидный минус: нельзя работать с анонимными значениями. Так как в JavaScript реализовать arr[-N] уже невозможно, решено было сделать методом arr.at(-N).

['a', 'b', 'c', 'd'].at(1); // 'b'
['a', 'b', 'c', 'd'].at(-1); // 'd'

const string = "1234"
string.at(-2) // '3'

hasOwn

Object.hasOwn(obj, prop) это безопасный способ проверить, есть ли у объекта собственное, не унаследованное, свойство, с помощью ключа prop.

const parent = {
  parentKey: 'parentValue'
}

const child = {
  __proto__: parent,
  childKey: 'childValue'
}

Object.hasOwn(child, 'parentKey'); //false
Object.hasOwn(parent, 'parentKey'); //true

hasOwn работает стабильнее, так как подходит для объектов созданных с помощью .create().

let person = Object.create(null);
person.name = 'Nikolai';

Object.hasOwn(person, 'name'); // true
person.hasOwnProperty('name'); // ошибка

Хотя в этом случае `hasOwnProperty` можно починить с помощью Object.prototype.hasOwnProperty.call:

Object.prototype.hasOwnProperty.call(person, 'name') // true

Изучите JavaScript в HTML Academy.

Чтобы точно знать и уметь всё самое нужное.

Записаться