Данная публикация является 2-ой частью перевода статьи «Introduction to commonly used ES6 features» под авторством Zell Liew, размещенного здесь. Перевод 1-ой части находится здесь.
Деструктуризация
Деструктуризация — удобный способ извлечения значений из массивов и объектов. Между деструктуризацией массивов и объектов существуют незначительные различия, поэтому рассмотрим их отдельно.
Деструктуризация объектов
Допустим, имеется следующий объект:
const Zell = { firstName: 'Zell', lastName: 'Liew' }
Для получения firstName и lastName из Zell необходимо создать две переменные, а затем присвоить каждой переменной значение следующим образом:
let firstName = Zell.firstName; // Zell let lastName = Zell.lastName; // Liew
С деструктуризацией создание и присвоение этих переменных производится одной единственной строкой кода. Ниже пример деструктуризации объекта:
let { firstName, lastName } = Zell; console.log(firstName); // Zell console.log(lastName); // Liew
При добавлении фигурных скобок к объявлению переменных конструируется указание создать эти переменные и затем присвоить Zell.firstName в firstName, а Zell.lastName в lastName.
Поясним, что происходит под «капотом»:
// То, что вы пишите: let { firstName, lastName } = Zell; // То, что ES6 выполняет: let firstName = Zell.firstName; let lastName = Zell.lastName;
Теперь если имя переменной уже занято, то невозможно объявить такую же переменную снова (особенно если вы используете let или const).
Следующий код не сработает:
let name = 'Zell Liew'; let course = { name: 'JS Fundamentals for Frontend Developers' // ... другие свойства } let { name } = course; // Uncaught SyntaxError: имя идентификатора уже было объявлено
В таких ситуациях можно переименовывать переменные одновременно с деструктуризацией с помощью двоеточия.
В примере ниже создается переменная courseName и ей присваивается course.name.
let { name: courseName } = course; console.log(courseName); // JS Fundamentals for Frontend Developers // То, что ES6 выполняет: let courseName = course.name;
Обратим внимание на то, что если деструктурировать переменную, которая отсутствует в объекте, то будет возвращено undefined.
let course = { name: 'JS Fundamentals for Frontend Developers' } let { package } = course; console.log(package); // undefined
Помните параметры по умолчанию? Также можно записывать параметры по умолчанию для деструктурированных переменных. Синтаксис похож на синтаксис объявления функций.
let course = { name: 'JS Fundamentals for Frontend Developers' } let { package = 'full course' } = course; console.log(package); // full course
Вы даже можете переименовывать переменные при указании параметров по умолчанию. Необходимо составить комбинацию из двух синтаксисов, что сначала будет необычно, но к этому можно привыкнуть:
let course = { name: 'JS Fundamentals for Frontend Developers' } let { package: packageName = 'full course' } = course; console.log(packageName); // full course
Это все, что касалось деструктуризации объектов. Теперь рассмотрим деструктуризацию массивов.
Деструктуризация массивов
Деструктуризация массивов и объектов аналогичны. При работе с массивами вместо фигурных скобок используются квадратные.
При деструктуризации массива:
- 1-ая переменная — первый элемент массива
- 2-ая переменная — второй элемент массива
- и т.д.
let [one, two] = [1, 2, 3, 4, 5]; console.log(one); // 1 console.log(two); // 2
Если при деструктуризации количество переменных превышает размер массива, дополнительные переменные будут undefined.
let [one, two, three] = [1, 2]; console.log(one); // 1 console.log(two); // 2 console.log(three); // undefined
При деструктуризации массива часто извлекаются только необходимые переменные. Для получения остатка используется rest оператор следующим способом:
let scores = ['98', '95', '93', '90', '87', '85']; let [first, second, third, ...rest] = scores; console.log(first); // 98 console.log(second); // 95 console.log(third); // 93 console.log(rest); // [90, 87, 85]
Rest оператор будет подробнее представлен в следующей части. Сейчас же рассмотрим перестановку переменных с помощью деструктуризации массива.
Перестановка переменных с помощью деструктуризации массива
Допустим, имеются две переменные a и b.
let a = 2; let b = 3;
Вам необходимо переставить эти переменные так, чтобы a стало равно 3 и b равно 2. В ES5 вы бы использовали временную третью переменную для решения этой задачи:
let a = 2; let b = 3; let temp; // перестановка temp = a; // в переменной temp число 2 a = b; // в переменной a число 3 b = temp; // в переменной b число 2
Такой код работает, несмотря на неясную и сбивающую с толку логику с включением третьей переменной.
С деструктуризацией массивов в ES6 это решается следующим способом:
let a = 2; let b = 3; // перестановка деструктуризацией массива [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 2
Такой способ перестановки переменных намного проще предыдущего.
Далее рассмотрим деструктуризацию массивов и объектов в функциях.
Деструктуризация массивов и объектов при объявлении функций
Деструктуризация может использоваться буквально везде, даже при объявлении функций.
Допустим, имеется функция, которая принимает массив значений и возвращает объект с тремя верхними значениями. Такая функция аналогия того, что происходит при деструктуризации массивов.
function topThree (scores) { let [first, second, third] = scores; return { first: first, second: second, third: third } }
Альтернативный способ записи такой функции заключается в деструктуризации scores при объявлении функции. В этом случае потребуется написать на одну строку кода меньше. Также необходимо помнить, что в функцию будет передаваться массив.
function topThree ([first, second, third]) { return { first: first, second: second, third: third } }
Если мы можем комбинировать параметры по умолчанию и деструктуризацию при объявлении функций, то, что будет результатом кода ниже?
function sayMyName ({ firstName = 'Zell', lastName = 'Liew' } = {}) { console.log(firstName + ' ' + lastName); }
Во-первых, заметим, что функция принимает один аргумент-объект. Этот объект необязательный и по умолчанию равен {}.
Во-вторых, происходит попытка деструктурировать переменные firstName и lastName из переданного объекта. Если такие свойства будут найдены, то они будут использованы.
В итоге если firstName или lastName не будут определены (undefined) в объекте, то им присвоятся значения Zell и Liew, соответственно.
Итак, такая функция выводит следующие результаты:
sayMyName(); // Zell Liew sayMyName({firstName: 'Zell'}); // Zell Liew sayMyName({firstName: 'Vincy', lastName: 'Zhang'}); // Vincy Zhang
Далее рассмотрим rest и spread.
Rest параметр и spread оператор
Rest параметр и spread оператор похожи, т.к. оба обозначаются тремя точками. Однако они отличаются тем, что выполняют при использовании. По этой причине они по-разному названы, и будут рассмотрены по отдельности.
Rest параметр
В свободной трактовке rest параметр указывает взять остаток данных и обернуть его в массив. Если детально, то происходит преобразование списка разделенных запятыми аргументов в массив.
Ознакомимся с rest параметром в действии. Допустим, имеется функция add, которая суммирует свои аргументы:
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 55
В ES5 мы зависим от переменной arguments каждый раз, когда имеем дело с функцией, которая принимает неизвестное количество переменных. Переменная arguments — массивоподобный Symbol.
function sum () { console.log(arguments); } sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Arguments — Symbol (не массив)
Один из способов посчитать эту сумму аргументов состоит в том, чтобы преобразовать его в массив с помощью Array.prototype.slice.call(arguments), а затем циклом пройти по каждому элементу такими методами массивов как forEach или reduce.
Уверен, forEach можете сами реализовать, поэтому ниже приведен пример с reduce:
// ES5 function sum () { let argsArray = Array.prototype.slice.call(arguments); return argsArray.reduce(function(sum, current) { return sum + current; }, 0) }
В ES6 c rest параметром предоставлена возможность перевести разделённые запятыми аргументы прямо в массив:
// ES6 const sum = (...args) => args.reduce((sum, current) => sum + current, 0); // ES6 без сильного сокращения кода с помощью стрелочных функций function sum (...args) { return args.reduce((sum, current) => sum + current, 0); }
Rest параметр был ранее коротко представлен в разделе про деструктуризацию. Тогда из массива были деструктурированы верхние три значения:
let scores = ['98', '95', '93', '90', '87', '85']; let [first, second, third] = scores; console.log(first); // 98 console.log(second); // 95 console.log(third); // 93
При необходимости получить остальные данные мы бы обратились к rest параметру.
let scores = ['98', '95', '93', '90', '87', '85']; let [first, second, third, ...restOfScores] = scores; console.log(restOfScores); // [90, 97, 95]
Если вас что-то сбило с толку, то запомните, что rest параметр переводить данные в массив и появляется в параметрах функций и при деструктуризации массивов.
Далее рассмотрим spread оператор.
Spread оператор
Spread оператор действует противоположно rest параметру. В свободной трактовке оператор принимает массив и разворачивает его в разделенный запятыми список аргументов.
let array = ['one', 'two', 'three']; // Ниже строки кода равнозначны console.log(...array); // one two three console.log('one', 'two', 'three'); // one two three
Spread оператор часто используется для конкатенации массивов простым для чтения и понимания способом.
Допустим, необходимо объединить следующие массивы:
let array1 = ['one', 'two']; let array2 = ['three', 'four']; let array3 = ['five', 'six'];
В ES5 для конкатенации массивов используется метод Array.concat. Для объединения множества массивов составляется цепь следующим образом:
// ES5 let combinedArray = array1.concat(array2).concat(array3); console.log(combinedArray) // ['one', 'two', 'three', 'four', 'five', 'six'];
В ES6 spread оператор позволяет объединять массивы в новый массив способом, который легче для чтения:
// ES6 let combinedArray = [...array1, ...array2, ...array3]; console.log(combinedArray); // ['one', 'two', 'three', 'four', 'five', 'six']
Spread оператор также может применяться для удаления элемента из массива без видоизменения массива. Этот метод распространен в Redux. Рекомендую вам посмотреть видео от Dan Abramov, чтобы разобраться, как это работает.
Расширенные литералы объектов
Расширенные литералы объектов в ES6 привносят три усовершенствования. К ним относятся:
- сокращения для значений свойств,
- сокращения для методов,
- возможность использовать вычисляемые имена свойств.
Рассмотрим каждое из них.
Сокращение для значений свойств
Замечали ли вы, что иногда записываете в свойство объекта переменную с именем, совпадающим с названием свойства объекта? Это показано на следующем примере:
const fullName = 'Zell Liew'; const Zell = { fullName: fullName }
В таких ситуациях вам бы хотелось писать код короче?
ES6 расширяет объекты сокращениями для значений свойств, что означает возможность писать только переменную, если имя переменной совпадает с именем свойства.
Это выглядит следующим образом:
const fullName = 'Zell Liew'; // ES6 const Zell = { fullName } // То, что выполняет ES6: const Zell = { fullName: fullName }
Сокращения для методов
Методами являются функции, связанные со свойством объекта. Ниже пример метода:
const anObject = { aMethod: function () { console.log("I'm a method!~~")} }
Сокращениями для методов в ES6 является то, что удаление : function из объявления метода не нарушает его работу:
const anObject = { // ES6 aShorthandMethod (arg1, arg2) {}, // ES5 aLonghandMethod: function (arg1, arg2) {}, }
С этим усовершенствованием объекты уже получают сокращение для метода, поэтому не рекомендую использовать стрелочные функции при объявлении объектов, т.к. это нарушит контекст this (вернитесь к разделу по стрелочным функциям, если не помните, почему так происходит).
const dontDoThis = { // Не следует так писать: arrowFunction: () => {} }
Теперь перейдем к последнему усовершенствованию объектов.
Вычисляемые имена свойств объектов
Иногда возникает необходимость в динамическом имени свойства при создании объектов. Раньше вам бы пришлось создать объект, а затем внести свойство следующим способом:
// ES5 const newPropertyName = 'smile'; // Сначала создать объект const anObject = { aProperty: 'a value' } // Затем записать свойство anObject[newPropertyName] = ':D'; // Добавление немного другого свойства в объект anObject['bigger ' + newPropertyName] = 'XD'; // Результат // { // aProperty: 'a value', // 'bigger smile': 'XD' // smile: ':D', // }
В ES6 нет необходимости в таком «обходном пути», т.к. существует возможность назначить динамическое имя свойства напрямую при создании объекта. В тоже время важно обернуть динамическое свойство в квадратные скобки:
const newPropertyName = 'smile'; // ES6 const anObject = { aProperty: 'a value', // Динамические названия свойств [newPropertyName]: ':D', ['bigger ' + newPropertyName]: 'XD', } // Результат // { // aProperty: 'a value', // 'bigger smile': 'XD' // smile: ':D', // }
Это всё, что касалось расширенных литералов объектов. Далее рассмотрим другую полезную особенность: шаблонные строки.
Шаблонные строки
Работа со строками в JavaScript крайне тяжелый процесс. Мы сталкивались с этим при создании функции announcePlayer в разделе про параметры по умолчанию. Ниже в коде созданы пробелы с пустыми строками, которые объединены сложением:
function announcePlayer (firstName, lastName, teamName) { console.log(firstName + ' ' + lastName + ', ' + teamName); }
В ES6 эта проблема решается шаблонными литералами (в спецификации они ранее назывались шаблонными строками).
Для создания шаблонного литерала необходимо обернуть строки в обратные апострофы. Внутри обратных апострофов используется специальный указатель ${}, в котором можно писать JavaScript код.
Ниже пример того, как это выглядит в действии:
const firstName = 'Zell'; const lastName = 'Liew'; const teamName = 'unaffiliated'; const theString = `${firstName} ${lastName}, ${teamName}`; console.log(theString); // Zell Liew, unaffiliated
Таким образом, появилось возможность составлять различные комбинации с помощью шаблонных литералов, что напоминает использование шаблонизатора.
Самой полезной особенностью шаблонных литералов является возможность создания многострочных строк. Ниже представлен пример:
const multi = `One upon a time, In a land far far away, there lived a witich, who could change night into day`;

Такие строки могут быть использованы для создания HTML элементов в JavaScript коде (это не лучший способ создания HTML элементов, но лучше, чем их создание один за одним по отдельности).
const container = document.createElement('div'); const aListOfItems = `<ul> <li>Point number one</li> <li>Point number two</li> <li>Point number three</li> <li>Point number four</li> </ul>`; container.innerHTML = aListOfItems; document.body.append(container);
Другой особенностью шаблонных литералов являются теги. Теги — это функции, которые позволяют манипулировать шаблонным литералом для замены любой строки.
Ниже пример:
const animal = 'lamb'; // Ниже тег const tagFunction = () => { // ваш код } // Функция tagFunction позволяет манипулировать шаблонным литералом const string = tagFunction `Mary had a little ${animal}`;
Если быть честным, то несмотря на то, что шаблонные теги производят впечатление, у меня до сих пор не возникло ситуации для их применения. С целью их более детального изучения рекомендую прочитать материал на MDN.
Вывод
Это были все особенности ES6, которые использую на регулярной основе. Определенно стоит потратить немного своего времени и изучить их, чтобы понимать, что вокруг пишут другие.
