JS Tip of the Day: Undefined vs. Null vs. Nullish

Undefined vs. Null vs. Nullish
Level: Intermediate

Both undefined and null are used to represent variables or properties as having no value. undefined is for properties which don’t exist or variables that have been declared but have not yet been defined, whereas null is more for existing variables or properties that have been defined but point to no value (particularly object values). If a value is either undefined or null it is considered to be a “nullish” value.

Some syntax or APIs specifically check for these values but it may not always be clear when one or the other is being used or expected.

Loose Equality (==)

When comparing either undefined or null with loose equality, only nullish values will evaluate to true. Any other comparison, even with other falsy values, will evaluate to false.

// checks for: nullish (for null and undefined)
null == null; // true
null == undefined; // true
undefined == null; // true
undefined == undefined; // true

null == false; // false
null == 0; // false
null == ''; // false
undefined == false; // false
undefined == 0; // false
undefined == ''; // false

Nullish Coalescing (??)

As its name suggests, the nullish coalescing operator works with nullish values. If the left-hand side operator is nullish, the expression evaluates to what is on the right. This differs from logical or (||) which checks for all falsy values.

// checks for: nullish
null ?? true; // true
undefined ?? true; // true

false ?? true; // false
0 ?? true; // 0
'' ?? true; // ''

Optional Chaining (?.)

Like with nullish coalescing, optional chaining also relies on checking for nullish values. If nullish, the result of the expression will be undefined (even if the original value was null).

// checks for: nullish
null?.hasOwnProperty('foo'); // undefined
undefined?.hasOwnProperty('foo'); // undefined

false?.hasOwnProperty('foo'); // false
0?.hasOwnProperty('foo'); // false
''?.hasOwnProperty('foo'); // false
({ foo: 'foo' })?.hasOwnProperty('foo'); // true

Default this Binding (Sloppy Mode)

In sloppy (non-strict) mode, when default this binding is applied to a function, the value of this in the function is the global object. This will happen when the function is called with any nullish context. Note: in strict mode, both null and undefined can be used to represent the this value.

// checks for: nullish (non-strict)
function isGlobal () {
    console.log(this === globalThis);
}

isGlobal.call(); // true
isGlobal.call(null); // true
isGlobal.call(undefined); // true

isGlobal.call({}); // false
isGlobal.call(true); // false

Default Parameters

Default parameters will be set only if the argument for that parameter was not provided (implicitly undefined) or if explicitly set to the value undefined.

// checks for: undefined
function defaults (param = true) {
    console.log(param);
}

defaults(); // true
defaults(undefined); // true

defaults(null); // null
defaults(false); // false
defaults(0); // 0

Prototypes

Prototypes are objects which other objects inherit from. When at the end of a prototype chain, the prototype is null. You cannot have an undefined prototype.

// checks for: null
Object.create(null); // (Ok)

Object.create(undefined); // TypeError

Number Conversion

null values can be successfully converted to a number, resulting in a 0 value. Converting undefined results in NaN. Note: parseInt()/parseFloat() will return NaN for both undefined and null.

// checks for: null
console.log(Number(null)); // 0
console.log(+null); // 0

console.log(Number(undefined)); // NaN
console.log(+undefined); // NaN

More info:


More tips: JavaScript Tips of the Day