6 функций JavaScript, которые вы должны знать
Опубликовано: 2020-09-24Чуть больше года назад мы говорили о 5 «новых» функциях JavaScript, которые вы должны знать, чтобы комфортно разрабатывать в Gutenberg. Там мы узнали, что такое деструктуризация объектов и массивов, как создавать стрелочные функции, что такое операторы расширения и остатка и так далее. К счастью, в JavaScript по-прежнему есть множество функций, которые, как только вы с ними познакомитесь, помогут вам писать более понятный и лаконичный код.
Сегодня я покажу вам шесть действительно крутых функций JavaScript. Операторы, область видимости переменных, промисы, асинхронные функции… готовы ли вы изучить их все?
#1 Необязательная цепочка в JavaScript с оператором ?.
Необязательный оператор цепочки позволяет упростить доступ к атрибутам объекта, когда есть вероятность, что один из них undefined или равен null . Например, предположим, что у вас есть объект, подобный следующему:
const toni = { name: 'Antonio', details: { age: 35, }, }; Чтобы получить доступ к age toni , сначала нужно получить доступ к атрибуту details , а затем к age :
function getAge( person ) { return person.details.age; } Проблема, с которой мы столкнулись в предыдущей функции, и я уверен, что в этом нет ничего нового для вас, заключается в том, что свойства, которые мы ожидаем от person , не всегда присутствуют. То есть, если либо person , либо person.details не undefined , приведенная выше функция вызовет ошибку типа:
getAge( toni ); // 35 getAge( {} ); // Uncaught TypeError: person.details is undefinedОбычно мы решаем эту проблему, добавляя несколько защитных мер:
function getAge( person ) { if ( ! person ) { return; } if ( ! person.details ) { return; } return person.details.age; } getAge( toni ); // 35 getAge( {} ); // undefined которые явно исправить это. К сожалению, получившаяся функция более сложна и содержит много шумного кода, отвлекающего наше внимание от действительно важных вещей. Чтобы вернуться на правильный путь и исправить решение более простым способом, все, что нам нужно сделать, это использовать необязательный оператор цепочки ?. :
function getAge( person ) { return person?.details?.age; } getAge( toni ); // 35 getAge( {} ); // undefined По сути, этот оператор позволяет нам получить доступ к каждому свойству, если оно не является undefined или null . Как только он обнаруживает, что мы пытаемся получить доступ к свойству несуществующего объекта, он возвращает undefined .
# 2 Нулевой оператор объединения ( ?? )
В JavaScript очень легко установить значение по умолчанию для переменной с помощью этого оператора: || , правильно? Неправильно! Мы все делали это, но это может привести к неожиданным побочным эффектам, если вы не будете осторожны…
Например, предположим, что у нас есть селектор в нашем хранилище Redux, который позволяет нам получить значение, установленное пользователем. Если пользователь еще не указал значение (то есть значение не undefined ), мы возвращаем значение по умолчанию, используя || :
function getValue( state ) { return state.value || 5; }Теперь давайте посмотрим на несколько примеров того, что мы получим с предыдущей функцией после того, как пользователь установит то или иное значение:
// No setValue( x )... getValue(); // 5 setValue( 2 ); getValue(); // 2 setValue( 1 ); getValue(); // 1 setValue( 0 ); getValue(); // 5 Упс! Все работало нормально, пока мы не дошли до последнего дела! По-видимому, если пользователь установил значение 0 , результатом функции станет 5 . Почему? Ну, объяснение довольно очевидно: x||y возвращает y тогда и только тогда, когда x является «ложным» значением. Обычно это работает нормально, так как значение undefined является ложным. Но так же есть false или 0 , например.
На самом деле мы хотели установить значение по default только тогда, когда его нет. Итак, мы можем добавить защиту, чтобы проверить, есть ли значение или нет:
function getValue( state ) { if ( undefined === state.value ) return 5; return state.value; }и мы даже можем написать это, используя тернарный оператор, если нам хочется:
function getValue( state ) { return undefined === state.value ? 5 : state.value; } но, опять же, оба решения излишне усложняют код. Чтобы присвоить переменной значение по умолчанию, мы должны использовать нулевой оператор объединения ?? , который возвращает правую часть тогда и только тогда, когда левая часть равна null или undefined :
function getValue( state ) { return state.value ?? 5; }и, сюрприз-сюрприз!, результат именно тот, что мы хотели:
// No setValue( x )... getValue(); // 5 setValue( 2 ); getValue(); // 2 setValue( 1 ); getValue(); // 1 setValue( 0 ); getValue(); // 0Да, и, кстати, вы можете использовать этот оператор в сочетании с присваиванием. Например, это:
value = value ?? 5;эквивалентно этому:
value ??= 5;#3 Промисы и асинхронные функции
Промисы — это механизм, реализованный в JavaScript, который позволяет нам упростить исходный код при работе с асинхронными операциями, то есть операциями, результаты которых доступны не сразу. Например, если мы хотим получить данные с нашего сервера, понятно, что ответ не будет мгновенным, так как мы должны ждать, пока сервер получит запрос, обработает его и отправит нам ответ обратно.
В старых версиях и библиотеках JavaScript (например, jQuery) мы реализовывали это с помощью обратных вызовов. Идея была довольно проста: вместе с самим запросом определить функцию (обратный вызов), которая должна быть вызвана после получения ответа. Таким образом, когда асинхронная операция (то есть получение данных с сервера) завершится, библиотека вызовет функцию, и наша логика возобновится:
jQuery.ajax( { url: 'https://server.com/wp-json/wp/users/1', success: ( user ) => { console.log( user ); }, } );Если мы запускаем только один изолированный запрос, это решение довольно элегантно и удобно. Но, как только нам нужно выполнить больше запросов, все быстро становится грязным. Например, если мы хотим запросить двух разных пользователей, нам придется вложить обратные вызовы:

jQuery.ajax( { url: 'https://server.com/wp-json/wp/v2/users/1', success: ( user1 ) => { jQuery.ajax( { url: 'https://server.com/wp-json/wp/v2/users/2', success: ( user2 ) => { console.log( user1, user2 ); }, } ); }, } );Промисы — это решение этой проблемы: если получение определенного результата не является мгновенным (например, когда мы получаем что-то с сервера), мы можем сразу вернуть промис JavaScript. Этот объект является оболочкой фактического значения и означает «обещание», что указанное значение будет доступно в какой-то момент в будущем.
Например, если бы мы переписали наш первый фрагмент, используя промисы, это выглядело бы так:
const promise = wp.apiFetch( { url: 'https://server.com/wp-json/wp/v2/users/1', } ); promise.then( ( user ) => console.log( user ) ); Как видите, wp.apiFetch должен получить пользователя 1 с сервера, но сразу выдает результат. Результатом, однако, является не сам пользователь, а обещание, которое будет разрешено пользователю после выполнения запросов. Поэтому все, что нам нужно сделать, это написать обратный вызов, который будет обрабатывать ответ промиса, когда он будет разрешен.
Вы сейчас можете подумать, что это не так сложно по сравнению с тем, что у нас было раньше, верно? В конце концов, мы все еще используем обратные вызовы… но вы можете увидеть, насколько это полезно, как только мы начнем объединять несколько запросов:
const promise1 = wp.apiFetch( { url: 'https://server.com/wp-json/wp/v2/users/1', } ); const promise2 = wp.apiFetch( { url: 'https://server.com/wp-json/wp/v2/users/2', } ); Promise.all( [ promise1, promise2 ] ).then( ( [ user1, user2 ] ) => console.log( user1, user2 ); ); Используя промисы, мы смогли запустить два параллельных запроса для получения пользователей 1 и 2 и использовать Promise.all для ожидания разрешения обоих промисов. Нет спагетти-кода с вложенными обратными вызовами.
Что ж, самое замечательное в обещаниях еще впереди. Мы можем использовать немного синтаксического сахара для работы с промисами JavaScript и писать асинхронный код, который выглядит синхронным. Все, что вам нужно сделать, это определить асинхронную функцию с помощью ключевого слова async , и вдруг все станет намного проще:
async function logTwoUsers( id1, id2 ) { const user1 = await wp.apiFetch( { url: '…' + id1 } ); const user2 = await wp.apiFetch( { url: '…' + id2 } ); console.log( user1, user2 ); } Всякий раз, когда вы вызываете асинхронную операцию внутри асинхронной функции, вы можете дождаться ее результата, используя ключевое слово await . Единственное, что вы должны иметь в виду, это то, что когда вы определяете async функцию, ее результатом всегда будет обещание:
async function getNumberFive() { return 5; } const p = getNumberFive(); // a promise p.then( console.log ); // prints "5" # 4 Область видимости переменной при использовании let и const
Как вы, наверное, уже знаете, теперь вы можете объявлять переменные, используя ключевые слова let и const . Первый определяет переменную, а второй определяет константу:
let x = 1; console.log( x ); // 1 x = 2; console.log( x ); // 2 const y = 1; console.log( y ); // 1 y = 2; // Uncaught TypeError: invalid assignment to const 'y' console.log( y ); // 1 У вас может возникнуть соблазн подумать, что let и var — это одно и то же, поскольку оба ключевых слова позволяют нам объявить непостоянную переменную. Но между ними есть существенная разница: их область применения. С let и const областью действия переменной является блок, в котором она определена. В var это целая функция.
function fn() { if ( true ) { var x = 1; let y = 2; const z = 3; }//end if console.log( x ); // 1 console.log( y ); // Uncaught ReferenceError: y is not defined console.log( z ); // Uncaught ReferenceError: z is not defined } # 5 Преобразование данных при использовании JSON.parse
Функция JSON.parse анализирует строку JSON и создает объект JavaScript. Например:
const x = JSON.parse( '{"x":1,"a":[1,2,3]}' ); // Object { x: 1, a: [ 1, 2, 3 ] } Чего большинство людей не знает об этом, так это того, что он поддерживает второй аргумент, называемый reviver . Этот параметр представляет собой функцию, которая будет выполняться для каждого анализируемого элемента, позволяя вам манипулировать значением по своему усмотрению. Например, представьте себе строку JSON, подобную следующей:
const json = '{"name":"David","birthday":"1985-12-01T10:00:00.000Z"}'; Если мы будем использовать JSON.parse как есть, он сгенерирует объект с двумя строковыми атрибутами: name и birthday . Но если мы предоставим функцию JSON.parse reviver мы сможем убедиться, что birthday анализируется как Date :
const user = JSON.parse( json, ( key, value ) => 'birthday' === key ? new Date( value ) : value ) # 6 Цифровой разделитель в исходном коде с использованием символа подчеркивания ( _ )
Наш последний совет сегодня — числовой разделитель. Существует предложение (в настоящее время на стадии 4), позволяющее записывать числа так, чтобы их было легче понять людям. Например, можете ли вы действительно сказать, насколько велики следующие числа?
10000000000 2189719.25 Если бы мы только могли использовать разделитель тысяч, это было бы намного проще интерпретировать! И это именно то, что мы можем сделать, используя подчеркивание _ :
10_000_000_000 2_189_719.25Резюме
Можно написать лучший код JavaScript, если вы привыкнете ко всем новым инструментам и функциям, включенным в JavaScript. Сегодня мы увидели несколько примеров того, что возможно с этим языком программирования. Надеюсь, вы узнали что-то новое, и если оно вам понравилось, не забудьте поделиться им с коллегами. До скорого!
Избранное изображение Сэма Дэна Труонга на Unsplash.
