ES2016-2018

Обзор важных нововведений в версиях стандарта языка JavaScript 2016 — 2018 годов

ES2016

1) Array.prototype.includes() – проверяет наличие значения в массиве (включая NaN):

const arr = [1, 3, 5, NaN];
arr.includes(5); // true
arr.includes(NaN); // true
arr.indexOf(NaN); // -1

2) Возведение в степень двумя звёздочками:

// было
const a = Math.pow(3, 5); // 243
// теперь ещё стало
const b = 7**5; // 16807

ES2017

1) Object.values() – возвращает массив значений объекта:

const obj = {
   a : 1,
   b : 2,
   c : 3
}
Object.values(obj); // [1, 2, 3]

2) Object.entries() – возвращает массив пар "ключ-значение" объекта:

const obj = {
   a : 1,
   b : 2,
   c : 3
}
Object.values(obj); // [["a", 1], ["b", 2], ["c", 3]]

3) String.prototype.padStart(), String.prototype.padEnd() – методы добавления к началу/концу строки подстроки, чтобы общая длина строки достигала заданной:

const strs = ["123", "12345", "1234567"];
strs.map((elem) => {
    console.log(elem.padStart(10, "-"));
})
// -------123
// -----12345
// ---1234567

const strs = ["98765", "9876", "987"];
strs.map((elem) => {
	console.log(elem.padEnd(7, "0"));
})
// 9876500
// 9876000
// 9870000

4) Async/Await – синтаксический сахар для работы с промисами. Async-функция всегда возвращает промис, await объявляется внутри кода async-функции и останавливает выполнение нижеследующего кода, пока не вернётся значение:

// async всегда возвращает promise
async function f() {
	return 1
}
f().then((res) => console.log(res))  // 1

// alert выполнится через 3 секунды
async function f() {
   const promise = new Promise((resolve, reject) => {
      setTimeout(() => resolve("answer"), 3000)
   });
   return(await promise); 
}
f().then((res) => alert(res));

// замена цепочки промисов на более понятные вызовы
new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
}).then(function(result) {
  console.log(result); 
  return result * 3;
}).then((result) => {
  console.log(result)	
	return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });
}).then(function(result) {
  console.log(result); 
  return result 
});

// async/await-синтаксис, с реальными fetch, а не с синтетическими таймаутами будет ещё изящнее
async function f() {
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000)
  });
  let result = await promise; 
  console.log(result);
  result = result * 3;
  console.log(result);
  promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve(result * 2), 1000)
  });
  result = await promise;
  console.log(result);
  return result;
}
f(); 

// параллельное выполнение кода, ответ придёт через 5 секунд, а не 10
async function twice(a, b) {
    [a, b] = await Promise.all([count(a), count(b)]);
    return a+b;
}

function count(n) {
    return new Promise(resolve => {setTimeout(() => {resolve(n*2)}, 5000)});
}

twice(4, 8).then(result => console.log(result))

// методы классов тоже могут быть асинхронными 
class Timer { 
    async waitASecond() {
        return await new Promise((resolve, reject) => { 
            setTimeout(() => resolve("answer"), 1000) 
         })
    }
} 
new Timer() .waitASecond() .then(alert);

ES2018

1) Операторы rest/spread. Оба обозначаются троеточием, первый используется для извлечения свойств из объекта, второй для создания:

// деструктурирование объекта

const {a, b, ...remaining} = {
    a : 1,
    b : 2,
    c : 3,
    d : 4
}
console.log(a);  // 1
console.log(b);  // 2
console.log(remaining);  // {c: 3, d: 4}

// копирование объекта 
const {...remaining} = { 
    x : 1, 
    y : [], 
    z : function() { return 1 } 
} 
console.log(remaining); // {x: 1, y: Array, z: ƒ} 

// удаление ненужного свойства объекта 
const {x, ...remaining} = { 
    x : 1, 
    y : "2", 
    z : "three" 
} 
console.log(remaining); // останется всё без ненужного свойства 

// spread объединяет свойство объектов 
const first = { 
    a : 1, 
    b : [2, 4, 6] 
} 
const second = { 
    с : "3", 
    d : function() {return 1} 
}
const common = {...first, ...second}; // {a: 1, b: Array(3), с: "3", d: ƒ} 

// при создании объекта с помощью spread свойства могут перезаписываться, если объявлять их после 
const first = { 
    a : 1, 
    b : 2
} 
const second = {...first, b : 3}; // {a: 1, b: 3} 

// но сохраняются, если объявление было до 
const first = {
    a : 1, 
    b : 2
} 
const second = {b : 3, ...first}; // {b: 2, a: 1}

2) Promise.finally() – вызывается в промисах после resolve и reject, независимо от их результата. Это позволяет вам избавиться от дубликации кода в обработчиках then() и catch():

new Promise((resolve, reject) => {
    resolve(1)
})
    .then((res) => {console.log(res)})
    .catch((error) => {console.log(error)})
    .finally(() => {
        console.log("This is finally")
    })
// 1, затем This is finally

new Promise((resolve, reject) => {
    reject(5)
})
    .then((res) => {console.log(res)})
    .catch((error) => {console.log(error)})
    .finally(() => {
        console.log("This is finally")
    })
// 5, затем This is finally

3) Асинхронная итерация for-await-of

const promises = [
    new Promise((resolve, reject) => resolve(1)),
    new Promise((resolve, reject) => resolve(2)),
    new Promise((resolve, reject) => resolve(3))
]

async function f1() {
    for (let i of promises) {
        console.log(i)
    }
}
async function f2() {
    for await (let i of promises) {
        console.log(i)
    }
}

f1();  // Promise, Promise, Promise
f2();  // 1, 2, 3

Теги: