Comparison Operations
Abstract equality / inequality and type conversion
Section titled “Abstract equality / inequality and type conversion”The Problem
Section titled “The Problem”The abstract equality and inequality operators (== and !=) convert their operands if the operand types do not match. This type coercion is a common source of confusion about the results of these operators, in particular, these operators aren’t always transitive as one would expect.
"" == 0; // true A 0 == "0"; // true A"" == "0"; // false Bfalse == 0; // truefalse == "0"; // true
"" != 0; // false A 0 != "0"; // false A"" != "0"; // true Bfalse != 0; // falsefalse != "0"; // falseThe results start to make sense if you consider how JavaScript converts empty strings to numbers.
Number(""); // 0Number("0"); // 0Number(false); // 0The Solution
Section titled “The Solution”In the statement false B, both the operands are strings ("" and "0"), hence there will be no type conversion and since "" and "0" are not the same value, "" == "0" is false as expected.
One way to eliminate unexpected behavior here is making sure that you always compare operands of the same type. For example, if you want the results of numerical comparison use explicit conversion:
var test = (a,b) => Number(a) == Number(b);test("", 0); // true;test("0", 0); // truetest("", "0"); // true;test("abc", "abc"); // false as operands are not numbersOr, if you want string comparison:
var test = (a,b) => String(a) == String(b);test("", 0); // false;test("0", 0); // truetest("", "0"); // false;Side-note: Number("0") and new Number("0") isn’t the same thing! While the former performs a type conversion, the latter will create a new object. Objects are compared by reference and not by value which explains the results below.
Number("0") == Number("0"); // true;new Number("0") == new Number("0"); // falseFinally, you have the option to use strict equality and inequality operators which will not perform any implicit type conversions.
"" === 0; // false 0 === "0"; // false"" === "0"; // falseFurther reference to this topic can be found here:
Which equals operator (== vs ===) should be used in JavaScript comparisons?.
NaN Property of the Global Object
Section titled “NaN Property of the Global Object”NaN (“Not a Number”) is a special value defined by the IEEE Standard for Floating-Point Arithmetic, which is used when a non-numeric value is provided but a number is expected (1 * "two"), or when a calculation doesn’t have a valid number result (Math.sqrt(-1)).
Any equality or relational comparisons with NaN returns false, even comparing it with itself. Because, NaN is supposed to denote the result of a nonsensical computation, and as such, it isn’t equal to the result of any other nonsensical computations.
(1 * "two") === NaN //false
NaN === 0; // falseNaN === NaN; // falseNumber.NaN === NaN; // false
NaN < 0; // falseNaN > 0; // falseNaN > 0; // falseNaN >= NaN; // falseNaN >= 'two'; // falseNon-equal comparisons will always return true:
NaN !== 0; // trueNaN !== NaN; // trueChecking if a value is NaN
Section titled “Checking if a value is NaN”You can test a value or expression for NaN by using the function Number.isNaN():
Number.isNaN(NaN); // trueNumber.isNaN(0 / 0); // trueNumber.isNaN('str' - 12); // true
Number.isNaN(24); // falseNumber.isNaN('24'); // falseNumber.isNaN(1 / 0); // falseNumber.isNaN(Infinity); // false
Number.isNaN('str'); // falseNumber.isNaN(undefined); // falseNumber.isNaN({}); // falseYou can check if a value is NaN by comparing it with itself:
value !== value; // true for NaN, false for any other valueYou can use the following polyfill for Number.isNaN():
Number.isNaN = Number.isNaN || function(value) { return value !== value;}By contrast, the global function isNaN() returns true not only for NaN, but also for any value or expression that cannot be coerced into a number:
isNaN(NaN); // trueisNaN(0 / 0); // trueisNaN('str' - 12); // true
isNaN(24); // falseisNaN('24'); // falseisNaN(Infinity); // false
isNaN('str'); // trueisNaN(undefined); // trueisNaN({}); // trueECMAScript defines a “sameness” algorithm called SameValue which, since ECMAScript 6, can be invoked with Object.is. Unlike the == and === comparison, using Object.is() will treat NaN as identical with itself (and -0 as not identical with +0):
Object.is(NaN, NaN) // trueObject.is(+0, 0) // false
NaN === NaN // false+0 === 0 // trueYou can use the following polyfill for Object.is() (from MDN):
if (!Object.is) { Object.is = function(x, y) { // SameValue algorithm if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 return x !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } };}Points to note
Section titled “Points to note”NaN itself is a number, meaning that it does not equal to the string “NaN”, and most importantly (though perhaps unintuitively):
typeof(NaN) === "number"; //trueShort-circuiting in boolean operators
Section titled “Short-circuiting in boolean operators”The and-operator (&&) and the or-operator (||) employ short-circuiting to prevent unnecessary work if the outcome of the operation does not change with the extra work.
In x && y, y will not be evaluated if x evaluates to false, because the whole expression is guaranteed to be false.
In x || y, y will not be evaluated if x evaluated to true, because the whole expression is guaranteed to be true.
Example with functions
Take the following two functions:
function T() { // True console.log("T"); return true;}
function F() { // False console.log("F"); return false;}Example 1
T() && F(); // falseOutput:
'T'
'F'
Example 2
F() && T(); // falseOutput:
‘F’
Example 3
T() || F(); // trueOutput:
‘T’
Example 4
F() || T(); // trueOutput:
'F'
'T'
Short-circuiting to prevent errors
var obj; // object has value of undefinedif(obj.property){ }// TypeError: Cannot read property 'property' of undefinedif(obj.property && obj !== undefined){}// Line A TypeError: Cannot read property 'property' of undefinedLine A: if you reverse the order the first conditional statement will prevent the error on the second by not executing it if it would throw the error
if(obj !== undefined && obj.property){}; // no error thrownBut should only be used if you expect undefined
if(typeof obj === "object" && obj.property){}; // safe option but slowerShort-circuiting to provide a default value
The || operator can be used to select either a “truthy” value, or the default value.
For example, this can be used to ensure that a nullable value is converted to a non-nullable value:
var nullableObj = null;var obj = nullableObj || {}; // this selects {}
var nullableObj2 = {x: 5};var obj2 = nullableObj2 || {} // this selects {x: 5}Or to return the first truthy value
var truthyValue = {x: 10};return truthyValue || {}; // will return {x: 10}The same can be used to fall back multiple times:
envVariable || configValue || defaultConstValue // select the first "truthy" of theseShort-circuiting to call an optional function
The && operator can be used to evaluate a callback, only if it is passed:
function myMethod(cb) { // This can be simplified if (cb) { cb(); }
// To this cb && cb();}Of course, the test above does not validate that cb is in fact a function and not just an Object/Array/String/Number.
Abstract Equality (==)
Section titled “Abstract Equality (==)”Operands of the abstract equality operator are compared after being converted to a common type. How this conversion happens is based on the specification of the operator:
Specification for the == operator:
7.2.13 Abstract Equality Comparison
The comparison `x == y`, where `x` and `y` are values, produces `true` or `false`. Such a comparison is performed as follows:-
- If `Type(x)` is the same as `Type(y)`, then:
-
- **a.** Return the result of performing Strict Equality Comparison `x === y`.
-
- If `x` is `null` and `y` is `undefined`, return `true`.
- If `x` is `undefined` and `y` is `null`, return `true`.
- If `Type(x)` is `Number` and `Type(y)` is `String`, return the result of the comparison `x == ToNumber(y)`.
- If `Type(x)` is `String` and `Type(y)` is `Number`, return the result of the comparison `ToNumber(x) == y`.
- If `Type(x)` is `Boolean`, return the result of the comparison `ToNumber(x) == y`.
- If `Type(y)` is `Boolean`, return the result of the `comparison x == ToNumber(y)`.
- If `Type(x)` is either `String`, `Number`, or `Symbol` and `Type(y)` is `Object`, return the result of the comparison `x == ToPrimitive(y)`.
- If `Type(x)` is Object and `Type(y)` is either `String`, `Number`, or `Symbol`, return the result of the comparison `ToPrimitive(x) == y`.
- Return `false`.
Examples:
Section titled “Examples:”1 == 1; // true1 == true; // true (operand converted to number: true => 1)1 == '1'; // true (operand converted to number: '1' => 1 )1 == '1.00'; // true1 == '1.00000000001'; // false1 == '1.00000000000000001'; // true (true due to precision loss)null == undefined; // true (spec #2)1 == 2; // false0 == false; // true0 == undefined; // false0 == ""; // trueNull and Undefined
Section titled “Null and Undefined”The differences between null and undefined
Section titled “The differences between null and undefined”null and undefined share abstract equality == but not strict equality ===,
null == undefined // truenull === undefined // falseThey represent slightly different things:
undefinedrepresents the absence of a value, such as before an identifier/Object property has been created or in the period between identifier/Function parameter creation and it’s first set, if any.nullrepresents the intentional absence of a value for an identifier or property which has already been created.
They are different types of syntax:
undefinedis a property of the global Object, usually immutable in the global scope. This means anywhere you can define an identifier other than in the global namespace could hideundefinedfrom that scope (although things can still beundefined)nullis a word literal, so it’s meaning can never be changed and attempting to do so will throw an Error.
The similarities between null and undefined
Section titled “The similarities between null and undefined”null and undefined are both falsy.
if (null) console.log("won't be logged");if (undefined) console.log("won't be logged");Neither null or undefined equal false (see this question).
false == undefined // falsefalse == null // falsefalse === undefined // falsefalse === null // falseUsing undefined
Section titled “Using undefined”- If the current scope can’t be trusted, use something which evaluates to undefined, for example
void 0;. - If
undefinedis shadowed by another value, it’s just as bad as shadowingArrayorNumber. - Avoid setting something as
undefined. If you want to remove a property bar from an Objectfoo,delete foo.bar;instead. - Existence testing identifier
fooagainstundefinedcould throw a Reference Error, usetypeof fooagainst"undefined"instead.
Logic Operators with Booleans
Section titled “Logic Operators with Booleans”var x = true, y = false;This operator will return true if both of the expressions evaluate to true. This boolean operator will employ short-circuiting and will not evaluate y if x evaluates to false.
x && y;This will return false, because y is false.
This operator will return true if one of the two expressions evaluate to true. This boolean operator will employ short-circuiting and y will not be evaluated if x evaluates to true.
x || y;This will return true, because x is true.
This operator will return false if the expression on the right evaluates to true, and return true if the expression on the right evaluates to false.
!x;This will return false, because x is true.
Logic Operators with Non-boolean values (boolean coercion)
Section titled “Logic Operators with Non-boolean values (boolean coercion)”Logical OR (||), reading left to right, will evaluate to the first truthy value. If no truthy value is found, the last value is returned.
var a = 'hello' || ''; // a = 'hello'var b = '' || []; // b = []var c = '' || undefined; // c = undefinedvar d = 1 || 5; // d = 1var e = 0 || {}; // e = {}var f = 0 || '' || 5; // f = 5var g = '' || 'yay' || 'boo'; // g = 'yay'Logical AND (&&), reading left to right, will evaluate to the first falsy value. If no falsey value is found, the last value is returned.
var a = 'hello' && ''; // a = ''var b = '' && []; // b = ''var c = undefined && 0; // c = undefinedvar d = 1 && 5; // d = 5var e = 0 && {}; // e = 0var f = 'hi' && [] && 'done'; // f = 'done'var g = 'bye' && undefined && 'adios'; // g = undefinedThis trick can be used, for example, to set a default value to a function argument (prior to ES6).
var foo = function(val) { // if val evaluates to falsey, 'default' will be returned instead. return val || 'default';}
console.log( foo('burger') ); // burgerconsole.log( foo(100) ); // 100console.log( foo([]) ); // []console.log( foo(0) ); // defaultconsole.log( foo(undefined) ); // defaultJust keep in mind that for arguments, 0 and (to a lesser extent) the empty string are also often valid values that should be able to be explicitly passed and override a default, which, with this pattern, they won’t (because they are falsy).
Empty Array
Section titled “Empty Array”/* ToNumber(ToPrimitive([])) == ToNumber(false) */[] == false; // trueWhen [].toString() is executed it calls [].join() if it exists, or Object.prototype.toString() otherwise. This comparison is returning true because [].join() returns '' which, coerced into 0, is equal to false ToNumber.
Beware though, all objects are truthy and Array is an instance of Object:
// Internally this is evaluated as ToBoolean([]) === true ? 'truthy' : 'falsy'[] ? 'truthy' : 'falsy'; // 'truthy'Automatic Type Conversions
Section titled “Automatic Type Conversions”Beware that numbers can accidentally be converted to strings or NaN (Not a Number).
JavaScript is loosely typed. A variable can contain different data types, and a variable can change its data type:
var x = "Hello"; // typeof x is a stringx = 5; // changes typeof x to a numberWhen doing mathematical operations, JavaScript can convert numbers to strings:
var x = 5 + 7; // x.valueOf() is 12, typeof x is a numbervar x = 5 + "7"; // x.valueOf() is 57, typeof x is a stringvar x = "5" + 7; // x.valueOf() is 57, typeof x is a stringvar x = 5 - 7; // x.valueOf() is -2, typeof x is a numbervar x = 5 - "7"; // x.valueOf() is -2, typeof x is a numbervar x = "5" - 7; // x.valueOf() is -2, typeof x is a numbervar x = 5 - "x"; // x.valueOf() is NaN, typeof x is a numberSubtracting a string from a string, does not generate an error but returns NaN (Not a Number):
"Hello" - "Dolly" // returns NaNEquality comparison operations
Section titled “Equality comparison operations”JavaScript has four different equality comparison operations.
It returns true if both operands belong to the same Type and are the same value.
Note: the value of an object is a reference.
You can use this comparison algorithm via Object.is (ECMAScript 6).
Examples:
Object.is(1, 1); // trueObject.is(+0, -0); // falseObject.is(NaN, NaN); // trueObject.is(true, "true"); // falseObject.is(false, 0); // falseObject.is(null, undefined); // falseObject.is(1, "1"); // falseObject.is([], []); // falseThis algorithm has the properties of an equivalence relation:
- Reflexivity:
Object.is(x, x)istrue, for any valuex - Symmetry:
Object.is(x, y)istrueif, and only if,Object.is(y, x)istrue, for any valuesxandy. - Transitivity: If
Object.is(x, y)andObject.is(y, z)aretrue, thenObject.is(x, z)is alsotrue, for any valuesx,yandz.
It behaves like SameValue, but considers +0 and -0 to be equal.
You can use this comparison algorithm via Array.prototype.includes (ECMAScript 7).
Examples:
[1].includes(1); // true[+0].includes(-0); // true[NaN].includes(NaN); // true[true].includes("true"); // false[false].includes(0); // false[1].includes("1"); // false[null].includes(undefined); // false[[]].includes([]); // falseThis algorithm still has the properties of an equivalence relation:
- Reflexivity:
[x].includes(x)istrue, for any valuex - Symmetry:
[x].includes(y)istrueif, and only if,[y].includes(x)istrue, for any valuesxandy. - Transitivity: If
[x].includes(y)and[y].includes(z)aretrue, then[x].includes(z)is alsotrue, for any valuesx,yandz.
It behaves like SameValue, but
- Considers
+0and-0to be equal. - Considers
NaNdifferent than any value, including itself
You can use this comparison algorithm via the === operator (ECMAScript 3).
There is also the !== operator (ECMAScript 3), which negates the result of ===.
Examples:
1 === 1; // true+0 === -0; // trueNaN === NaN; // falsetrue === "true"; // falsefalse === 0; // false1 === "1"; // falsenull === undefined; // false[] === []; // falseThis algorithm has the following properties:
- Symmetry:
x === yistrueif, and only if, y === xistrue, for any valuesxandy`. - Transitivity: If
x === yandy === zaretrue, thenx === zis alsotrue, for any valuesx,yandz.
But is not an equivalence relation because
NaNis not reflexive:NaN !== NaN
If both operands belong to the same Type, it behaves like the Strict Equality Comparison.
Otherwise, it coerces them as follows:
undefinedandnullare considered to be equal- When comparing a number with a string, the string is coerced to a number
- When comparing a boolean with something else, the boolean is coerced to a number
- When comparing an object with a number, string or symbol, the object is coerced to a primitive
If there was a coercion, the coerced values are compared recursively. Otherwise the algorithm returns false.
You can use this comparison algorithm via the == operator (ECMAScript 1).
There is also the != operator (ECMAScript 1), which negates the result of ==.
Examples:
1 == 1; // true+0 == -0; // trueNaN == NaN; // falsetrue == "true"; // falsefalse == 0; // true1 == "1"; // truenull == undefined; // true[] == []; // falseThis algorithm has the following property:
- Symmetry:
x == yistrueif, and only if,y == xistrue, for any valuesxandy.
But is not an equivalence relation because
NaNis not reflexive:NaN != NaN- Transitivity does not hold, e.g.
0 == ''and0 == '0', but'' != '0'
Relational operators (<, <=, >, >=)
Section titled “Relational operators (<, <=, >, >=)”When both operands are numeric, they are compared normally:
1 < 2 // true2 <= 2 // true3 >= 5 // falsetrue < false // false (implicitly converted to numbers, 1 > 0)When both operands are strings, they are compared lexicographically (according to alphabetical order):
'a' < 'b' // true'1' < '2' // true'100' > '12' // false ('100' is less than '12' lexicographically!)When one operand is a string and the other is a number, the string is converted to a number before comparison:
'1' < 2 // true'3' > 2 // truetrue > '2' // false (true implicitly converted to number, 1 < 2)When the string is non-numeric, numeric conversion returns NaN (not-a-number). Comparing with NaN always returns false:
1 < 'abc' // false1 > 'abc' // falseBut be careful when comparing a numeric value with null, undefined or empty strings:
1 > '' // true1 < '' // false1 > null // true1 < null // false1 > undefined // false1 < undefined // falseWhen one operand is a object and the other is a number, the object is converted to a number before comparison.So null is particular case because Number(null);//0
new Date(2015)< 1479480185280 // truenull > -1 //true({toString:function(){return 123}}) > 122 //trueInequality
Section titled “Inequality”Operator != is the inverse of the == operator.
Will return true if the operands aren’t equal.
The javascript engine will try and convert both operands to matching types if they aren’t of the same type.
Note: if the two operands have different internal references in memory, then false will be returned.
Sample:
1 != '1' // false1 != 2 // trueIn the sample above, 1 != '1' is false because, a primitive number type is being compared to a char value. Therefore, the Javascript engine doesn’t care about the datatype of the R.H.S value.
Operator: !== is the inverse of the === operator.
Will return true if the operands are not equal or if their types do not match.
Example:
1 !== '1' // true1 !== 2 // true1 !== 1 // falseGrouping multiple logic statements
Section titled “Grouping multiple logic statements”You can group multiple boolean logic statements within parenthesis in order to create a more complex logic evaluation, especially useful in if statements.
if ((age >= 18 && height >= 5.11) || (status === 'royalty' && hasInvitation)) { console.log('You can enter our club');}We could also move the grouped logic to variables to make the statement a bit shorter and descriptive:
var isLegal = age >= 18;var tall = height >= 5.11;var suitable = isLegal && tall;var isRoyalty = status === 'royalty';var specialCase = isRoyalty && hasInvitation;var canEnterOurBar = suitable || specialCase;
if (canEnterOurBar) console.log('You can enter our club');Notice that in this particular example (and many others), grouping the statements with parenthesis works the same as if we removed them, just follow a linear logic evaluation and you’ll find yourself with the same result. I do prefer using parenthesis as it allows me to understand clearer what I intended and might prevent for logic mistakes.
List of Comparison Operators
Section titled “List of Comparison Operators”|Operator|Comparison|Example
|---|---|---|---|---|---|---|---|---|---
|==|Equal|i == 0
|===|Equal Value and Type|i === "5"
|!=|Not Equal|i != 5
|!==|Not Equal Value or Type|i !== 5
|>|Greater than|i > 5
|<|Less than|i < 5
|>=|Greater than or equal|i >= 5
|<=|Less than or equal|i <= 5
Bit fields to optimise comparison of multi state data
Section titled “Bit fields to optimise comparison of multi state data”A bit field is a variable that holds various boolean states as individual bits. A bit on would represent true, and off would be false. In the past bit fields were routinely used as they saved memory and reduced processing load. Though the need to use bit field is no longer so important they do offer some benefits that can simplify many processing tasks.
For example user input. When getting input from a keyboard’s direction keys up, down, left,right you can encode the various keys into a single variable with each direction assigned a bit.
Example reading keyboard via bitfield
var bitField = 0; // the value to hold the bitsconst KEY_BITS = [4,1,8,2]; // left up right downconst KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right downwindow.onkeydown = window.onkeyup = function (e) { if(e.keyCode >= 37 && e.keyCode <41){ if(e.type === "keydown"){ bitField |= KEY_BITS[e.keyCode - 37]; }else{ bitField &= KEY_MASKS[e.keyCode - 37]; } }}Example reading as an array
var directionState = [false,false,false,false];window.onkeydown = window.onkeyup = function (e) { if(e.keyCode >= 37 && e.keyCode <41){ directionState[e.keyCode - 37] = e.type === "keydown"; }}To turn on a bit use bitwise or | and the value corresponding to the bit. So if you wish to set the 2nd bit bitField |= 0b10 will turn it on. If you wish to turn a bit off use bitwise and & with a value that has all by the required bit on. Using 4 bits and turning the 2nd bit off bitfield &= 0b1101;
You may say the above example seems a lot more complex than assigning the various key states to a array. Yes It is a little more complex to set but the advantage comes when interrogating the state.
If you want to test if all keys are up.
// as bit fieldif(!bitfield) // no keys are on
// as array test each item in arrayif(!(directionState[0] && directionState[1] && directionState[2] && directionState[3])){You can set some constants to make things easier
// postfix U,D,L,R for Up down left rightconst KEY_U = 1;const KEY_D = 2;const KEY_L = 4;const KEY_R = 8;const KEY_UL = KEY_U + KEY_L; // up leftconst KEY_UR = KEY_U + KEY_R; // up Rightconst KEY_DL = KEY_D + KEY_L; // down leftconst KEY_DR = KEY_D + KEY_R; // down rightYou can then quickly test for many various keyboard states
if ((bitfield & KEY_UL) === KEY_UL) { // is UP and LEFT only downif (bitfield & KEY_UL) { // is Up left downif ((bitfield & KEY_U) === KEY_U) { // is Up only downif (bitfield & KEY_U) { // is Up down (any other key may be down)if (!(bitfield & KEY_U)) { // is Up up (any other key may be down)if (!bitfield ) { // no keys are downif (bitfield ) { // any one or more keys are downThe keyboard input is just one example. Bitfields are useful when you have various states that must in combination be acted on. Javascript can use upto 32 bits for a bit field. Using them can offer significant performance increases. They are worth being familiar with.
Remarks
Section titled “Remarks”When using boolean coercion, the following values are considered “falsy”:
false0""(empty string)nullundefinedNaN(not a number, e.g.0/0)document.all¹ (browser context)
Everything else is considered “truthy”.