Skip to content

Commit d5e3050

Browse files
authored
Merge pull request #1118 from javascript-tutorial/lex111/nullish-operator
Translate 1.2.12. Nullish coalescing operator
2 parents 74ee137 + acf2833 commit d5e3050

File tree

35 files changed

+166
-0
lines changed

35 files changed

+166
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Оператор объединения с null '??'
2+
3+
[recent browser="new"]
4+
5+
В этой статье мы будем говорить, что значение выражение "определено", если оно отличается от `null` или `undefined`.
6+
7+
Оператор объединения с null представляет собой два вопросительных знака `??`.
8+
9+
Результат выражения `a ?? b` будет следующим:
10+
- `a`, если значение `a` определено,
11+
- `b`, если значение `a` не определено.
12+
13+
То есть оператор `??` возвращает первый аргумент, если он не `null/undefined`, иначе второй.
14+
15+
Оператор объединения с null не является чем-то принципиально новым. Это всего лишь удобный синтаксис, как из двух значений получить одно "определённое".
16+
17+
Вот как можно переписать выражение `result = a ?? b`, используя уже знакомые нам операторы:
18+
19+
```js
20+
result = (a !== null && a !== undefined) ? a : b;
21+
```
22+
23+
Как правило, оператор `??` нужен для того, чтобы задать значение по умолчанию для потенциально неопределённой переменной.
24+
25+
Например, в следующем примере, если переменная `user` не определена, покажем модальное окно с надписью `Аноним`:
26+
27+
```js run
28+
let user;
29+
30+
alert(user ?? "Аноним"); // Аноним
31+
```
32+
33+
Конечно, если бы переменная `user` содержала любое значение, кроме `null/undefined`, то мы бы увидели его:
34+
35+
```js run
36+
let user = "Иван";
37+
38+
alert(user ?? "Аноним"); // Иван
39+
```
40+
41+
Кроме этого, можно записать последовательность из операторов `??`, чтобы получить первое значение из списка, которое не является `null/undefined`.
42+
43+
Допустим, у нас есть данные пользователя в переменных `firstName`, `lastName` или `nickName`. Все они могут быть неопределёнными, если отсутствует соответствующая информация.
44+
45+
Выведем имя пользователя, используя одну из этих переменных, а в случае если все они не определены, то покажем "Аноним".
46+
47+
Для этого воспользуемся оператором `??`:
48+
49+
```js run
50+
let firstName = null;
51+
let lastName = null;
52+
let nickName = "Суперкодер";
53+
54+
// показывает первое определённое значение:
55+
*!*
56+
alert(firstName ?? lastName ?? nickName ?? "Аноним"); // Суперкодер
57+
*/!*
58+
```
59+
60+
## Сравнение с ||
61+
62+
Оператор ИЛИ `||` можно использовать для того же, что и `??`, как это было показано в [предыдущей главе](info:logical-operators#or-finds-the-first-truthy-value).
63+
64+
Например, если в приведённом выше коде заменить `??` на `||`, то будет тот же самый результат:
65+
66+
```js run
67+
let firstName = null;
68+
let lastName = null;
69+
let nickName = "Суперкодер";
70+
71+
// показывает первое истинное значение:
72+
*!*
73+
alert(firstName || lastName || nickName || "Аноним"); // Суперкодер
74+
*/!*
75+
```
76+
77+
Оператор ИЛИ `||` существует с самого появления JavaScript, поэтому ранее для решения похожих задач разработчики использовали именно его.
78+
79+
С другой стороны, сравнительно недавно в язык был добавлен оператор объединения с null `??` как раз потому, что многие были недовольны оператором `||`.
80+
81+
Важное различие между ними заключается в том, что:
82+
- `||` возвращает первое *истинное* значение.
83+
- `??` возвращает первое *определённое* значение.
84+
85+
Проще говоря, оператор `||` не различает `false`, `0`, пустую строку `""` и `null/undefined`. Для него они все одинаковые, т.е. являются ложными значениями. Если первым аргументом для оператора `||` будет любое из перечисленных значений, то в качестве результата мы получим второй аргумент.
86+
87+
Однако на практике часто требуется использовать значение по умолчанию только тогда, когда переменная является `null/undefined`. Ведь именно тогда значение действительно неизвестно/не определено.
88+
89+
Например, рассмотрим следующий пример:
90+
91+
```js run
92+
let height = 0;
93+
94+
alert(height || 100); // 100
95+
alert(height ?? 100); // 0
96+
```
97+
98+
- `height || 100` проверяет, имеет ли переменная `height` ложное значение, что так и есть,
99+
- поэтому результатом является второй аргумент, т.е. `100`.
100+
- `height ?? 100` проверяет, что переменная `height` содержит `null/undefined`, а поскольку это не так,
101+
- то результатом является сама переменная `height`, т.е. `0`.
102+
103+
Если нулевая высота является "нормальным" значением, которое не должно заменяться значением по умолчанию, то оператор `??` делает как раз то, что нужно.
104+
105+
## Приоритет
106+
107+
Оператор `??` имеет довольно низкий приоритет: `5`, согласно [таблице на MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table).
108+
Таким образом, оператор `??` вычисляется до `=` и `?`, но после большинства других операций, таких как `+`, `*`.
109+
110+
Из этого следует, что если нужно выбрать значение при помощи оператора `??` вместе с другими операторами в выражении, следует добавить круглые скобки:
111+
112+
```js run
113+
let height = null;
114+
let width = null;
115+
116+
// важно: используйте круглые скобки
117+
let area = (height ?? 100) * (width ?? 50);
118+
119+
alert(area); // 5000
120+
```
121+
122+
Иначе, если опустить скобки, то оператор `*` выполнится первым, так как у него приоритет выше, чем у `??`, а это приведёт к неправильным результатам.
123+
124+
```js
125+
// без круглых скобок
126+
let area = height ?? 100 * width ?? 50;
127+
128+
// ...то же самое, что предыдущее выражение (вероятно, это не то, что нам нужно):
129+
let area = height ?? (100 * width) ?? 50;
130+
```
131+
132+
### Использование ?? вместе с && или ||
133+
134+
По соображениям безопасности JavaScript запрещает использование оператора `??` вместе с `&&` и `||`, если только приоритет явно не указан в круглых скобках.
135+
136+
Выполнение следующего кода приведёт к синтаксической ошибке:
137+
138+
```js run
139+
let x = 1 && 2 ?? 3; // Синтаксическая ошибка
140+
```
141+
142+
Это довольно спорное ограничение, которое было описано в спецификации языка, чтобы избежать ошибок при замене оператора `||` на `??`.
143+
144+
Используйте круглые скобки, чтобы обойти это ограничение:
145+
146+
```js run
147+
*!*
148+
let x = (1 && 2) ?? 3; // Работает без ошибок
149+
*/!*
150+
151+
alert(x); // 2
152+
```
153+
154+
## Итого
155+
156+
- Оператор объединения с null `??` — это быстрый способ выбрать первое "определённое" значение из списка.
157+
158+
Используется для присвоения переменным значений по умолчанию:
159+
160+
```js
161+
// будет height=100, если переменная height равна null или undefined
162+
height = height ?? 100;
163+
```
164+
165+
- Оператор `??` имеет очень низкий приоритет, лишь немного выше, чем у `?` и `=`, поэтому при использовании его в выражении, скорее всего, потребуются скобки.
166+
- Запрещено использовать вместе с `||` или `&&` без явно указанных круглых скобок.

1-js/02-first-steps/12-while-for/1-loop-last-value/solution.md renamed to 1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md

File renamed without changes.

1-js/02-first-steps/12-while-for/1-loop-last-value/task.md renamed to 1-js/02-first-steps/13-while-for/1-loop-last-value/task.md

File renamed without changes.

1-js/02-first-steps/12-while-for/2-which-value-while/solution.md renamed to 1-js/02-first-steps/13-while-for/2-which-value-while/solution.md

File renamed without changes.

1-js/02-first-steps/12-while-for/2-which-value-while/task.md renamed to 1-js/02-first-steps/13-while-for/2-which-value-while/task.md

File renamed without changes.

1-js/02-first-steps/12-while-for/3-which-value-for/solution.md renamed to 1-js/02-first-steps/13-while-for/3-which-value-for/solution.md

File renamed without changes.

1-js/02-first-steps/12-while-for/3-which-value-for/task.md renamed to 1-js/02-first-steps/13-while-for/3-which-value-for/task.md

File renamed without changes.

1-js/02-first-steps/12-while-for/4-for-even/solution.md renamed to 1-js/02-first-steps/13-while-for/4-for-even/solution.md

File renamed without changes.
File renamed without changes.

1-js/02-first-steps/12-while-for/5-replace-for-while/solution.md renamed to 1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md

File renamed without changes.

0 commit comments

Comments
 (0)