Хабрахабр

[Перевод] Примеры использования некоторых новых возможностей 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 заметно упрощают решение каких-нибудь задач?

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть