DEV Community

Romain Trotard
Romain Trotard

Posted on • Edited on • Originally published at romaintrotard.com

Different types of equality in javascript.

Javascript has a lot of types of equality. From my point of view, it is important to know how they work and the differences between them to understand
when to use each one. Let's start with the most known: the strict equality.

Strict equality

The strict equality or triple equals === checks the the equality between two values with their types. When we have objects ({} or []), a
function or a Symbol, the references are compared:

console.log(23 === 23); // true console.log('hello' === 'hello'); // true console.log(0 === -0); // true console.log(undefined === undefined); // true console.log(null === null); // true console.log(null === undefined); // false console.log('23' === 23); // false console.log({} === {}); // false console.log([] === []); // false console.log(NaN === NaN); // false console.log(Infinity === Infinity); // true console.log(Infinity === -Infinity); // false console.log(Symbol('aSymbol') === Symbol('aSymbol')); // false console.log(Symbol.for('aSymbol') === Symbol.for('aSymbol')); // true const firstMethod = () => {}; const secondMethod = () => {}; console.log(firstMethod === secondMethod); // false 
Enter fullscreen mode Exit fullscreen mode

This equality is the most used in projects. Watch out to cases 0 === -0 and NaN !== NaN.

Weak equality

The weak equality or double equals == converts the two values to a same type (named type coercion) and then compare them. When we have objects ({} or []), a
function or a Symbol, the references are compared:

console.log(23 == 23); // true console.log('bonjour' == 'bonjour'); // true console.log(0 == -0); // true console.log(undefined == undefined); // true console.log(null == null); // true console.log(null == undefined); // true console.log('23' == 23); // true console.log({} == {}); // false console.log([] == []); // false console.log(NaN == NaN); // false console.log(Infinity == Infinity); // true console.log(Infinity == -Infinity); // false console.log(Symbol('aSymbol') == Symbol('aSymbol')); // false console.log(Symbol.for('aSymbol') == Symbol.for('aSymbol')); // true const firstMethod = () => {}; const secondMethod = () => {}; console.log(firstMethod == secondMethod); // false 
Enter fullscreen mode Exit fullscreen mode

Here are some specific cases:

console.log('' == false); // true console.log('' == 0); // true console.log([] == ''); // true console.log([[]] == ''); // true console.log([1] == true); // true console.log([0] == false); // true console.log([[0]] == false); // true 
Enter fullscreen mode Exit fullscreen mode

Object.is

Object.is works like the strict equality except for two cases:

console.log(Object.is(NaN, NaN)); // true console.log(NaN === NaN); // false console.log(Object.is(0, -0)); // false console.log(0 === -0); // true 
Enter fullscreen mode Exit fullscreen mode

This equality is used in the dependencies's arrays of React hooks: useEffect, useCallback and useMemo.

Warning: the method is not available on IE11, a polyfill will be required.

Shallow equal

The shallow equal is a comparison based on Object.is which will also compare first level values of object ({} or []):

// Implementation of Object.is not to use polyfill function is(x, y) { // Let's detect if we are not in the case 0 === -0 where we should have !Object.is(0, -0) if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y } else { // The second case is NaN !== NaN but Object.is(NaN, NaN) return x !== x && y !== y } } function shallowEqual(objA, objB) { if (is(objA, objB)) return true if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false } // Let's go through all keys of objects const keysA = Object.keys(objA) const keysB = Object.keys(objB) if (keysA.length !== keysB.length) return false for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false } } return true } 
Enter fullscreen mode Exit fullscreen mode

In this case we have the following equalities:

console.log(shallowEqual(0, -0)); // false console.log(shallowEqual({}, {})); // true console.log(shallowEqual([], [])); // true console.log(shallowEqual(new String('value'), new String('value'))); // true console.log(shallowEqual({ firstKey: 'firstValue', secondKey: 'secondValue '}, { firstKey: 'firstValue', secondKey: 'secondValue '})); // true console.log(shallowEqual(['firstValue', 'secondValue'], ['firstValue', 'secondValue'])); // true console.log(shallowEqual({ 0: 'firstValue', 1: 'secondValue '}, ['firstValue', 'secondValue '])); // true console.log(shallowEqual({ firstKey: {} }, { firstKey: {} })); // false console.log(shallowEqual({ firstKey: [] }, { firstKey: [] })); // false console.log(shallowEqual({ firstKey: 'firstValue' }, { firstKey: 'firstValue', secondKey: 'secondValue' })); // false console.log(shallowEqual([ 'firstValue' ], [ 'firstValue', 'secondValue' ])); // false console.log(shallowEqual([ {} ], [ {} ])); // false 
Enter fullscreen mode Exit fullscreen mode

This equality is used in React for PureComponent (in shouldComponentUpdate lifecycle method) and React.memo (default equality which
is overridable).

Deep equal

The deep equal make the deeply comparison of objects possible (not only the first level):

// Implementation of Object.is not to use polyfill function is(x, y) { // Let's detect if we are not in the case 0 === -0 where we should have !Object.is(0, -0) if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y } else { // The second case is NaN !== NaN but Object.is(NaN, NaN) return x !== x && y !== y } } function deepEqual(object1, object2) { if (is(object1, object2)) { return true; } if ( typeof object1 !== "object" || object1 === null || typeof object2 !== "object" || object2 === null ) { return false; } // Let's go through all keys of objects const object1Keys = Object.keys(object1); const object2Keys = Object.keys(object2); if (object1Keys.length !== object2Keys.length) { return false; } for (let i = 0; i < object1Keys.length; i++) { const key = object1Keys[i]; if ( !Object.prototype.hasOwnProperty.call(object2, key) || // We call recursively the method to go deeper as soon as we have an object !deepEqual(object1[key], object2[key]) ) { return false; } } return true; } 
Enter fullscreen mode Exit fullscreen mode

In this case we have the following equalities:

console.log(deepEqual(0, -0)); // false console.log(deepEqual({}, {})); // true console.log(deepEqual([], [])); // true console.log(deepEqual({ firstKey: 'firstValue', secondKey: 'secondValue '}, { firstKey: 'firstValue', secondKey: 'secondValue '})); // true console.log(deepEqual(['firstValue', 'secondValue'], ['firstValue', 'secondValue'])); // true console.log(deepEqual({ 0: 'firstValue', 1: 'secondValue '}, ['firstValue', 'secondValue '])); // true console.log(deepEqual(deepEqual({ firstKey: {} }, { firstKey: {} })); // true console.log(deepEqual({ firstKey: [] }, { firstKey: [] })); // true console.log(deepEqual([ {} ], [ {} ])); // true console.log(deepEqual(deepEqual({ firstKey: { deepKey: { key: 'value1' } } }, { firstKey: { deepKey: { key: 'value2' } } })); // false console.log(deepEqual({ firstKey: 'firstValue' }, { firstKey: 'firstValue', secondKey: 'secondValue' })); // false console.log(deepEqual([ 'firstValue' ], [ 'firstValue', 'secondValue' ])); // false 
Enter fullscreen mode Exit fullscreen mode

This equality is commonly used in tests when we have to assert the result of a function (for example) to the expected result.

It exists some libraries which can go faster than other implementations when doing deep equals:
fast-deep-equal and
react-fast-compare when developing with React.


Thank you for reading.
You can find my social links on my website.

Top comments (0)