Условное ветвление: if, ‘?’
Иногда нам нужно выполнить различные действия в зависимости от условий.
Для этого мы можем использовать инструкцию if и условный оператор ? , который также называют оператором «вопросительный знак».
Инструкция «if»
Инструкция if(. ) вычисляет условие в скобках и, если результат true , то выполняет блок кода.
let year = prompt('В каком году была опубликована спецификация ECMAScript-2015?', ''); if (year == 2015) alert( 'Вы правы!' );
В примере выше, условие – это простая проверка на равенство ( year == 2015 ), но оно может быть и гораздо более сложным.
Если мы хотим выполнить более одной инструкции, то нужно заключить блок кода в фигурные скобки:
if (year == 2015)
Мы рекомендуем использовать фигурные скобки <> всегда, когда вы используете инструкцию if , даже если выполняется только одна команда. Это улучшает читаемость кода.
Преобразование к логическому типу
Инструкция if (…) вычисляет выражение в скобках и преобразует результат к логическому типу.
Давайте вспомним правила преобразования типов из главы Преобразование типов:
- Число 0 , пустая строка «» , null , undefined и NaN становятся false . Из-за этого их называют «ложными» («falsy») значениями.
- Остальные значения становятся true , поэтому их называют «правдивыми» («truthy»).
Таким образом, код при таком условии никогда не выполнится:
if (0) < // 0 is falsy . >
…а при таком – выполнится всегда:
if (1) < // 1 is truthy . >
Мы также можем передать заранее вычисленное в переменной логическое значение в if , например так:
let condition = (year == 2015); // преобразуется к true или false if (condition)
Блок «else»
Инструкция if может содержать необязательный блок «else» («иначе»). Он выполняется, когда условие ложно.
let year = prompt('В каком году была опубликована спецификация ECMAScript-2015?', ''); if (year == 2015) < alert( 'Да вы знаток!' ); >else < alert( 'А вот и неправильно!' ); // любое значение, кроме 2015 >
Несколько условий: «else if»
Иногда нужно проверить несколько вариантов условия. Для этого используется блок else if .
let year = prompt('В каком году была опубликована спецификация ECMAScript-2015?', ''); if (year < 2015) < alert( 'Это слишком рано. ' ); >else if (year > 2015) < alert( 'Это поздновато' ); >else
В приведённом выше коде JavaScript сначала проверит year < 2015 . Если это неверно, он переходит к следующему условию year >2015 . Если оно тоже ложно, тогда сработает последний alert .
Блоков else if может быть и больше. Присутствие блока else не является обязательным.
Условный оператор „?“
Иногда нам нужно определить переменную в зависимости от условия.
let accessAllowed; let age = prompt('Сколько вам лет?', ''); if (age > 18) < accessAllowed = true; >else < accessAllowed = false; >alert(accessAllowed);
Так называемый «условный» оператор «вопросительный знак» позволяет нам сделать это более коротким и простым способом.
Оператор представлен знаком вопроса ? . Его также называют «тернарный», так как этот оператор, единственный в своём роде, имеет три аргумента.
let result = условие ? значение1 : значение2;
Сначала вычисляется условие : если оно истинно, тогда возвращается значение1 , в противном случае – значение2 .
let accessAllowed = (age > 18) ? true : false;
Технически, мы можем опустить круглые скобки вокруг age > 18 . Оператор вопросительного знака имеет низкий приоритет, поэтому он выполняется после сравнения > .
Этот пример будет делать то же самое, что и предыдущий:
// оператор сравнения "age > 18" выполняется первым в любом случае // (нет необходимости заключать его в скобки) let accessAllowed = age > 18 ? true : false;
Но скобки делают код более простым для восприятия, поэтому мы рекомендуем их использовать.
На заметку:
В примере выше вы можете избежать использования оператора вопросительного знака ? , т.к. сравнение само по себе уже возвращает true/false :
// то же самое let accessAllowed = age > 18;
Несколько операторов „?“
Последовательность операторов вопросительного знака ? позволяет вернуть значение, которое зависит от более чем одного условия.
let age = prompt('Возраст?', 18); let message = (age < 3) ? 'Здравствуй, малыш!' : (age < 18) ? 'Привет!' : (age < 100) ? 'Здравствуйте!' : 'Какой необычный возраст!'; alert( message );
Поначалу может быть сложно понять, что происходит. Но при ближайшем рассмотрении мы видим, что это обычная последовательная проверка:
- Первый знак вопроса проверяет age < 3 .
- Если верно – возвращает 'Здравствуй, малыш!' . В противном случае, проверяет выражение после двоеточия „:“, вычисляет age < 18 .
- Если это верно – возвращает 'Привет!' . В противном случае, проверяет выражение после следующего двоеточия „:“, вычисляет age < 100 .
- Если это верно – возвращает 'Здравствуйте!' . В противном случае, возвращает выражение после последнего двоеточия – 'Какой необычный возраст!' .
Вот как это выглядит при использовании if..else :
if (age < 3) < message = 'Здравствуй, малыш!'; >else if (age < 18) < message = 'Привет!'; >else if (age < 100) < message = 'Здравствуйте!'; >else
Нетрадиционное использование „?“
Иногда оператор «вопросительный знак» ? используется в качестве замены if :
let company = prompt('Какая компания создала JavaScript?', ''); (company == 'Netscape') ? alert('Верно!') : alert('Неправильно.');
В зависимости от условия company == 'Netscape' , будет выполнена либо первая, либо вторая часть после ? .
Здесь мы не присваиваем результат переменной. Вместо этого мы выполняем различный код в зависимости от условия.
Не рекомендуется использовать оператор вопросительного знака таким образом.
Несмотря на то, что такая запись короче, чем эквивалентная инструкция if , она хуже читается.
Вот, для сравнения, тот же код, использующий if :
let company = prompt('Какая компания создала JavaScript?', ''); if (company == 'Netscape') < alert('Верно!'); >else
При чтении глаза сканируют код по вертикали. Блоки кода, занимающие несколько строк, воспринимаются гораздо легче, чем длинный горизонтальный набор инструкций.
Смысл оператора «вопросительный знак» ? – вернуть то или иное значение, в зависимости от условия. Пожалуйста, используйте его именно для этого. Когда вам нужно выполнить разные ветви кода – используйте if .
Задачи
if (строка с нулём)
важность: 5
Выведется ли alert ?
if ("0")
Да, выведется.
Любая строка, кроме пустой (а строка "0" – не пустая), в логическом контексте становится true .
Можно запустить и проверить:
if ("0")
Название JavaScript
важность: 2
Используя конструкцию if..else , напишите код, который будет спрашивать: „Какое «официальное» название JavaScript?“
Если пользователь вводит «ECMAScript», то показать: «Верно!», в противном случае – отобразить: «Не знаете? ECMAScript!»
Оператор опциональной последовательности
Оператор опциональной последовательности ?. позволяет получить значение свойства, находящегося на любом уровне вложенности в цепочке связанных между собой объектов, без необходимости проверять каждое из промежуточных свойств в ней на существование. ?. работает подобно оператору . , за исключением того, что не выбрасывает исключение, если объект, к свойству или методу которого идёт обращение, равен null или undefined . В этих случаях он возвращает undefined .
Таким образом, мы получаем более короткий и понятный код при обращении к вложенным по цепочке свойствам объекта, когда есть вероятность, что какое-то из них отсутствует.
Интерактивный пример
Синтаксис
?.prop; obj?.[expr]; arr?.[index]; func?.(args);
Описание
Оператор опциональной последовательности предоставляет способ упростить доступ к значениям в цепочке объектов, когда возможно, что какое-то свойство (или метод) в ней равно undefined или null .
Для примера, создадим объект obj , имеющий вложенную структуру. Без оператора опциональной последовательности поиск глубоко расположенных подсвойств требует проверки всех промежуточных свойств на существование, например:
let nestedProp = obj.first && obj.first.second;
Если обращаться к obj.first.second без проверки obj.first , то, если свойство obj.first равно null или undefined , выбросится исключение TypeError .
Однако, с оператором опциональной последовательности ( ?. ) не требуется явно проверять ссылку на obj.first перед обращением к obj.first.second :
let nestedProp = obj.first?.second;
Если используется оператор ?. вместо . , JavaScript знает о необходимости проверки obj.first перед обращением к obj.first.second . Если значение obj.first равно null или undefined , выполнение выражения автоматически прекращается и возвращается undefined .
Это эквивалентно следующему (кроме создания временной переменной):
let temp = obj.first; let nestedProp = temp === null || temp === undefined ? undefined : temp.second;
Опциональная последовательность с вызовом функции
Вы можете использовать ?. , когда необходимо вызвать метод, которого может не существовать. Это может быть полезно, например, при использовании API, в котором метод может быть недоступен из-за устаревания или не поддерживаемости устройством пользователя.
Использование ?. с вызовом функции значит, что выполнение автоматически вернёт undefined , а не выбросит исключение, если метод не найден:
let result = someInterface.customMethod?.();
Примечание: Для существующего свойства, не являющегося функцией, использование конструкции x.y?.() всё равно выбросит TypeError исключение ( x.y не является функцией ).
Работа с колбэк-функциями и обработчиками событий
Если вы используете колбэк-функции или извлекаете методы объекта деструктурирующим присваиванием, Вы можете получить несуществующие значения, которые нельзя вызывать как функции до проверки на их существование. Используя оператор ?. , вы можете избежать лишних проверок:
// С использованием ES2019 function doSomething(onContent, onError) try // . делаем что-то с данными > catch (err) if (onError) // проверяем, существует ли onError onError(err.message); > > >
// С использованием оператора опциональной последовательности function doSomething(onContent, onError) try // . делаем что-то с данными > catch (err) onError?.(err.message); // не выбросит исключение, если onError равен undefined > >
Опциональные последовательности в выражениях
Вы также можете использовать оператор опциональной последовательности, когда обращаетесь к свойству с помощью скобочной нотации:
let nestedProp = obj?.["prop" + "Name"];
Примеры
Базовый пример
В этом примере производится обращение к свойству name элемента с ключом bar объекта Map . Элемент с таким ключом отсутствует, но исключение выброшено не будет; nameBar равен undefined .
let myMap = new Map(); myMap.set("foo", name: "baz", desc: "inga" >); let nameBar = myMap.get("bar")?.name;
Сокращённое выполнение
При использовании оператора опциональной последовательности в выражениях, где левая часть операнда равна null или undefined , выражение не будет выполнено. Например:
let potentiallyNullObj = null; let x = 0; let prop = potentiallyNullObj?.[x++]; console.log(x); // 0, т.к. x не был инкрементирован
Совместное использование операторов опциональной последовательности
Во вложенных объектах возможно использование оператора опциональной последовательности неограниченное количество раз:
let customer = name: "Carl", details: age: 82, location: "Paradise Falls", // точный адрес неизвестен >, >; let customerCity = customer.details?.address?.city; // … это также работает с вызовами функций let duration = vacations.trip?.getTime?.();
Использование с оператором ??
Оператор ?? может использоваться после опциональной последовательности для установления значения по умолчанию:
let customer = name: "Carl", details: age: 82 >, >; const customerCity = customer?.city ?? "Unknown city"; console.log(customerCity); // Unknown city
Спецификации
| Specification |
|---|
| ECMAScript Language Specification # prod-OptionalExpression |
Совместимость с браузерами
BCD tables only load in the browser
Смотрите также
- Github - Proposal-pipeline-operator
- TC39 proposals
- Nullish Coalescing Operator
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 7 авг. 2023 г. by MDN contributors.
Your blueprint for a better internet.
MDN
Support
- Product help
- Report an issue
Our communities
Developers
- Web Technologies
- Learn Web Development
- MDN Plus
- Hacks Blog
- Website Privacy Notice
- Cookies
- Legal
- Community Participation Guidelines
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.
Что означает знак вопроса в строке comment?.author
Для чего ставится знак вопроса перед точкой при обращении к свойству объекта?
- Количеству голосов ▼
- Дата создания
13 июля 2022
Этот оператор называется "оператор опциональной последовательности". Он позволяет обращаться в нутренним свойствам объекта, без риска что родительское свойство отсутствует в объекте. Чтобы лучше это понять, разберем пример.
Предположим у нас есть сущности пользователей с их адресами в виде объектов по типу:
const user = name: 'Ivan', age: 29, address: country: 'Russia', city: 'Perm', street: 'Leonova', >, >;
Предположим мы хотим сформировать строку содержащую страну и город пользователя Russia, Perm . Для этого мы просто обращаемся к внутренним ключам объекта в ключе address :
const result = `$user.address.country>, $user.address.city>`; console.log(result); // => Russia, Perm
Но что если у нас будет пользователь с отсутствующим адресом? Такой код выдаст ошибку, потому что мы попытались обратиться к свойствам country и city у значения undefined :
; const result = `$, $`; // TypeError: Cannot read properties of undefined (reading 'country') console.log(result);
У объекта user нет свойства address , поэтому мы не можем к обратиться к нему. Чтобы избежать такой ошибки, используется оператор опциональной последовательности: добавляется знак вопроса после свойства, которого может не быть в объекте. Такой код уже не выдаст ошибку:
; const result = `$, $`; console.log(result); // => undefined, undefined
Не смотря на то, что это удобный инструмент, бездумно его использовать не стоит. Обычно его использование говорит о том, что в приложении есть проблемы. Например, в примере выше, должна стоять проверка на существование адреса пользователя. Если адреса нет, то нет смысла составлять строку. Если добавить такую проверку, то необходимость в использовании этого оператора отпадает.
Операторы . ?? и |>: будущие возможности JavaScript, которые вам понравятся
Джастин Фуллер, автор материала, перевод которого мы сегодня публикуем, предлагает рассмотреть три новых возможности, появление которых ожидается в JavaScript в обозримом будущем. Сначала он расскажет о процессе развития JS, а после этого представит обзор этих возможностей и покажет примеры их использования. Речь пойдёт об операторах ?. , ?? и |> .
О стандарте ECMAScript и развитии JavaScript
Если вы уже знакомы с особенностями деятельности рабочей группы ECMA TC39, с тем, как она осуществляет отбор и обработку предложений о совершенствовании JavaScript, вы вполне можете пропустить этот раздел. Если же вы из тех, кому интересно об этом узнать — вот краткий обзор того, чем занимается TC39.
JavaScript — это реализация стандарта, называемого ECMAScript, который был создан для стандартизации реализаций языка, которые появились в ранние годы существования веб-браузеров.
Существует восемь редакций стандарта ECMAScript и семь релизов (четвёртая редакция стандарта не выходила, после третьей сразу идёт пятая). Разработчики JavaScript-движков приступают к реализации новшеств языка после выхода стандарта. Здесь можно увидеть, что не каждый движок реализует все возможности, при этом некоторым движкам для введения новшеств требуется больше времени, чем другим. Хотя такое положение дел и не идеально, это, всё же, лучше, чем полное отсутствие стандартов.
Предложения
Новые возможности языка изначально представляют собой так называемые предложения, которые, до включения в стандарт, проходят процедуру согласования. Если предложение признано полезным и обратно совместимым со всем тем, что уже существует, оно будет включено в следующую редакцию стандарта.
Процесс рассмотрения предложений состоит из пяти шагов, описанных в этом документе. В самом начале предложение находится в состоянии черновика (strawman), это то же самое, что и Stage 0. На этом шаге предложение либо ещё не представлено техническому комитету, либо оно ещё не отвергнуто, но пока ещё не соответствует критериям, позволяющим перейти к следующему этапу согласования. Те возможности, о которых мы будем говорить ниже, уже прошли Stage 0.
Мне хотелось бы порекомендовать читателям избегать использования в продакшне новшеств JS, предложения, описывающие которые, находятся на этапе Stage 0. Лучше дождаться перехода их к более стабильным этапам согласования. Цель этой рекомендации заключается в том, чтобы помочь вам избежать проблем в том случае, если предложение будет отвергнуто или окажется очень сильно изменённым.
Система тестирования
Материалы, в которых рассказывают о новых возможностях языков программирования, часто содержат фрагменты кода, вырванные из контекста. Иногда эти возможности используются для создания неких учебных приложений. Однако, ни того, ни другого мы делать здесь не будем. Так как я — большой поклонник TDD, я полагаю, что лучший способ изучения некоей новой технологии заключается в её тестировании.
Мы будем использовать здесь, для освоения описываемых возможностей JS, то, что Джим Ньюкирк называет обучающими тестами. Такие тесты построены не на утверждениях о коде, написанном на некоем языке. Они построены на анализе утверждений, касающихся самого языка. Тот же подход может оказаться полезным и при изучении API сторонних разработчиков, и при освоении любой возможности языка.
Транспиляторы
Если вы не знакомы с транспиляторами, то у вас, возможно, возникнет вопрос о том, как мы собираемся использовать возможности языка, которые пока не реализованы. Тут надо отметить, что JavaScript постоянно развивается, а для реализации в распространённых движках его новых возможностей, интересных программистам, нужно время. В результате в экосистеме JS существует такое понятие, как транспиляторы.
Они позволяют преобразовывать код, написанный на JS с использованием новейших возможностей, которые, например, ещё не включены в стандарты и не реализованы популярными движками, в JS-код, который понимают существующие среды выполнения JavaScript-программ. Это позволят, например, использовать в коде даже предложения уровня Stage 0, а то, что получится после обработки кода транспилятором, можно будет выполнить, например, в современных браузерах или в среде Node.js. Делается это путём преобразования нового кода таким образом, что он, для среды исполнения, выглядит как код, написанный на одной из поддерживаемых ей версий JS.
Одним из самых популярных JavaScript-транспиляторов является Babel, совсем скоро мы поговорим о том, как им пользоваться.
Подготовка рабочей среды
Если вы хотите самостоятельно повторить всё то, о чём мы будем говорить — вы можете это сделать, настроив npm-проект и установив необходимые зависимости. Предполагается, что сейчас у вас уже установлены Node.js и NPM.
Для того чтобы подготовиться к нашим экспериментам, выполните следующую команду, находясь в отведённой для этих экспериментов директории:
npm init -f && npm i ava@1.0.0-beta.3 @babel/preset-env@7.0.0-beta.42 @babel/preset-stage-0@7.0.0-beta.42 @babel/register@7.0.0-beta.42 @babel/polyfill@7.0.0-beta.42 @babel/plugin-transform-runtime@7.0.0-beta.42 @babel/runtime@7.0.0-beta.42 --save-dev
Затем добавьте следующее в файл package.json :
"scripts": < "test": "ava" >, "ava": < "require": [ "@babel/register", "@babel/polyfill" ] >
Далее, создайте файл .babelrc со следующим содержимым:
< "presets": [ ["@babel/preset-env", < "targets": < "node": "current" >>], "@babel/preset-stage-0" ], "plugins": [ "@babel/plugin-transform-runtime" ] >
Теперь вы готовы к тому, чтобы писать тесты, исследующие новые возможности JS.
1. Оператор ?.
В ходе написания приложений на JavaScript мы постоянно работаем с объектами. Однако иногда эти объекты не имеют той структуры, которой мы от них ожидаем. Вот, например, объект с данными. Подобный объект можно получить, скажем, в результате запроса к базе данных или при обращении к некоему API.
const data = < user: < address: < street: 'Pennsylvania Avenue', >, >, >;
Этот объект описывает пользователя, прошедшего регистрацию. А вот — похожий объект, но в данном случае пользователь, которого он описывает, регистрацию не завершил.
const data = < user: <>, >;
Если попытаться получить доступ к свойству street объекта address , вложенного в этот «неполный» объект, рассчитывая на то, что он будет выглядеть так же, как объект, содержащий все необходимые данные, можно столкнуться с такой ошибкой:
console.log(data.user.address.street); // Uncaught TypeError: Cannot read property 'street' of undefined
Для того чтобы этого избежать, в текущих условиях необходимо использовать, для обращения к свойству street , такую конструкцию:
const street = data && data.user && data.user.address && data.user.address.street; console.log(street); // undefined
Я полагаю, что всё это, во-первых, плохо выглядит, во-вторых, это тяжело писать, а в-третьих — подобные конструкции оказываются слишком длинными.
Именно в таких ситуациях как нельзя кстати оказываются опциональные последовательности или опциональные цепочки (optional chaining), представленные оператором, выглядящим как знак вопроса с точкой ( ?. ).
console.log(data.user?.address?.street); // undefined
Выглядит это лучше, да и строить такие конструкции проще. Уверен, вы с этим утверждением согласитесь. Убедившись в полезности этой новой возможности, исследуем её. Напишем тест, поместив код в файл optional-chaining.test.js . Мы, в этом разделе, будем постепенно дополнять этот файл новыми тестами.
import test from 'ava'; const valid = < user: < address: < street: 'main street', >, >, >; function getAddress(data) < return data?.user?.address?.street; >test('Optional Chaining returns real values', (t) => < const result = getAddress(valid); t.is(result, 'main street'); >);
Этот тест позволяет нам убедиться в том, что оператор ?. , в том случае, если объект выглядит так, как ожидается, работает точно так же, как способ доступа к свойствам объекта через точку. Теперь проверим поведение этого оператора в ситуации, когда объект не является тем, чем мы его считаем.
test('Optional chaining returns undefined for nullish properties.', (t) => < t.is(getAddress(), undefined); t.is(getAddress(null), undefined); t.is(getAddress(<>), undefined); >);
А вот как опциональные последовательности работают при применении их для доступа к элементам массивов:
const valid = < user: < address: < street: 'main street', neighbors: [ 'john doe', 'jane doe', ], >, >, >; function getNeighbor(data, number) < return data?.user?.address?.neighbors?.[number]; >test('Optional chaining works for array properties', (t) => < t.is(getNeighbor(valid, 0), 'john doe'); >); test('Optional chaining returns undefined for invalid array properties', (t) => < t.is(getNeighbor(<>, 0), undefined); >);
Иногда случает так, что нам неизвестно, реализована ли в объекте какая-то функция. Например, такая ситуация распространена при работе с браузерами. Устаревшие браузеры могут не содержать реализаций неких функций. Благодаря оператору ?. мы можем узнать, реализована ли в объекте интересующая нас функция или нет. Вот как это сделать:
const data = < user: < address: < street: 'main street', neighbors: [ 'john doe', 'jane doe', ], >, getNeighbors() < return data.user.address.neighbors; >>, >; function getNeighbors(data) < return data?.user?.getNeighbors?.(); >test('Optional chaining also works with functions', (t) => < const neighbors = getNeighbors(data); t.is(neighbors.length, 2); t.is(neighbors[0], 'john doe'); >); test('Optional chaining returns undefined if a function does not exist', (t) => < const neighbors = getNeighbors(<>); t.is(neighbors, undefined); >);
Выражения не будут выполнены, если в цепочке что-то не так. Если говорить о внутренней реализацией этого механизма, то наше выражение преобразуется примерно в следующее:
value == null ? value[some expression here]: undefined;
В результате после оператора ?. не будет выполнено ничего в том случае, если значение представлено в виде undefined или null . Взглянуть на это правило в действии можно с помощью следующего теста:
let neighborCount = 0; function getNextNeighbor(neighbors) < return neighbors?.[++neighborCount]; >test('It short circuits expressions', (t) => < const neighbors = getNeighbors(data); t.is(getNextNeighbor(neighbors), 'jane doe'); t.is(getNextNeighbor(undefined), undefined); t.is(neighborCount, 1); >);
Как видите, опциональные последовательности позволяют уменьшить потребность в конструкциях if , в сторонних библиотеках вроде lodash , и в использовании неуклюжих конструкций, в которых применяется && .
▍Оператор. и производительность
Вероятно, вы заметили, что использование опциональных последовательностей означает дополнительную нагрузку на систему. Всё дело в том, что каждый раз, когда используется оператор ?. , система вынуждена проводить дополнительные проверки. Злоупотребление оператором ?. может заметно повлиять на производительность программ.
Я посоветовал бы вам использовать эту возможность вместе с некоей системой проверки, которая позволяет анализировать объекты при их получении откуда-либо или при их создании. Это сократит необходимость в использовании конструкции ?. и ограничит её воздействие на производительность.
2. Оператор ??
Вот несколько распространённых операций, с которыми можно встретиться в процессе работы с JavaScript. Обычно они выглядят как единое выражение, смысл которого заключается в следующем:
- Проверка значения на undefined и null .
- Указание значения, используемого по умолчанию.
- Обеспечение того, что появление значений 0 , false , и '' не приводит к использованию значения, применяемого по умолчанию.
value != null ? value : 'default value';
Можно встретить и неграмотно написанный вариант такого выражения:
value || 'default value'
Проблема второго примера заключается в том, что третий пункт вышеприведённого списка здесь не выполняется. Появление тут числа 0 , значения false или пустой строки будет распознано как значение false , а это — не то, что нам нужно. Именно поэтому проверку на null и undefined нужно проводить в явном виде:
value != null
Это выражение аналогично такому:
value !== null && value !== undefined
В подобных ситуациях очень кстати окажется новый оператор, называемый «объединение со значением null » (nullish coalescence), который выглядит как два знака вопроса ( ?? ). В подобной ситуации теперь можно будет воспользоваться следующей конструкцией:
value ?? 'default value';
Это защищает нас от случайного использования значения по умолчанию при появлении в выражениях значений, распознаваемых как false , но при этом позволяет выявлять значения null и undefined , не прибегая к тернарному оператору и к проверке вида != null .
Теперь, познакомившись с этим оператором, мы можем написать тесты для того, чтобы проверить его в деле. Эти тесты поместим в файл nullish-coalescing.test.js .
import test from 'ava'; test('Nullish coalescing defaults null', (t) => < t.is(null ?? 'default', 'default'); >); test('Nullish coalescing defaults undefined', (t) => < t.is(undefined ?? 'default', 'default'); >); test('Nullish coalescing defaults void 0', (t) => < t.is(void 0 ?? 'default', 'default'); >); test('Nullish coalescing does not default 0', (t) => < t.is(0 ?? 'default', 0); >); test('Nullish coalescing does not default empty strings', (t) => < t.is('' ?? 'default', ''); >); test('Nullish coalescing does not default false', (t) => < t.is(false ?? 'default', false); >);
Из этих тестов можно понять, что значения по умолчанию используются для null , undefined и void 0 (оно преобразуется в undefined ). При этом значения по умолчанию не применяются в тех случаях, когда в выражении появляются значения, воспринимаемые как ложные, вроде 0 , пустой строки и false .
3. Оператор |>
В функциональном программировании есть такое понятие, как композиция. Это — действие, представляющее собой объединение в цепочку нескольких вызовов функций. Каждая функция принимает, в качестве входных данных, выходные данные предыдущей функции. Вот пример композиции, подготовленный средствами обычного JavaScript:
function doubleSay (str) < return str + ", " + str; >function capitalize (str) < return str[0].toUpperCase() + str.substring(1); >function exclaim (str) < return str + '!'; >let result = exclaim(capitalize(doubleSay("hello"))); result //=> "Hello, hello!"
Подобное распространено настолько широко, что средства композиции функций существуют во множестве библиотек, поддерживающих функциональное программирование, например, в таких, как lodash и ramda.
Благодаря новому конвейерному оператору (pipeline operator), который выглядит как комбинация вертикальной черты и знака «больше» ( |>) , можно отказаться от использования сторонних библиотек и переписать вышеприведённый пример следующим образом:
let result = "hello" |> doubleSay |> capitalize |> exclaim; result //=> "Hello, hello!"
Цель появления этого оператора заключается в том, чтобы улучшить читаемость цепочек вызовов функций. Кроме того, в будущем, этот оператор найдёт применение в конструкциях частичного применения функций. Сейчас это можно сделать следующим образом:
let result = 1 |> (_ => Math.max(0, _)); result //=> 1 let result = -5 |> (_ => Math.max(0, _)); result //=> 0
Разобравшись с основами, напишем тесты, разместив их в файле pipeline-operator.test.js :
import test from 'ava'; function doubleSay (str) < return str + ", " + str; >function capitalize (str) < return str[0].toUpperCase() + str.substring(1); >function exclaim (str) < return str + '!'; >test('Simple pipeline usage', (t) => < let result = "hello" |>doubleSay |> capitalize |> exclaim; t.is(result, 'Hello, hello!'); >); test('Partial application pipeline', (t) => < let result = -5 |>(_ => Math.max(0, _)); t.is(result, 0); >); test('Async pipeline', async (t) => < const asyncAdd = (number) =>Promise.resolve(number + 5); const subtractOne = (num1) => num1 - 1; const result = 10 |> asyncAdd |> (async (num) => subtractOne(await num)); t.is(await result, 14); >);
Анализируя эти тесты, можно заметить, что при применении в конвейере асинхронной функции, объявленной с использованием ключевого слова async , нужно дождаться появления значения, воспользовавшись ключевым словом await . Дело тут в том, что значение становится объектом Promise . Имеются несколько предложений изменений, направленных на поддержку конструкций вида |> await asyncFunction , но они ещё не реализованы и решение об их дальнейшей судьбе пока не принято.
Итоги
Надеемся, вам понравились ожидаемые возможности JS, которым посвящён этот материал. Вот репозиторий с тестами, которыми мы занимались в этом материале. А вот, для удобства — ссылки на рассмотренные здесь новшества: оператор ?., оператор ??, оператор |>.
Уважаемые читатели! Как вы относитесь к появлению новых возможностей JS, о которых мы рассказали?
