minor fixes

This commit is contained in:
Ilya Kantor 2021-02-02 11:43:35 +03:00
parent 19bf2d3f5a
commit 3fa4c32e1d
7 changed files with 32 additions and 23 deletions

View file

@ -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)

View file

@ -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

View file

@ -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.

View file

@ -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:

View file

@ -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?

View file

@ -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.

View file

@ -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