Что нового появилось в языке JavaScript к 2020 году

Что появилось в языке за последнее время? Довольно много всего написано про ES6, но неясно что происходило после него.

Сейчас все пользуются именно версией ES6?

Пробовала искать в Яндексе, там ссылки на документацию где убористым шрифтом что то написано, а мне интересно что за фишки появились с 2015 года.

Чем Вы пользуетесь?

Что удобнее стало по сравнению с ES6?


Ответы (1 шт):

Автор решения: Vasily

Optional chaining

Раньше если нужный элемент не присутствовал в DOM, а мы пытались к нему обратиться:

document.querySelector("#roooot").classList
// Uncaught TypeError: Cannot read property "classList" of null

Теперь мы можем добавить знак вопроса перед точкой, что бы решить эту проблему:

document.querySelector("#roooot")?.classList
// undefined
// и никаких ошибок 

Это особенно удобно если мы не знаем есть ли нужный(ые) ключ(и) в объекте:

user.details.address.country
// Uncaught ReferenceError: user is not defined

Для того что бы не писать проверку типа:

if (user && user.details && user.details.address && user.details.address.country) {}

Теперь достаточно:

user?.details?.address?.country
// undefined
// и никаких ошибок

подробнее про оператор опциональной последовательности можно почитать здесь.


Nullish coalescing operator

Раньше при использовании логического ИЛИ мы могли удобно проверять на falsy-значения:

value || "not falsy"

// условие сработает если value будет равно
// false, 0, 0n, null, undefined, NaN, или пустой строке

Теперь с помощью оператора ?? мы можем так же удобно проверять исключительно на null или undefined:

value ?? "not null or undefined"

// условие сработает если value будет равно
// null или undefined

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


Dynamic imports

Теперь мы можем динамически загружать скрипты на клиенте, описанные как модули, используя для этого промисы или синтаксис async/await:

// в файле index.html подключен
<script src="base.js"></script>

// файл module.js содержит    
export const func = () => {
  console.log("we are in module")
}

// добавляем импорт модуля в файл base.js 
(async () {
  const module = await import("./module.js") 
  module.func()
  // we are in module 
})()

// все это возможно без использования
// сборщиков типа webpack'a

подробнее про динамические импорты можно почитать здесь.


Методы класса Promise

allSettled

Раньше, при использовании метода all если на одном из промисов срабатывал onreject:

const one = Promise.resolve("one")
const two = Promise.reject("something went wrong")

;(await () => {
  const all = await Promise.all([one, two])  
  console.log(all)
  // получаем ошибку
  // UnhandledPromiseRejectionWarning: something went wrong
  // конечно можно (нужно) добавить обработчик
  // но гораздо проще воспользоваться
})()

Теперь, при использовании метода allSettled мы можем получить результаты по всем завершенным промисам в удобном формате:

(await () => {
  const all = await Promise.allSettled([one, two])  
  console.log(all)
  // [
  //  { status: "fulfilled", value: "one" },
  //  { status: "rejected", reason: "something went wrong" },
  // ]
})()

any

(experimental)

Данный метод перебирает промисы и возвращает первый, который выполнен успешно, даже если другие перед этим отработали onreject:

const one = Promise.reject("rejected promise")
const two = new Promise(resolve => setTimeout(() => resolve("two"), 100))
const three = new Promise(resolve => setTimeout(() => resolve("three"), 1000))

;(async () => {
  const all = await Promise.any([one, two, three])  
  console.log(all)
  // two
})()

В случае если не один из промисов не завершен успешно, то будет возвращен новый подкласс ошибки AggregateError, который сгруппирует неудачи каждого из промисов в один логический блок:

const one = Promise.reject("one")
const two = Promise.reject("two")
const three = Promise.reject("three")

;(async () => {
  const all = await Promise.any([one, two, three])  
  console.log(all)
  // AggregateError ...
})()

Последний вариант будет удобно использовать как противоположность методу all.


Class Fields

Public

Instance Fields

Теперь, для того что бы создать поле, которое будет доступно каждому instanc'у класса, мы можем делать так:

class ExampleOne {
  country = "Russia"
}

const exampleOne = new ExampleOne()
console.log(exampleOne.country)
// Russia

class ExampleTwo {
  country
}

const exampleTwo = new ExampleTwo()
console.log(exampleTwo.country)
// undefined

подробнее про публичные поля экземпляра можно посмотреть здесь.

Static Fields

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

class Example {
  static country = "Russia"
}

console.log(Example.country)
// Russia

const example = new Example()

console.log(example.country)
// undefined

// они не переопределяются в наследниках класса

class Example {
  static country = "Russia"
}

class AnotherExample extends Example {
  static country = "USA"
}

console.log(Example.country)
// Russia

console.log(AnotherExample.country)
// USA

подробнее про публичные статические поля можно посмотреть здесь.

Private

Instance Fields

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

class Example {
  #country = "Russia"
}

console.log(Example.country)
// undefined

console.log(Example.#country)
// SyntaxError: Private field "#country" must be declared in an enclosing class

подробнее про приватные поля экземпляра можно посмотреть здесь.


Цикл for await...of

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

const one = new Promise(resolve => setTimeout(() => resolve("one"), 1000))
const two = new Promise(resolve => setTimeout(() => resolve("two"), 2000))
const promises = [one, two]

;(async () => {
  for (let promise of await Promise.all(promises)) {
    // метод all БУДЕТ ЖДАТЬ ЗАВЕРШЕНИЯ ВСЕХ ПРОМИСОВ
    // и только после этого начнет перебор
    // в консоли результаты будут выведены одновременно
    console.log(promise)
    // one
    // two
  }
})()

Теперь мы можем сделать так:

(async () => {
    for await (let promise of promises) {
        // получаем вывод в консоль в тот момент 
        // когда конкретный промис выполняется
        console.log(promise)        
        // one
        // two
    }
})()

// в консоли мы увидим "one" спустя одну секунду
// после начала выполнения функции
// а спустя еще одну секунду 
// (в общей сложности через две)
// будет напечатано "two" 

подробнее про цикл for await...of можно посмотреть здесь.


Методы Объектов

getOwnPropertyDescriptor

Теперь если мы захотим посмотреть конфигурацию определенного поля у какого либо объекта мы можем сделать так:

const descriptor = Object.getOwnPropertyDescriptor(address, "city")

console.log(descriptor)
// {
//   value: "Moscow",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

подробнее про метод getOwnPropertyDescriptor можно посмотреть здесь.

По аналогии, что бы посмотреть конфигурацию по всему объекту, а не только по какому то его конкретному ключу присутствует метод getOwnPropertyDescriptors (обратите внимание на s в конце):

const descriptors = Object.getOwnPropertyDescriptors(address)

console.log(descriptors)
// {
//   country: {
//     value: "Russia",
//     writable: true,
//     enumerable: true,
//     configurable: true
//   },
//   city: {
//     value: "Moscow",
//     writable: true,
//     enumerable: true,
//     configurable: true
//   }
// }

Это довольно удобно когда нам нужно, например, скопировать конфигурацию объекта и использовать ее где то еще.

entries

Теперь мы можем преобразовывать объекты в формат двумерного массива:

const address = {
  country: "Russia",
  city: "Moscow" 
}

const entries = Object.entries(address)

console.log(entries)
// [
//   [ "country", "Russia" ],
//   [ "city", "Moscow" ]
// ]

Это удобно, например, при использовании вместе с циклом for...of и деструктуризацией:

for (let [key, value] of entries) {
  console.log(key, value) 
}

// country Russia
// city Moscow

Обратный метод называется fromEntries:

const object = Object.fromEntries(entries)

console.log(object)
// { country: "Russia", city: "Moscow" }

Можно преобразовывать не только объекты:

const indexedArray =  Object.entries([10, 20, 30])

console.log(indexedArray)
// [ 
//   [ "0", 10 ], 
//   [ "1", 20 ], 
//   [ "2", 30 ] 
// ]

values

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

const values = Object.values(address)

console.log(values)
// [ "Russia", "Moscow" ]

Методы Массивов

flat

Теперь мы можем легко убирать уровень(ни) вложенности в массивах:

const array = [ [1, 2], [3, [ 4, 5 ]] ]

let flatted = array.flat()

console.log(flatted)
// [ 1, 2, 3, [ 4, 5 ] ] 

При необходимости так же можно указать нужную глубину:

flatted = array.flat(2)

console.log(flatted)
// [ 1, 2, 3, 4, 5 ]

подробнее про метод flat можно посмотреть здесь.


Строки

Tagged templates

Теперь мы можем легко отфильтровать строку-шаблон от динамических значений, которые мы в нее передаем:

const address = {
  country: "Russia",
  city: "Moscow"
}

const splitTemplate = (string, city, country) => {
  console.log(string, country, city)
  // [ "I'm from ", ', ', '.' ] Moscow Russia
  // в первом массиве собраны все строки
  // а динамические значения идут отдельно   
}

splitTemplate`I\'m from ${address.city}, ${address.country}.`

подробнее про теговые шаблоны можно посмотреть здесь.

Методы

padStart/padEnd

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

const string = "Мне очень нравиться Stack Overflow"

const modified = string.padEnd(40, "!")

console.log(modified)
// Мне очень нравиться Stack Overflow!!!!!!

подробнее про методы padStart и padEnd.

matchAll

Теперь можно сразу получить все результаты совпадений, которые прошли проверку:

const getAll = string => {
    return [...string.matchAll(/d(b+)d/g)].flat()
}

console.log(getAll("cdbbdbsbz"))
// [ "dbbd", "bb" ]

console.log(getAll("abcdrettj"))
// []

подробнее про метод matchAll можно посмотреть здесь.


globalThis

Теперь мы можем обращаться к глобальным объектам используя globalThis.

В браузерах:

globalThis
// window

На платформе NodeJS:

globalThis
// global

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

→ Ссылка