[Перевод] Примеры использования некоторых новых возможностей JavaScript
Автор материала, перевод которого мы сегодня публикуем, говорит, что новые возможности JavaScript, которые попали в стандарт ES2019, уже официально доступны в браузерах Chrome, Firefox и Safari, а также на платформе Node.js. Если нужно поддерживать устаревшие браузеры, то воспользоваться новшествами можно, транспилируя JS-код с помощью Babel. Здесь мы рассмотрим примеры использования некоторых новых возможностей JS.
Метод Object.fromEntries
В ES2017 появился метод Object.entries
. Он преобразует объект в массив. Например, это может выглядеть так:
let students = {
amelia: 20,
beatrice: 22,
cece: 20,
deirdre: 19,
eloise: 21
}
Object.entries(students)
// [
// [ 'amelia', 20 ],
// [ 'beatrice', 22 ],
// [ 'cece', 20 ],
// [ 'deirdre', 19 ],
// [ 'eloise', 21 ]
// ]
Этот метод стал замечательным дополнением к возможностям языка. Дело в том, что он позволял удобно обрабатывать данные объектов с помощью многочисленных методов, встроенных в прототип Array
. Среди этих методов, например, можно отметить map
, filter
, reduce
. Но для того, чтобы преобразовать массив обратно в объект, к сожалению, удобных средств не существовало. Всё приходилось делать вручную, с помощью цикла:
let students = {
amelia: 20,
beatrice: 22,
cece: 20,
deirdre: 19,
eloise: 21
}
// преобразуем объект в массив для того чтобы воспользоваться методом .filter()
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]
// преобразуем многомерный массив обратно в объект
let drinkingAgeStudents = {}
for (let [name, age] of overTwentyOne) {
drinkingAgeStudents[name] = age;
}
// { beatrice: 22, eloise: 21 }
Метод Object.fromEntries
создан для того чтобы избавиться от подобных циклов. Он позволяет решить ту же самую задачу с помощью гораздо меньшего объёма кода. Это вполне может способствовать тому, чтобы разработчики чаще пользовались бы методами массивов для обработки преобразованных в массивы объектов.
let students = {
amelia: 20,
beatrice: 22,
cece: 20,
deirdre: 19,
eloise: 21
}
// преобразуем объект в массив для того чтобы воспользоваться методом .filter()
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]
// преобразуем многомерный массив обратно в объект
let drinkingAgeStudents = Object.fromEntries(overTwentyOne);
// { beatrice: 22, eloise: 21 }
Важно отметить, что массивы и объекты недаром являются различными структурами данных. В некоторых случаях преобразование одной в другую ведёт к потере данных. В следующем примере можно видеть, как при преобразовании массива в объект теряются те элементы массива, которые оказываются дублирующимися ключами объекта.
let students = [
[ 'amelia', 22 ],
[ 'beatrice', 22 ],
[ 'eloise', 21],
[ 'beatrice', 20 ]
]
let studentObj = Object.fromEntries(students);
// { amelia: 22, beatrice: 20, eloise: 21 }
// пропала первая запись beatrice!
▍Поддержка
- Chrome 75
- Firefox 67
- Safari 12.1
Метод Array.prototype.flat
Многомерные массивы — это структуры данных, с которыми программисты встречаются довольно-таки часто. Особенно — при загрузке неких данных. При этом уменьшение размерности массива всегда было важной задачей. Решить эту задачу можно было всегда, но код её решения нельзя было назвать очень уж привлекательным.
Рассмотрим следующий пример. Здесь, в результате обработки массива объектов с помощью функции map
, у нас оказывается многомерный массив. Его мы хотим сделать более «плоским».
let courses = [
{
subject: "math",
numberOfStudents: 3,
waitlistStudents: 2,
students: ['Janet', 'Martha', 'Bob', ['Phil', 'Candace']]
},
{
subject: "english",
numberOfStudents: 2,
students: ['Wilson', 'Taylor']
},
{
subject: "history",
numberOfStudents: 4,
students: ['Edith', 'Jacob', 'Peter', 'Betty']
}
]
let courseStudents = courses.map(course => course.students)
// [
// [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
// [ 'Wilson', 'Taylor' ],
// [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
// ]
// тут мы могли бы попытаться воспользоваться чем-то вроде [].concat.apply([], courseStudents)
Теперь в нашем распоряжении имеется метод Array.prototype.flat
, который принимает необязательный аргумент, указывающий то, на какой уровень надо «поднять» элементы массива.
let courseStudents = [
[ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
[ 'Wilson', 'Taylor' ],
[ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]
let flattenOneLevel = courseStudents.flat(1)
console.log(flattenOneLevel)
// [
// 'Janet',
// 'Martha',
// 'Bob',
// [ 'Phil', 'Candace' ],
// 'Wilson',
// 'Taylor',
// 'Edith',
// 'Jacob',
// 'Peter',
// 'Betty'
// ]
let flattenTwoLevels = courseStudents.flat(2)
console.log(flattenTwoLevels)
// [
// 'Janet', 'Martha',
// 'Bob', 'Phil',
// 'Candace', 'Wilson',
// 'Taylor', 'Edith',
// 'Jacob', 'Peter',
// 'Betty'
// ]
Обратите внимание на то, что если этому методу не передавать аргументов, то он будет поднимать элементы массива на один уровень. Это очень важно, так как в нашем случае нужно преобразовать массив в полностью плоскую структуру данных. Вот что получается при использовании этого метода без параметров:
let courseStudents = [
[ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
[ 'Wilson', 'Taylor' ],
[ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]
let defaultFlattened = courseStudents.flat()
console.log(defaultFlattened)
// [
// 'Janet',
// 'Martha',
// 'Bob',
// [ 'Phil', 'Candace' ],
// 'Wilson',
// 'Taylor',
// 'Edith',
// 'Jacob',
// 'Peter',
// 'Betty'
// ]
Оправдание подобного устройства этого метода можно найти в том, что он, по умолчанию, не стремится превратить любой массив в одномерный, требуя конкретных инструкций по преобразованию массива. Если в одномерный массив нужно преобразовать массив, точные параметры которого неизвестны, методу flat
можно передать значение Infinity
.
let courseStudents = [
[ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
[ 'Wilson', 'Taylor' ],
[ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]
let alwaysFlattened = courseStudents.flat(Infinity)
console.log(alwaysFlattened)
// [
// 'Janet', 'Martha',
// 'Bob', 'Phil',
// 'Candace', 'Wilson',
// 'Taylor', 'Edith',
// 'Jacob', 'Peter',
// 'Betty'
// ]
Как обычно, подобными операциями следует пользоваться с осторожностью. Такой подход, вероятно, нельзя назвать удачным выбором для тех случаев, когда глубина обрабатываемого массива действительно неизвестна.
▍Поддержка
- Chrome 75
- Firefox 67
- Safari 12
Метод Array.prototype.flatMap
Вместе с методом flat
в нашем распоряжении теперь оказался и новый комбинированный метод — Array.prototype.flatMap
. Выше мы, на самом деле, уже видели пример ситуации, в которой этот метод может пригодиться, но давайте рассмотрим ещё один пример.
Предположим, перед нами стоит задача вставки неких элементов в массив. Как мы решили бы её раньше, до появления новых возможностей JS? Например — так:
let grades = [78, 62, 80, 64]
let curved = grades.map(grade => [grade, grade + 7])
// [ [ 78, 85 ], [ 62, 69 ], [ 80, 87 ], [ 64, 71 ] ]
let flatMapped = [].concat.apply([], curved)
// теперь массив оказался плоским. Тут можно было бы использовать метод flat, но раньше этого метода в JS не существовало
// [
// 78, 85, 62, 69,
// 80, 87, 64, 71
// ]
Теперь, когда у нас есть метод Array.prototype.flat
, этот код можно улучшить:
let grades = [78, 62, 80, 64]
let flatMapped = grades.map(grade => [grade, grade + 7]).flat()
// [
// 78, 85, 62, 69,
// 80, 87, 64, 71
// ]
Та задача, которую мы тут решаем, основана на сравнительно популярном паттерне (особенно это касается функционального программирования). Поэтому то, как красиво мы можем её решить с помощью метода flatMap
, не может не радовать:
let grades = [78, 62, 80, 64]
let flatMapped = grades.flatMap(grade => [grade, grade + 7]);
// [
// 78, 85, 62, 69,
// 80, 87, 64, 71
// ]
Вспомните о том, что по умолчанию метод Array.prototype.flat
работает так, будто ему передана единица. Метод flatMap
ведёт себя точно так же, то есть — «поднимает» элементы массива лишь на 1 уровень. Он представляет собой результат комбинации методов map
и flat
.
let grades = [78, 62, 80, 64]
let flatMapped = grades.flatMap(grade => [grade, [grade + 7]]);
// [
// 78, [ 85 ],
// 62, [ 69 ],
// 80, [ 87 ],
// 64, [ 71 ]
// ]
▍Поддержка
- Chrome 75
- Firefox 67
- Safari 12
Методы String.prototype.trimStart и String.prototype.trimEnd
Ещё одно приятное новшество ES2019 — это псевдонимы, которые дают некоторым строковым методам более понятные имена. Раньше в нашем распоряжении были методы String.prototype.trimRight
и String.prototype.trimLeft
:
let message = " Welcome to CS 101 "
message.trimRight()
// ' Welcome to CS 101'
message.trimLeft()
// 'Welcome to CS 101 '
message.trimRight().trimLeft()
// 'Welcome to CS 101'
Методы это замечательные, но хорошо то, что им дали имена, которые больше соответствуют их цели. А цель их заключается в удалении начальных и конечных пробельных символов из строк.
let message = " Welcome to CS 101 "
message.trimEnd()
// ' Welcome to CS 101'
message.trimStart()
// 'Welcome to CS 101 '
message.trimEnd().trimStart()
// 'Welcome to CS 101'
▍Поддержка
- Chrome 75
- Firefox 67
- Safari 12
Необязательный аргумент блока catch
Ещё одна приятная возможность ES2019 — это то, что аргумент в блоках try-catch
теперь стал необязательным. Ранее всем блокам catch
надо было передавать, в качестве параметра, объект исключения. Аргумент приходилось передавать catch
даже в том случае, если он не использовался.
try {
let parsed = JSON.parse(obj)
} catch(e) {
// e можно игнорировать или использовать
console.log("error")
}
Теперь это не так. Если объект исключения не используется в блоке catch
— тогда в этот блок не нужно и ничего передавать.
try {
let parsed = JSON.parse(obj)
} catch {
console.log("error")
}
Это — отличная возможность, которая пригодится в тех случаях, когда программист заранее знает о том, возникновение какой нештатной ситуации приведёт к попаданию в соответствующий блок catch
.
▍Поддержка
- Chrome 75
- Firefox 67
- Safari 12
Изменения в методе Function.prototype.toString
Стандарт ES2019 принёс изменения в то, как работает метод функций toString
. Ранее он немного искажал оформление выводимого кода:
function greeting() {
const name = 'CSS Tricks'
console.log(`hello from ${name}`)
}
greeting.toString()
//'function greeting() {\nconst name = \'CSS Tricks\'\nconsole.log(`hello from ${name} //`)\n}
Теперь этот метод отражает реальное представление исходного кода функций.
function greeting() {
const name = 'CSS Tricks'
console.log(`hello from ${name}`)
}
greeting.toString()
// 'function greeting() {\n' +
// " const name = 'CSS Tricks'\n" +
// ' console.log(`hello from ${name}`)\n' +
// '}'
▍Поддержка
Итоги
Здесь мы рассмотрели примеры использования лишь совсем немногих новых возможностей JavaScript. Если вы интересуетесь новшествами JS — загляните в этот репозиторий и в эту таблицу.
Уважаемые читатели! Сталкивались ли вы с ситуациями, в которых новые возможности JS заметно упрощают решение каких-нибудь задач?