Checking for the Absence of a Value in JavaScript

When I first started learning JavaScript I was confused by the seemingly endless ways developers check for the absence of a value:

console.log(value == null)
console.log(value === null)
console.log(value == undefined)
console.log(value === undefined)
console.log(value === undefined || value === null)
console.log(typeof value === 'undefined')
console.log(typeof value == 'undefined')
console.log(typeof value === 'undefined' || value === null)
console.log(typeof value === 'undefined' || value == null)
console.log(typeof value == 'undefined' || value == null)
console.log(typeof value == 'undefined' || value === null)
console.log(!value)

Which one is right?

The Absence of a Value

In order to understand which of these expressions is correct we must first take a look at the two ways JavaScript represents the lack of a value.

Undefined

undefined is one of JavaScript’s primitive types which means checking its type using the typeof operator returns the string 'undefined':

console.log(typeof undefined) // undefined

It is the default value of any declared, but unassigned variable:

var x
console.log(x) // undefined

It is the value returned when trying to access an undeclared object property:

var obj = {}
console.log(obj.a) // undefined

It is the default return value of a function which does not return:

function f() {}
console.log(f()) // undefined

It is returned by the void operator, an operator which evaluates an expression and then returns undefined:

console.log(void 0) // undefined
console.log(void 'hello') // undefined
console.log(void(3 + 2)) // undefined
console.log(void(/* any expression */)) // undefined

And lastly, it is not a literal. It is a property of the global object, an object that always exists in the global scope (accessible through the window property on browsers).

Null

null is also a JavaScript primitive type, but checking its type using the typeof operator does not return what you’d expect:

console.log(typeof null) // object

According to W3Schools you can consider this behavior a bug in JavaScript. typeof null should return 'null', but since a lot of code has already been written with the assumption that typeof null erroneously returns 'object', it will not be changed to avoid breaking old code.

Unlike undefined, null does not show up as a default value anywhere. Instead it is usually returned by functions which are expected to return an object when one could not be retrieved from the given parameters.

For example, in browsers document.getElementById returns null if no element with the given ID was found in the HTML document:

console.log(document.getElementById('some-id-which-no-element-has')) // null

In contrast to undefined, null is a literal. It is not the identifier of some property. It represents a lack of identification.

Based on these characteristics it is safe to say that both undefined and null represent the absence of a value. Therefore, any code we write which aims to check for the absence of a value should account for both undefined and null.

Equality

Now that we understand undefined and null, we still need to briefly address the difference between == and === in order to understand the expressions at the beginning of this post.

Strict

Strict equality is invoked using === and is relatively straight forward. If two values, a and b, are of different types then a === b will return false. However, if they are of the same type then true is returned if their contents match and false otherwise:

Examples:

console.log(0 === 0) // true
console.log('hello!' === 'hello!') // true
console.log(null === null) // true
console.log(undefined === undefined) // true

console.log(0 === 5) // false
console.log(0 === '0') // false
console.log(0 === 'hello!') // false
console.log(null === undefined) // false

var obj = {}
console.log(obj === {}) // false (because objects are compared by reference)
console.log(obj === obj) // true (because reference to same object)

Loose

Loose quality is invoked using == and often produces unexpected results. If two values, a and b, are of the same type then a === b is returned. However, if they are of different types then JavaScript will attempt to coerce (i.e. convert) the two values to the same type and then strictly equate the two. This second case has prompted the use of loose equality to be largely discouraged by the JavaScript community:

Examples:

console.log(1 == 1) // true
console.log(1 == '1') // true (because the string was converted to a number)
console.log('1' == 1) // true (because the string was converted to a number)
console.log(0 == false) // true (because the boolean was converted to a number)
console.log(0 == null) // false (because absence of a value is never considered equal to a concrete value)
console.log({} == {}) // false (because objects are compared by reference)
console.log(0 == undefined) // false (because absence of a value is never considered equal to a concrete value)
console.log(null == undefined) // true (because both represent the absence of a value)
console.log(undefined == null) // true (because both represent the absence of a value)
console.log("hello!" == false) // false
console.log("" == false) // true (because the string was converted to a boolean and an empty string kind of represents falsity in the realm of strings I guess?)
console.log([] == false) // true (because the array was converted to a boolean and an empty array kind of represents falsity in the realm of array I guess?)

If you’re feeling confused you wouldn’t be the only one. Check out this operand conversion table and this article about truthy and falsey values if you want to fully understand loose equality. Additionally, if you want a handy reference of how == and === behave then I would recommend this link.

Bringing It All Together

It’s time to check which of the expressions from the beginning of the post work! Let’s take a look at the first expression and write a checklist to evaluate it:

console.log(value == null)

You may have noticed that value == undefined would also work for almost the same reasons. However, value == null is safer because the value of undefined is not guaranteed to stay constant. Prior to JavaScript version ES5 undefined could be reassigned since it’s simply a global property and even in the most recent versions of JavaScript undefined can be shadowed by a local variable. This could never happen with null because it is a literal and that makes it the objectively better choice.

It is worth noting that these issues with undefined can be avoided by using the void operator instead because it is guaranteed to return the expected value of undefined. Most commonly, the expression passed to the void operator for this purpose is 0 because void 0 is short and quick to evaluate. However, I would still not recommend using value == void 0 in place of value == null because it may confuse other programmers (many of which are unfamiliar with the void operator), null is two characters shorter than void 0, and void 0 may be marginally slower than null since 0 must be evaluated before undefined is returned.

These methods work except for one lurking issue. All of our questions assume that we know for a fact that value has been declared and we have access to it. However, if value is undeclared our code will throw a ReferenceError. This may seem absurd because don’t we always know if a variable has been declared or not? Unfortunately this is not always the case.

Many JavaScript libraries aim to be platform agnostic. They are designed in such a way which allows them to run in the browser, on the server, or as a Node.js module. In Node.js there is a global variable module which can be used to export methods for use in other modules, but on the browser this variable is never declared. Therefore, if we execute module == null on Node.js it would return false, but on browsers it would throw a ReferenceError! One way to handle this issue would be to use try catch blocks to catch the ReferenceError and resume execution in the case we’re not running on Node.js:

try {
  value // expression statement will throw a ReferenceError if value is an undeclared variable
  console.log('value is declared') // will log if the previous statement did not throw an error
} catch (err) {
  console.log('value is undeclared') // will log if a ReferenceError was thrown
}

Note that if any code following the first statement in the try block throws an error for some other reason then the catch block would be executed even though value was declared. This issue can be avoided by checking that the thrown error was specifically a ReferenceError using the instanceof operator:

try {
  value
  console.log('value is declared')
  /* some potentially error-throwing code */
} catch (err) {
  if (err instanceof ReferenceError) {
    console.log('value is undeclared')
  } else {
    console.log('Some other error occurred')
  }
}

Note that this solution only works if the potentially error-throwing code does not also throw a ReferenceError because it would also match the if condition. I cannot think of any reason anyone would do this on purpose. This situation would likely arise due to misspelling the name of a declared variable. For this reason you should try to keep the code in the try catch blocks as short as possible. The if condition could also be altered to check the ReferenceError message string for our specific variable err instanceof ReferenceError && err.message.split(' ')[0] === 'value', but I do not recommend it because it assumes your code has misspelled variables names which can and should be debugged and fixed.

The code with the if condition kept the same is a good solution if you specifically want to check if a variable is declared or not. However, if you want to classify undeclared variables as absent values and lump them in with undefined and null then fortunately there is a better solution. It turns out that checking the type of an undeclared variable using the typeof operator will not throw a ReferenceError, but will return the string 'undefined' instead. This is convenient because checking the type of a declared variable with a value of undefined using the typeof operator will also return the string 'undefined'. So the expression typeof value === 'undefined' also checks off the first item on our checklist! However, it doesn’t take into account if value is null so we must add an additional check in an or clause:

console.log(typeof value === 'undefined' || value === null)

This method works in every situation, but it is slower than value == null. The optimal strategy is to use this method when you don’t know if value has been declared and use the previous method when you do. This is the approach taken by CoffeeScript when transpiling its existential operator to JavaScript.

You may have noticed that a few of the expressions at the beginning of the post look almost identical to the expression we just evaluated. Interestingly enough the following four expressions share the same behavior:

console.log(typeof value === 'undefined' || value === null)
console.log(typeof value === 'undefined' || value == null)
console.log(typeof value == 'undefined' || value == null)
console.log(typeof value == 'undefined' || value === null)

So why did we choose the expression with strict equality in both conditions?

The second bullet point makes a strong argument for using strict equality for the second condition because value may not be the same type as null, but in the first condition both typeof value and 'undefined' are guaranteed to be of type string so the decision to use strict equality is only supported by the first and third bullet points. This makes the first expression above the best choice.

Lastly, let’s evaluate the rest of the expressions from the beginning of the post:

console.log(value === null) // doesn't account for undefined
console.log(value === undefined) // doesn't account for null
console.log(value === undefined || value === null) // works, but is simply a slower version of value == null and value == undefined
console.log(typeof value === 'undefined') // doesn't account for null
console.log(typeof value == 'undefined') // doesn't account for null
console.log(!value) // erroneously returns true for falsey values such as false, '', [], 0, etc.

Object Properties

When checking for the absence of a value in an object property, additional considerations must be made regarding the property itself. Consider the following example where we use value == null to check for the absence of a value in each object’s key property:

var obj1 = {}
var obj2 = {
  key: undefined
}

console.log(obj1.key == null) // true
console.log(obj2.key == null) // true

An object without a key property produces the same result as an object with its key property set to a value of undefined. This is because in contrast to undeclared variables, trying to access the value of an undeclared property always returns undefined. This means that value == null is a good solution if you want to classify undeclared properties as absent values and lump them in with undefined and null. However, if you specifically want to check if a property is declared or not then a different method must be used.

One way is to use the in operator:

var obj1 = {}
var obj2 = {
  key: undefined
}

console.log('key' in obj1) // false
console.log('key' in obj2) // true

Note that a string or Symbol containing the property name must be used on the lefthand side of the in operator, not a token. This may seem like a good solution, but consider the following case:

var obj1 = {}
var obj2 = {
  constructor: undefined
}

console.log('constructor' in obj1) // true
console.log('constructor' in obj2) // true

Probably not what you expected right? The expression 'constructor' in obj1 returns true because the constructor property was inherited from the object’s prototype chain. This means that the in operator considers both the specific properties of the object as well as inherited properties.

Fortunately, there is a way to check just the specific uninherited properties of the object using the hasOwnProperty method, which itself is inherited from the Object constructor, or class in object oriented terms:

var obj1 = {}
var obj2 = {
  constructor: undefined
}

console.log(obj1.hasOwnProperty('constructor')) // false
console.log(obj2.hasOwnProperty('constructor')) // true

Note that unlike the in operator, the hasOwnProperty method can only take a string argument. There is one caveat to using the hasOwnProperty method. Consider the following case:

var obj = {
  hasOwnProperty: function () {
    return true
  }
}

console.log(obj.hasOwnProperty('wow')) // true

The hasOwnProperty method of the Object constructor was shadowed, or overridden in object oriented terms, by a method which always returns true. Accessing properties always prefers uninherited to inherited ones which is why true was returned for the name of an undeclared property. Fortunately there is a way around this. The hasOwnProperty method can be accessed directly from the Object constructor and called with this as a specified value using the call method of the Function constructor. The call method takes the value of this as its first argument and the arguments to the called function as the rest of its arguments:

var obj = {
  hasOwnProperty: function () {
    return true
  }
}

console.log(Object.prototype.hasOwnProperty.call(obj, 'wow')) // false

If you find yourself using this method more than once I would recommend extracting it out as a function:

function hasOwnProperty(obj, property) {
  return Object.prototype.hasOwnProperty.call(obj, property)
}

var obj = {
  hasOwnProperty: function () {
    return true
  }
}

console.log(hasOwnProperty(obj, 'wow')) // false

Conclusion

To recap here are the optimal expressions.

Checking if a variable is declared:

try {
  value
  // value is declared
} catch (err) {
  if (err instanceof ReferenceError) {
    // value is undeclared
  } else {
    // some other error occurred
  }
}

Checking for the absence of an uninherited property in an object when the object definitely doesn’t have a shadowing hasOwnProperty property:

!obj.hasOwnProperty(key)

Checking for the existence of an uninherited property in an object when the object definitely doesn’t have a shadowing hasOwnProperty property:

obj.hasOwnProperty(key)

Checking for the absence of an uninherited property in an object when the object may have a shadowing hasOwnProperty property:

!Object.prototype.hasOwnProperty.call(obj, key)

Checking for the existence of an uninherited property in an object when the object may have a shadowing hasOwnProperty property:

Object.prototype.hasOwnProperty.call(obj, key)

Checking for the absence of an inherited or uninherited property in an object:

!(key in obj)

Checking for the existence of an inherited or uninherited property in an object:

key in obj

Checking for the absence of a value when the value may be an undeclared variable:

typeof value === 'undefined' || value === null

Checking for the existence of a value when the value may be an undeclared variable (derived using De Morgan’s Law):

typeof value !== 'undefined' && value !== null

Checking for the absence of a value when the value is definitely declared:

value == null

Checking for the existence of a value when the value is definitely declared:

value != null

Checking for the absence of a value when the value is definitely declared and you want to avoid loose equality:

value === null || value === void 0

Checking for the existence of a value when the value is definitely declared and you want to avoid loose equality (derived using De Morgan’s Law):

value !== null && value !== void 0

Feel free to use combinations of these to fit your needs. For example, here’s how you would check if an object has an uninherited property which has an absent value such as undefined or null when the object definitely doesn’t have a shadowing hasOwnProperty property:

obj.hasOwnProperty(key) && obj[key] == null

Thank you for reading!