Short Circuiting (&& and ||)
Let's go back to two logical operators we already used before but didn't use their full potential yet. And I'm talking about the AND (&&)
operator and the OR (||)
operator and how we can use them for something called short-circuiting.
So up until this point, we have used logical operators only to combine boolean values. But the truth is that we can do a lot more with the AND and OR operators. Let's start with the OR operator here.
Let's start by logging the below code
/**
* We haven't done this before, but you can actually use non-boolean
* values as operands
*/
console.log(3 || 'Michael');
You can see that the result is three. Hence, that means that the results of the OR operator don't always have to be a boolean. That's something that I didn't show you before. There are three properties that I didn't tell you before about logical operators.
So, in the above operation, we used two values that were not booleans, and it then returned a value that was not a boolean. In the case of the OR operator, short-circuiting means that if the first value is a truthy value, it will immediately return that first value. That's exactly what we see in the above console with the three, which is a truthy value. So again, if the first operand is truthy in the OR operator, then the other operand will not even be evaluated. JavaScript will not even take a look at it. Hence, that's what we mean by short-circuiting.
Now let's do some more.
console.log('' || 'John');
console.log(true || 0);
console.log(undefined || null);
Before looking at the result, try to guess the results of the above operations.
Let's see the results together.
The result of the first operation is John
because the first operand (''
) is a falsy value. Hence, the second operand will also be evaluated, and that's John
, and it will then be returned. Once more, we see that the result of the OR operator doesn't have to be a boolean. It will simply return the truthy value.
Next up, we have true || 0
. And so the first operand is a truthy value, and in fact, it's the boolean value true, so that will be the result of the operator.
Finally, we have undefined || null
, and as you already know, undefined
is a falsy value, and so we then go to the second operand. There is no short-circuiting and so that's the one that's going to be returned. That's the reason why we have null
, and that happens even though null
is also a falsy value.
Let's now generalize this to more operators so that I can give you the general rule of how this operator works, no matter with how many values.
Consider the below code snippet:
console.log(undefined || 0 || '' || 'hello' || 23 || null);
What do you think that the result of the above code will be?
Well, the result as you can see is the string 'hello'
. It is because, 'hello'
is the first truthy value in the chain of OR operations. We start with undefined
which is falsy, then we go to the next one; zero, which is also falsy, then to the next one; the empty string, which is also falsy, and finally, we get 'hello'
which is truthy. Hence, it will short circuit the entire evaluation, and it will be the returned value.
If we think about this, it actually makes sense because, in the OR operation, the result is true if at least one operand is true. So if the first operand is already true, for example: 3 || 'Michael'
, then JavaScript doesn't even have to look at the other values because the result of the expression will already be true anyway. Hence it will short circuit and then simply return that first result.
Now let's see a more practical application of this. Let's say that there might be a property on the restaurant object with the number of guests.
Something like this:
restaurant.numGuests;
We don't know if that property exists on the restaurant object. However, we want to define a variable based on this number of guests. And we want to set a default value if this doesn't exist.
What we want to do is this.
/**
* If restaurant.numGuests exists then we assign its value else
* we assign the value of 10
*/
const numberOfGuests = restaurant.numGuests ? restaurant.numGuests : 10;
console.log(numGuests)
You see that we get a value of 10. That's because restaurant.numGuests
doesn't exist, .i.e. it is undefined
. Hence 10 will be the result of this ternary operator.
But now, if we set the property like:
restaurant.numGuests = 23
const numberOfGuests = restaurant.numGuests ? restaurant.numGuests : 10;
console.log(numberOfGuests)
Then numberOfGuests
will be 23.
Instead of using the above solution, we can take advantage of short-circuiting and the OR operator.
const numberOfGuests2 = restaurant.numGuests || 10;
console.log(numberOfGuests2); // Result ==> 10
Let's analyze why this works...😏
Considering the fact that restaurant.numGuests
is undefined, meaning it does not exist on the restaurant object, this means that it's a falsy value. Hence, as we already know, the second value which is 10 will be the result of the OR operator.
But now, if we if we set the value of 23 as we did previously to restaurant.numGuests
, then restaurant.numGuests
will now be a truthy value, and therefore the OR operator short circuits, and it will become the return value, or let's say the result of the operation.
restaurant.numGuests = 23
const numberOfGuests2 = restaurant.numGuests || 10;
console.log(numberOfGuests2); // Result ==> 23
This is a way easier method of setting default values than dealing with the ternary operator or, even worse, an if-else statement. Before moving on to the AND operator, there's one more thing that I need to tell you here: the above solutions will not work when the number of guests is zero.
Let's see what happens.
restaurant.numGuests = 0;
const numberOfGuests = restaurant.numGuests ? restaurant.numGuests : 10;
console.log('SOLUTION 1 RESULT = ', numberOfGuests);
const numberOfGuests2 = restaurant.numGuests || 10;
console.log('SOLUTION 2 RESULT = ', numberOfGuests2);
They're both 10, and that's not the result that we want. But given what we already know, it makes sense because restaurant.numGuests
is now zero, so it's a falsy value, and therefore the second one which is 10 will be the result of the operation and therefore assigned to numberOfGuests2
.
However, zero is the real number of guests. And so that's the value that we would like numberOfGuests2
to have. But instead, we get the default value of 10. Hence, that's obviously not what we want, and we will explore a great solution to this problem in the next lecture.
Now, let's talk about the AND operator. When it comes to short circuit evaluation, the AND operator works in the opposite way of the OR operator.
Let's start with the below code snippet.
console.log(0 && 'Michael');
We get zero as the result. And so what this means is that the AND operator short circuits when the first value is falsy. And then immediately returns that falsy value without even evaluating the second operand. Again, that's exactly the opposite of what happens with the OR operator, which short circuits when the first operand is true.
Let's try something different.
console.log(8 && 'Michael');
In this second example, the string 'Michael'
is returned. So when the first operand is a truthy value, it means that the evaluation continues, and then simply the last value is returned. Once again, this makes sense if we think about it. The AND operator is only true if all the operands are true. Hence, if the first operand is false, then it means that the entire result of the AND operation will already be false anyway. And so, there is no need to even look at any of the other operands.
I think that's a good way to memorize how both (AND and OR operators) of them work. Let's have another example of the AND operator but this time with multiple operands.
console.log('Sr' && 23 && null && 'Michael');
The result is null
. To understand why let's apply the same logic as before. The operand 'Sr'
is a truthy value, and therefore the evaluation continues. 23
is also truthy, but then null
is a falsy value. Therefore, evaluation no longer needs to continue because, at this point, the whole result of the end operation is going to be false anyway. As a result, null
is the value that's going to be returned, and it's short circuits the rest of the evaluation. It will not even take a look at the operand 'Michael'
.
Let's see another practical example:
//Check if the method exists on restaurant object
if (restaurant.orderPizza) {
// If it does then execute
restaurant.orderPizza('mushrooms', 'spinach');
}
We can use the AND operator to avoid an if statement like the one above, where all we want to do is to check if a certain property or value exists. In the above code snippet, we're pretending that we don't know if orderPizza
exists. Hence, we first check if it exists, and only then do we execute it.
With the knowledge we gained about the AND operator, we can do this in a simpler way.
restaurant.orderPizza && restaurant.orderPizza('mushrooms', 'spinach');
What the above line is saying is that, if restaurant.orderPizza
does not exist, that is, if it is undefined, it will then short circuit the evaluation, and nothing else will happen. That's the same thing the if block was doing. If it does exist, .i.e. if it's a truthy value, then the second part here will be evaluated.
It's perfectly fine to have as second operand a function call. We can put anything there. It doesn't just have to be a single value.
Note that I'm not saying that you should go ahead and replace all your if statements with the AND or the OR operators, so please definitely don't do that because it's will make your code very hard to read in the future.
To finsish, let's summarise: