Falsy, truthy & nullish
Falsy & truthy
Falsy and truthy values are values that are coerced into false and true respectively, when encountered in a boolean context.
The falsy values are:
false
0
- (also 0.0, -0, 0n, etc.)""
- empty string (really empty, no spaces etc.)null
undefined
NaN
- "Not a Number"
All other values are truthy!
This means that any non-empty string always coerces into true
in a boolean context.
Thus in JavaScript also "0"
coerces into true
!
Typical boolean contexts are logical operations and conditions in conditional statements:
console.log( 1 || 0 ); // logs: 1 // Because 1 coerces into true (short-circuiting).
console.log( 'John' && 'Jane' ); // logs: "Jane" // Because "John" coerces into true (short-circuiting).
let condition = 1;
if (condition) { console.log("This gets returned.") } // condition was coerced into true
condition = "Hello";
if (condition) { console.log("This gets returned.") } // condition was coerced into true
condition = '';
if (condition) { console.log("This gets NOT returned.") } // condition was coerced into false
condition = null;
if (!condition) { console.log("This gets returned.") } // condition was coerced into false, but 'not false' is true.
In other contexts than a boolean context, for instance in a numerical context,
values coerce in a different way, as we have seen in the previous chapter.
In a numerical context some truthy values are coerced into NaN
(console.log("foo" * 2); // logs: NaN
)
and falsy value NaN
is not coerced into 0
, but falsy value false
is.
Comparison operations do act within a boolean context (they return a boolean) but the comparison is based on a numerical evaluation as we have seen in the previous chapter.
Nullish
So, statement if(someVar)
skips execution for any falsy value of someVar
. If you want to check
for specific falsy (or truthy) values, you can use the strict equality operators.
let someVar = null;
if (someVar) { console.log("This does NOT get returned."); } // null is falsy, thus the condition is evaluated as false.
if (someVar !== false) { console.log("This DOES get returned."); } // null and false are of different data types. Condition is true.
The non-strict equality comparison operators should be avoided (see previous chapter), except maybe
in those situations where you want to check if something is (n)either undefined
(n)or null
,
that is, to check whether something is nullish or not.
Value null
is only mutually equivalent with undefined
(and vice versa) in a non-strict equality check.
We have seen this being used before in the equivalent short-circuit function for the
nullish coalescing operator ??
.
if( foo == null ) { // Only executed if foo is either null or undefined }
if( foo != null ) { // Only executed if foo neither null nor undefined }
// or:
if( foo == undefined ) { // Only executed if foo is either null or undefined }
if( foo != undefined ) { // Only executed if foo neither null nor undefined }
In a previous chapter, we explained
the difference between null
and undefined
.
The next example uses the non-strict equality comparison operator with null
and undefined
.
let myName
function greetingIfDefined(name) {
// if (name != null) { console.log("Hello "+name+", how ya doin'?"); } // or:
if (name != undefined) { console.log("Hello "+name+", how ya doin'?"); }
else { console.log("No greetings for you, you're "+name+"."); }
}
greetingIfDefined(myName); // logs: "No greetings for you, you're undefined."
myName = null;
greetingIfDefined(myName); // logs: "No greetings for you, you're null."
// Anything else than 'null' or 'undefined' is NOT mutually equivalent in a non-strict equality check; condition is true:
greetingIfDefined(false); // logs: "Hello false, how ya doin'?"
greetingIfDefined(0); // logs: "Hello 0, how ya doin'?"
greetingIfDefined(NaN); // logs: "Hello NaN, how ya doin'?"
greetingIfDefined(""); // logs: "Hello , how ya doin'?"
greetingIfDefined("guys"); // logs: "Hello guys, how ya doin'?"
greetingIfDefined(-Math.PI); // logs: "Hello -3.141592653589793, how ya doin'?"
Of course we can also use two strict equality operators and connect them by a logical AND. This involves a little more code, but may make the programmer's intention clearer and the code more comprehensible.
function greetingIfDefined(name) {
if (name !== null && name !== undefined) { console.log("Hello "+name+", how ya doin'?"); }
else { console.log("No greetings for you, you're a "+name+"."); }
}
You may be inclined considering the nullish coalescing operator as an option to perform this "nullish check", but this would make things only unnecessary cumbersome.
function greetingIfNotNullish(name) {
let defined = true;
name ?? (defined = false);
if (defined) { console.log("Hello "+name+", how ya doin'?"); }
else { console.log("No greetings for you, you're a "+name+"."); }
}