minor fixes
This commit is contained in:
parent
19bf2d3f5a
commit
3fa4c32e1d
7 changed files with 32 additions and 23 deletions
|
@ -9,7 +9,6 @@ true + false = 1
|
||||||
"$" + 4 + 5 = "$45"
|
"$" + 4 + 5 = "$45"
|
||||||
"4" - 2 = 2
|
"4" - 2 = 2
|
||||||
"4px" - 2 = NaN
|
"4px" - 2 = NaN
|
||||||
7 / 0 = Infinity
|
|
||||||
" -9 " + 5 = " -9 5" // (3)
|
" -9 " + 5 = " -9 5" // (3)
|
||||||
" -9 " - 5 = -14 // (4)
|
" -9 " - 5 = -14 // (4)
|
||||||
null + 1 = 1 // (5)
|
null + 1 = 1 // (5)
|
||||||
|
|
|
@ -16,7 +16,6 @@ true + false
|
||||||
"$" + 4 + 5
|
"$" + 4 + 5
|
||||||
"4" - 2
|
"4" - 2
|
||||||
"4px" - 2
|
"4px" - 2
|
||||||
7 / 0
|
|
||||||
" -9 " + 5
|
" -9 " + 5
|
||||||
" -9 " - 5
|
" -9 " - 5
|
||||||
null + 1
|
null + 1
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Logical operators: ||, && and !
|
# Logical operators
|
||||||
|
|
||||||
There are four logical operators in JavaScript: `||` (OR), `&&` (AND), `!` (NOT), '??' (Nullish Coalescing).
|
There are four logical operators in JavaScript: `||` (OR), `&&` (AND), `!` (NOT), `??` (Nullish Coalescing). Here we cover the first three, the `??` operator is in the next article.
|
||||||
|
|
||||||
Although they are called "logical", they can be applied to values of any type, not only boolean. Their result can also be of any type.
|
Although they are called "logical", they can be applied to values of any type, not only boolean. Their result can also be of any type.
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
[recent browser="new"]
|
[recent browser="new"]
|
||||||
|
|
||||||
Here, in this article, we'll say that an expression is "defined" when it's neither `null` nor `undefined`.
|
|
||||||
|
|
||||||
The nullish coalescing operator is written as two question marks `??`.
|
The nullish coalescing operator is written as two question marks `??`.
|
||||||
|
|
||||||
|
As it treats `null` and `undefined` similarly, we'll use a special term here, in this article. We'll say that an expression is "defined" when it's neither `null` nor `undefined`.
|
||||||
|
|
||||||
The result of `a ?? b` is:
|
The result of `a ?? b` is:
|
||||||
- if `a` is defined, then `a`,
|
- if `a` is defined, then `a`,
|
||||||
- if `a` isn't defined, then `b`.
|
- if `a` isn't defined, then `b`.
|
||||||
|
|
||||||
|
|
||||||
In other words, `??` returns the first argument if it's not `null/undefined`. Otherwise, the second one.
|
In other words, `??` returns the first argument if it's not `null/undefined`. Otherwise, the second one.
|
||||||
|
|
||||||
The nullish coalescing operator isn't anything completely new. It's just a nice syntax to get the first "defined" value of the two.
|
The nullish coalescing operator isn't anything completely new. It's just a nice syntax to get the first "defined" value of the two.
|
||||||
|
@ -21,29 +20,31 @@ We can rewrite `result = a ?? b` using the operators that we already know, like
|
||||||
result = (a !== null && a !== undefined) ? a : b;
|
result = (a !== null && a !== undefined) ? a : b;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Now it should be absolutely clear what `??` does. Let's see where it helps.
|
||||||
|
|
||||||
The common use case for `??` is to provide a default value for a potentially undefined variable.
|
The common use case for `??` is to provide a default value for a potentially undefined variable.
|
||||||
|
|
||||||
For example, here we show `Anonymous` if `user` isn't defined:
|
For example, here we show `user` if defined, otherwise `Anonymous`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let user;
|
let user;
|
||||||
|
|
||||||
alert(user ?? "Anonymous"); // Anonymous
|
alert(user ?? "Anonymous"); // Anonymous (user not defined)
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, if `user` had any value except `null/undefined`, then we would see it instead:
|
Here's the example with `user` assigned to a name:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let user = "John";
|
let user = "John";
|
||||||
|
|
||||||
alert(user ?? "Anonymous"); // John
|
alert(user ?? "Anonymous"); // John (user defined)
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also use a sequence of `??` to select the first value from a list that isn't `null/undefined`.
|
We can also use a sequence of `??` to select the first value from a list that isn't `null/undefined`.
|
||||||
|
|
||||||
Let's say we have a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be undefined, if the user decided not to enter a value.
|
Let's say we have a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be not defined, if the user decided not to enter a value.
|
||||||
|
|
||||||
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them are undefined.
|
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them aren't defined.
|
||||||
|
|
||||||
Let's use the `??` operator for that:
|
Let's use the `??` operator for that:
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
The OR `||` operator exists since the beginning of JavaScript, so developers were using it for such purposes for a long time.
|
Historically, the OR `||` operator was there first. It exists since the beginning of JavaScript, so developers were using it for such purposes for a long time.
|
||||||
|
|
||||||
On the other hand, the nullish coalescing operator `??` was added to JavaScript only recently, and the reason for that was that people weren't quite happy with `||`.
|
On the other hand, the nullish coalescing operator `??` was added to JavaScript only recently, and the reason for that was that people weren't quite happy with `||`.
|
||||||
|
|
||||||
|
@ -96,16 +97,18 @@ alert(height || 100); // 100
|
||||||
alert(height ?? 100); // 0
|
alert(height ?? 100); // 0
|
||||||
```
|
```
|
||||||
|
|
||||||
- The `height || 100` checks `height` for being a falsy value, and it really is.
|
- The `height || 100` checks `height` for being a falsy value, and it's `0`, falsy indeed.
|
||||||
- so the result is the second argument, `100`.
|
- so the result of `||` is the second argument, `100`.
|
||||||
- The `height ?? 100` checks `height` for being `null/undefined`, and it's not,
|
- The `height ?? 100` checks `height` for being `null/undefined`, and it's not,
|
||||||
- so the result is `height` "as is", that is `0`.
|
- so the result is `height` "as is", that is `0`.
|
||||||
|
|
||||||
If the zero height is a valid value, that shouldn't be replaced with the default, then `??` does just the right thing.
|
In practice, the zero height is often a valid value, that shouldn't be replaced with the default. So `??` does just the right thing.
|
||||||
|
|
||||||
## Precedence
|
## Precedence
|
||||||
|
|
||||||
The precedence of the `??` operator is rather low: `5` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table). So `??` is evaluated before `=` and `?`, but after most other operations, such as `+`, `*`.
|
The precedence of the `??` operator is about the same as `||`, just a bit lower. It equals `5` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table), while `||` is `6`.
|
||||||
|
|
||||||
|
That means that, just like `||`, the nullish coalescing operator `??` is evaluated before `=` and `?`, but after most other operations, such as `+`, `*`.
|
||||||
|
|
||||||
So if we'd like to choose a value with `??` in an expression with other operators, consider adding parentheses:
|
So if we'd like to choose a value with `??` in an expression with other operators, consider adding parentheses:
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ The code below triggers a syntax error:
|
||||||
let x = 1 && 2 ?? 3; // Syntax error
|
let x = 1 && 2 ?? 3; // Syntax error
|
||||||
```
|
```
|
||||||
|
|
||||||
The limitation is surely debatable, but it was added to the language specification with the purpose to avoid programming mistakes, when people start to switch to `??` from `||`.
|
The limitation is surely debatable, it was added to the language specification with the purpose to avoid programming mistakes, when people start to switch from `||` to `??`.
|
||||||
|
|
||||||
Use explicit parentheses to work around it:
|
Use explicit parentheses to work around it:
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,8 @@ let clone = Object.assign({}, user);
|
||||||
|
|
||||||
It copies all properties of `user` into the empty object and returns it.
|
It copies all properties of `user` into the empty object and returns it.
|
||||||
|
|
||||||
|
There are also other methods of cloning an object, e.g. using the [spread operator](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial.
|
||||||
|
|
||||||
## Nested cloning
|
## Nested cloning
|
||||||
|
|
||||||
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||||
|
|
|
@ -166,9 +166,9 @@ userGuest.admin?.(); // nothing (no such method)
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Here, in both lines we first use the dot (`user1.admin`) to get `admin` property, because the user object must exist, so it's safe read from it.
|
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.
|
||||||
|
|
||||||
Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `user1`). Otherwise (for `user2`) the evaluation stops without errors.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ But there's a subtle difference between `Array.from(obj)` and `[...obj]`:
|
||||||
So, for the task of turning something into an array, `Array.from` tends to be more universal.
|
So, for the task of turning something into an array, `Array.from` tends to be more universal.
|
||||||
|
|
||||||
|
|
||||||
## Get a new copy of an array/object
|
## Copy an array/object
|
||||||
|
|
||||||
Remember when we talked about `Object.assign()` [in the past](info:object-copy#cloning-and-merging-object-assign)?
|
Remember when we talked about `Object.assign()` [in the past](info:object-copy#cloning-and-merging-object-assign)?
|
||||||
|
|
||||||
|
@ -233,8 +233,11 @@ It is possible to do the same thing with the spread syntax.
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let arr = [1, 2, 3];
|
let arr = [1, 2, 3];
|
||||||
|
|
||||||
|
*!*
|
||||||
let arrCopy = [...arr]; // spread the array into a list of parameters
|
let arrCopy = [...arr]; // spread the array into a list of parameters
|
||||||
// then put the result into a new array
|
// then put the result into a new array
|
||||||
|
*/!*
|
||||||
|
|
||||||
// do the arrays have the same contents?
|
// do the arrays have the same contents?
|
||||||
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
|
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
|
||||||
|
@ -252,8 +255,11 @@ Note that it is possible to do the same thing to make a copy of an object:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let obj = { a: 1, b: 2, c: 3 };
|
let obj = { a: 1, b: 2, c: 3 };
|
||||||
|
|
||||||
|
*!*
|
||||||
let objCopy = { ...obj }; // spread the object into a list of parameters
|
let objCopy = { ...obj }; // spread the object into a list of parameters
|
||||||
// then return the result in a new object
|
// then return the result in a new object
|
||||||
|
*/!*
|
||||||
|
|
||||||
// do the objects have the same contents?
|
// do the objects have the same contents?
|
||||||
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
|
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
|
||||||
|
@ -267,7 +273,7 @@ alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
|
||||||
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
|
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
|
||||||
```
|
```
|
||||||
|
|
||||||
This way of copying an object is much shorter than `let objCopy = Object.assign({}, obj);` or for an array `let arrCopy = Object.assign([], arr);` so we prefer to use it whenever we can.
|
This way of copying an object is much shorter than `let objCopy = Object.assign({}, obj)` or for an array `let arrCopy = Object.assign([], arr)` so we prefer to use it whenever we can.
|
||||||
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue