Skip to content

About the author of Daydream Drift

Tomasz Niezgoda (LinkedIn/tomaszniezgoda & GitHub/tniezg) is the author of this blog. It contains original content written with care.

Please link back to this website when referencing any of the materials.

Author:

Weak Comparison HELL

Published

Weak comparisons in JavaScript can be used to produce more concise code. However, in practice only a few shortcuts are often used by developers. The rest is risky and produces errors in code which are difficult to debug.

No matter which shortcuts are in fact used, it's best to add some tooling to mitigate bugs stemming from having them in code. ESLint and TypeScript are well-established choices for finding such high-risk places.

Below is a bunch of such tricks.

Conditions

Type coercion (coerce = force) when using == is a process which converts two values to numbers unless they're both already the same type (this summary is not entirely accurate but true most of the time). Type coercion can be memorized but it's easier to just always use ===. Triple equals skips type conversion and requires types of compared values to be the same.

How values are converted by JavaScript to become the same number type is as follows:

JS type coercion

valueOf gets called first on an object type, otherwise toString and what one of these functions return is coerced further if needed. Next, true converts to 1 and false to 0. In the end, any Number other than 0 means comparison is successful and values are equal.

Using == is technically fine if the domain of return values is known. For example, Array.length will always return an integer. So, writing [1,2,3].length == 2 is safe, even though the weak equality operator is used. However, === will have the exact same effect, because two integers are being compared and there's no type coercion happening. Again, it's not worth remembering that == can be used instead.

Coercion also happens with different comparison operators, such as > and <. Nowadays, it's very rare to see these used for non-numerical values and it's not advised.

Conditionals

In contrast to type coercion, JavaScript conditionals evaluate their expressions to boolean, i.e. they stop earlier on the graph linked earlier. It's common to see a condition like:

// #1
if(object){...}

Conditionals like this one are ok as long as we know that: if object has a "truthy" value it will always be an object. Another solution here would be to instead write:

if(typeof object === 'object'){...}

But, if object's value can be other types as well, a boolean for example, then the first (#1) comparison is too weak to know we're dealing with an object.

Forcing boolean

Sometimes, it's a good idea to ensure that instead of a "truthy" value a boolean is returned. !! makes it possible. For example:

function isAvailable(primaryValue, defaultValue) {
  return !!(primaryValue || defaultValue)
}

If !! isn't used inside isAvailable, the returned value may be surprising to anyone using isAvailable in their code if the contract was to have isAvailable always return boolean. This is often forgotten about by developers when using || and &&.

null - good, undefined - bad

null cannot be redefined (shadowed), being not a variable. Also, null does not require scope resolution by JavaScript, neither lookup up to the global scope. All this means null is safer to use and faster than undefined.

How to break undefined:

'use strict';

var undefined = 'abc';

function doSomething(){
	var someVar;// undefined	
	
	if(someVar === undefined){
		console.log('someVar is undefined');
	}
}

doSomething();

Nothing will be output after the script runs, because undefined was redefined to mean something else.

Extra reading: