
Метод 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' +
// '}'
?Поддержка
- Chrome 75
- Firefox 60
- Safari 12 (частично)
Итоги
Здесь мы рассмотрели примеры использования лишь совсем немногих новых возможностей JavaScript. Если вы интересуетесь новшествами JS — загляните в этот репозиторий и в эту таблицу.
Уважаемые читатели! Сталкивались ли вы с ситуациями, в которых новые возможности JS заметно упрощают решение каких-нибудь задач?

Комментарии (21)
mayorovp
26.08.2019 15:23Глядя на примеры с toString можно подумать, что раньше оно весь код в одну строку выводило, а теперь разбивает по строкам.
Хотя на самом деле, раньше оно теряло выравнивание, а теперь не теряет.
Keyten
26.08.2019 17:35trimStart / trimEnd ведут к очевидным проблемам с rtl текстом: ведь как начало, так и конец строки находятся наоборот, и кто-то может ожидать, что trimStart на арабском тексте удалит пробелы справа.
mayorovp
27.08.2019 08:29+1Так ведь оно и правда удаляет пробелы справа… Собственно, потому от названий trimLeft/trimRight и отказались.
aleksandy
27.08.2019 08:24-1Если объект исключения не используется в блоке catch
То это плохой код.
Исключение либо надо как-то обрабатывать, хотя бы в тот же лог записать, либо вообще не ловить.mayorovp
27.08.2019 08:30Если это feature detect — то не нужно. Если исключение было при попытке записать что-то в лог — то его тоже в лог записывать не нужно.
aleksandy
27.08.2019 13:161. А можно пример подобного feature detect? Я что-то не представляю, как по факту наличия какого-то (неизвестного!!!) исключения можно сделать вывод, о доступности/недоступности чего-либо.
2. Как часто Вы заворачиваете console.log() в try-блок?mayorovp
27.08.2019 13:23+1let templateEngine; try { eval("true"); templateEngine = jitTemplateEngine; } catch { templateEngine = interpreterTemplateEngine; }
justboris
27.08.2019 18:53По-хорошему, в catch-блоке надо проверить тип ошибки, и выбросить ее дальше если он неправильный. Сэкономит потом время на отлов багов в этом месте
mayorovp
27.08.2019 18:58Но тут есть два вопроса:
Какой тип ошибки предполагается увидеть? В стандарте не указывается какое исключение кидает eval будучи запрещённой через CSP!
Какое ещё исключение в принципе возможно в этом блоке? От чего предполагается защищаться?
justboris
28.08.2019 04:15Хотя бы проверять, что это был EvalError, а не какой-то еще. Можно получить
ReferenceError: jitTemplateEngine is not defined
, если где-нибудь в имени переменной опечататься.mayorovp
28.08.2019 06:25Так как вы надёжно отличите EvalError от какого-то ещё, если стандарт не указывает какоё именно исключение выбрасывать?
justboris
28.08.2019 08:46+1?\(?)/?
возможно, с eval проверять нечего. Но вот, в варианте внизу с TouchEvent есть что, например. В общем, вариантов, где ошибку можно оставить без проверки, не так уж и много, чтобы опциональный аргумент был сколько-нибудь полезным.
noodles
28.08.2019 00:27+11. А можно пример подобного feature detect? Я что-то не представляю, как по факту наличия какого-то (неизвестного!!!) исключения можно сделать вывод, о доступности/недоступности чего-либо.
function isTouchDevice() { try { document.createEvent('TouchEvent'); return true; } catch { return false; } }
torbasow
27.08.2019 12:39Не очень понятно, зачем бы на практике так часто уплощать массив, чтобы потребовались специальные методы.
pterodaktil
Аж глаза заболели от повсеместного let
messersveet
Чтобы глаза привыкли к let, надо сначала закрыть их, досчитать до десяти, и плавно открывать.
Ну а вообще, 2019 годе, же, ну.
pterodaktil
2019 год же, const
Tenebrius
На два символа больше. Так много печатать лень =)
codemafia
Это полиция хорошего кода! Немедленно отойдите от клавиатуры в окно.
justboris
А вы точно полиция? У вас никнейм отклеился