ECMAScript 2022 - это новый стандарт JavaScript, который будет выпущен в июне 2022 года. Давайте посмотрим на самые важные изменения, которые, наиболее вероятно, должны появиться в новом релизе, так как они достигли уже 4-ой, последней стадии принятия новвоведений в спецификации EcmaScript (TC39).

TC39 - это группа JavaScript разработчиков, которые принимают и выпускают новвоведения. В их процессе добавления улучшений в язык есть 4 стадии, где первая - это просто предложенная идея, а четвертая - обновление, готовое к выпуску в новой версии ECMAScript.

1. Метод "at()" в массивах

Наконец-то! ES2022 дал нам возможность обращаться к массивам с конца. Это незначительное новшество увеличит удобочитаемость кода при работе с массивами и строками.

At() метод с положительным числом работает так же, как и [ ], но передача отрицательного числа в этот метод позволяет нам получать значения с конца.

Вместо того, чтобы писать:

const arr = [1,2,3,4]
arr[arr.length - 2] // 3
arr.slice(-2)[0]    // 3

const str = "1234"
str[str.length - 2] // '3'
str.slice(-2)[0]    // '3'

Мы сможем писать:

const arr = [1,2,3,4]
arr.at(-2) // 3

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

2. Error cause

Свойство .cause в объекте ошибки позволяет нам указать, какая ошибка спровоцировала другую ошибку. Довольно очевидно, не так ли? Пример использования данного свойства:

try {
  //Выполняем какое-то действие, которое выбросит ошибку
  doSomeComputationThatThrowAnError() 
} catch (error) {
  throw new Error('Я результат другой ошибки', { cause: error })
}

Error cause будет идеальным решением для связки ошибок в цепочки, подобное есть в других языках программирования, например, в Java.

3. Ключевое слово "await" вне функции

Знали ли вы, что нельзя использовать await в коде вне функции? Если нет, то для вас это не так важно. Но остальные могут быть уверены, что ES2022 изменит это.

В чём польза?

  • Это позволяет загружать модули динамически

const serviceName = await fetch("https://example.com/what-service-should-i-use")
const service = await import(`/services/${serviceName}.js`)

// ИЛИ

const params = new URLSearchParams(location.search);
const theme = params.get('theme');
const stylingFunctions = await import(`/styling-functions-${theme}.js`);
  • Это позволяет загружать модули условно

const date = new Date()

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

Я почти уверен, что есть и больше вариантов использования обновленного await (возможно, менее абстрактного, чем в приведенных примерах). Пишите свои примеры в комментариях!

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

Классы в JavaScript были представлены еще в ES6, но их реализация едва ли соответствовала ООП. Много разработчиков использовали TypeScript для включения некоторых новых возможностей, а сейчас мы можем их использовать в чистом JavaScript.

Приватные поля или же свойства - одна из этих возможностей. ES2022 даёт нам возможность создавать их и получать ошибку, когда мы пытаемся к ним обратиться, находясь вне класса. Аналогично и с приватными методами. Интересно, что команда JavaScript решила использовать # в виде префикса для обозначения подобных полей.

Пример приватного поля:

class Human {
  #name = "John";
  
  setName(name) {
    this.#name = name;
  }
}

const human = new Human()
human.#name = 'Amy'  // ОШИБКА!
human.setName('Amy') // ОК

И приватного метода:

class Human {
  name = "John";
  
  constructor(name) {
    this.#setName('Amy') // OK
  }
  
  #setName(name) {
    this.name = name;
  }
}

const human = new Human()
human.#setName('Amy') // ОШИБКА!

Итог

Список неполный, но я выбрал, по моему мнению, наиболее важные и полезные обновления в новом выпуске. Вы можете найти все улучшения здесь. Дайте знать, какие фичи вам больше всего понравились, и какими вы планируете пользоваться.

P.S. Это был мой первый перевод. А вот оригинальная статья.

Комментарии (28)


  1. powerman
    13.02.2022 00:57
    +11

    Интересно, что команда JavaScript решила использовать #

    "Интересно" - это эвфемизм?


  1. azizoid
    13.02.2022 04:21
    +6

    Мои любимые:

    a ||= b // вместо a || (a = b)
    a &&= b // вместо a && (a = b)
    a ??= b // вместо a ?? (a = b)


    1. Shannon
      13.02.2022 16:21

      Это фича из ES2021, поэтому уже везде доступно, а ES2022 будет принят в стандарт летом, поэтому пока только под флагом работает или через полифилы.
      Из не указанного в статье, еще будет добавлен модификатор d для regexp, метод hasOwn, который можно использовать вместо in, может еще что-то


  1. rqdkmndh
    13.02.2022 10:31

    Цепочки ошибок - очень хорошо. А вот аваэйтами вне функций - как то спорно. Смысл авейта внутри ф-ции как раз и состоит в том, интерпретатор может выйти из неё и продолжить работу основного кода. А если авейт встретится в основном коде - застопорится всё наглухо, или как?


    1. Cerberuser
      13.02.2022 11:24

      По идее, интерпретатор просто закончит текущую итерацию event loop и возобновит его, как только сработает событие, на котором завязан await.


    1. vanxant
      13.02.2022 15:26
      +1

      Это нужно например для всяких скриптов сборки и прочих там тестов, когда вам нужно дождаться завершения асинхронной функции. Сейчас в этом случае нужно писать ещё одну асинхронную функцию, единственный смысл которой — сделать await внутри. Как бы зачем?


      1. rqdkmndh
        14.02.2022 00:45

        Я просто уже представил себе килотонны кода, написанных горекодерами, в которых десятки запросов будут авэйтится последовательно в основном потоке... Надеюсь, мне не придеться разгребать такое.


        1. Chamie
          14.02.2022 04:15

          В смысле, авейтиться синхронно? Я думаю, просто модуль, при появлении в нём авейтов, будет считаться асинхронным.


  1. Saiv46
    13.02.2022 10:53
    +1

    class Translator {
      static translations = {
        yes: 'ja',
        no: 'nein',
        maybe: 'vielleicht',
      };
      static englishWords = [];
      static germanWords = [];
      static { // (A)
        for (const [english, german] of Object.entries(this.translations)) {
          this.englishWords.push(english);
          this.germanWords.push(german);
        }
      }
    }

    Моё любимое - теперь не надо писать код за пределами класса, при этом всё это будет наследоваться.


    1. Alendorff
      14.02.2022 17:37

      Не понял что-то... Что это за Статик блок с доступом к this?

      Ага, почитал. Код инициализации Статик полей.

      Тут разбор есть https://2ality.com/2021/09/class-static-block.html


  1. vasyakolobok77
    13.02.2022 11:02
    +1

    Cause в обработку ошибок стоило добавить уже давно. Для полноты картины не хватает еще supressed. Но в принципе, те, кто хочет попробовать cause в текущем коде, могут воспользоваться таким workaround-ом:

    try {
        ...
    } catch (x) {
        const e = new Error('My error');
        e.stack+= '\nCaused by: ' + x.stack;
        throw e;
    }

    Приватные поля доступны через babel уже давно. Единственное, babel транспайлит доступ к таким полям в адовые цепочки вызовов и время доступа серьезно возрастает. С нативной поддержкой рантайма проблема должна уйти.

    Метод at() тоже выглядит удобным, правда в обычной жизни не так часто применим.


  1. Pavel1114
    13.02.2022 11:17

    at() выглядит как костыль


    1. arthurlomakin
      13.02.2022 12:07
      +2

      Да и # выглядит как костыль


      1. vendelieu
        14.02.2022 10:42
        -1

        js выглядит как костыль


    1. polearnik
      13.02.2022 20:54
      +2

      обычный синтактический сахар


      1. Pavel1114
        14.02.2022 03:36
        +4

        [-1] — синтаксический сахар, а at() — костыль


        1. Artima
          14.02.2022 08:14
          +1

          Да, могли бы завезти: [-1], [1:3], [-1:] и т.д.


          1. faiwer
            14.02.2022 12:21
            +1

            1-е сломало бы обратную совместимость.


  1. arthurlomakin
    13.02.2022 12:06

    del


  1. kawena54
    13.02.2022 13:19
    -8

    скажите import появился без разминования для написания в одну строку?

    то в vue 3 + vite requer нету , типа:

    let placeholder = ref(import '/assets/placeholder.png')

    или

    let placeholder = ref(import from '/assets/placeholder.png')

    и второй вопрос:

    в общем опять же в vue 3 + vite (не знаю влияет это на ответ) сделал return array.sort() и это работает, потом задумался и вспомил что sort возвращает void и вы можете это загуглить. как это работает?


  1. SniperDes
    13.02.2022 15:23
    +3

    Для 3 пункта хорошо бы добавить об ограничении использования await: только на верхнем уровне модуля (в оригинальной статье "top-level await"). А то из текста получается, будто теперь его вообще везде можно писать без async.


    1. kawena54
      13.02.2022 17:49

      всё верно можно и в топ уровне , там не только же про веб, но и про nodejs


  1. corporateanon
    13.02.2022 15:23
    +4

    Ключевое слово private было бы намного читабельнее.


    1. Shannon
      13.02.2022 17:29
      +2

      Есть даже FAQ от tc39 почему # вместо private
      github.com/tc39/proposal-private-fields/blob/main/FAQ.md

      Если вкратце, то из-за того, что обращение к this.x неявно создает публичное поле, даже если создано приватное, то 2 основных пункта почему отказались от private:
      — чтобы код на js (доступ к полям) работал так же быстро, как и до введения приватных полей, не вводя дополнительные сложности и проверки в рантайме.
      — чтобы неявно не нарушать инкапсуляцию, если подкласс захочет иметь публичный x, а суперкласс имеет приватный x.

      class Base {
          #x = 0
          getPrivateX() {
              return this.#x
          }
      }
      class Derived extends Base {
          x = 1
      }
      const example = new Derived()
      console.log(`public: ${example.x}, private: ${example.getPrivateX()}`)
      
      > public: 1, private: 0

      Также там приводят в пример, что в C# можно обойти инкапсуляцию, и единственное гарантированное решение это явно использовать синтаксис @x или #x.

      И по сути, до этого негласным правилом было называть приватные поля с подчеркивания, то использование # будет просто более явным.
      В итоге выбрали # методом исключения остальных вариантов, которые или не подходили, или создавали потенциальные места для ошибок программистов.
      И так как они решили, что вводить # нужно обязательно по разным причинам, то использование дополнительно к этому слова private будет уже избыточным.


    1. gwisp
      13.02.2022 19:40
      +1

      Ещё интересно, во что будут транслироваться private-поля из TypeScript. Будет ли к ним добавляться #. И если да, то не сломает ли это ничего.

      UPD: этот вопрос подробно рассмотрен здесь. TLDR: не будет. То есть фактически будет два типа private: # и старый.


  1. axmed2004
    14.02.2022 10:42
    -1

    Почему нельзя индексы массивов сделать как в питоне? Очень удобно же имхо


  1. asnow
    14.02.2022 10:43
    -1

    Столько костылей, уже браузерам пора переходить на новый язык, и испортить файлы в новом расширении