You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/07-optional-chaining/article.md
+30-17Lines changed: 30 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,7 +32,7 @@ In many practical cases we'd prefer to get `undefined` instead of an error here
32
32
let html =document.querySelector('.elem').innerHTML; // error if it's null
33
33
```
34
34
35
-
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
35
+
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML`property of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.
47
+
It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code.
48
48
49
-
E.g. let's try getting `user.address.street.name`.
49
+
Here's how the same would look for `document.querySelector`:
50
50
51
-
We need to check both `user.address` and `user.address.street`:
51
+
```js run
52
+
let html =document.querySelector('.elem') ?document.querySelector('.elem').innerHTML:null;
53
+
```
54
+
55
+
We can see that the element search `document.querySelector('.elem')` is actually called twice here. Not good.
56
+
57
+
For more deeply nested properties, it becomes even uglier, as more repetitions are required.
58
+
59
+
E.g. let's get `user.address.street.name` in a similar fashion.
The code is short and clean, there's no duplication at all.
94
102
103
+
Here's an example with `document.querySelector`:
104
+
105
+
```js run
106
+
let html =document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element
107
+
```
108
+
95
109
Reading the address with `user?.address` works even if `user` object doesn't exist:
96
110
97
111
```js run
@@ -108,9 +122,9 @@ E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/un
108
122
```warn header="Don't overuse the optional chaining"
109
123
We should use `?.` only where it's ok that something doesn't exist.
110
124
111
-
For example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
125
+
For example, if according to our code logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
112
126
113
-
So, if`user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
127
+
Then, if`user` happens to be undefined, we'll see a programming error about it and fix it. Otherwise, if we overuse `?.`, coding errors can be silenced where not appropriate, and become more difficult to debug.
114
128
```
115
129
116
130
````warn header="The variable before `?.` must be declared"
@@ -127,15 +141,15 @@ The variable must be declared (e.g. `let/const/var user` or as a function parame
127
141
128
142
As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.
129
143
130
-
So, if there are any further function calls or side effects, they don't occur.
144
+
So, if there are any further function calls or operations to the right of `?.`, they won't be made.
131
145
132
146
For instance:
133
147
134
148
```js run
135
149
let user = null;
136
150
let x = 0;
137
151
138
-
user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++
152
+
user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++
139
153
140
154
alert(x); // 0, value not incremented
141
155
```
@@ -162,13 +176,13 @@ userAdmin.admin?.(); // I am admin
162
176
*/!*
163
177
164
178
*!*
165
-
userGuest.admin?.(); // nothing (no such method)
179
+
userGuest.admin?.(); // nothing happens (no such method)
166
180
*/!*
167
181
```
168
182
169
-
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the user object exists, so it's safe read from it.
183
+
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the `user` object exists, so it's safe read from it.
170
184
171
-
Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
185
+
Then `?.()` checks the left part: if the `admin` function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
172
186
173
187
The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.
174
188
@@ -179,7 +193,7 @@ let user1 = {
179
193
firstName:"John"
180
194
};
181
195
182
-
let user2 =null;
196
+
let user2 =null;
183
197
184
198
alert( user1?.[key] ); // John
185
199
alert( user2?.[key] ); // undefined
@@ -192,17 +206,16 @@ delete user?.name; // delete user.name if user exists
192
206
```
193
207
194
208
````warn header="We can use `?.` for safe reading and deleting, but not writing"
195
-
The optional chaining `?.` has no use at the left side of an assignment.
209
+
The optional chaining `?.` has no use on the left side of an assignment.
196
210
197
211
For example:
198
212
```js run
199
213
let user =null;
200
214
201
215
user?.name="John"; // Error, doesn't work
202
-
// because it evaluates to undefined = "John"
216
+
// because it evaluates to: undefined = "John"
203
217
```
204
218
205
-
It's just not that smart.
206
219
````
207
220
208
221
## Summary
@@ -217,4 +230,4 @@ As we can see, all of them are straightforward and simple to use. The `?.` check
217
230
218
231
A chain of `?.` allows to safely access nested properties.
219
232
220
-
Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
233
+
Still, we should apply `?.` carefully, only where it's acceptable, according to our code logic, that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
0 commit comments