JavaScript equality: equals, == vs ===, and the equal sign

JavaScript equality: loose == vs strict ===, type coercion, null and undefined, NaN and Object.is. Short notes after snippets describe expected console output (Node.js v20.18.2).

Published

Updated

Read time 6 min read

Reviewed byDeepak Prasad

JavaScript equality: equals, == vs ===, and the equal sign

People look up javascript equals, javascript equality, equal javascript, and equality javascript for the same core topic: how the language decides whether two values “match.” In code you express that with the equal sign operators: a single pair == for loose equality, or === when you want strict equality (often what people mean when they type javascript === in a search). Understanding both helps you read older codebases and avoid subtle bugs.

Loose equality may apply type coercion so operands become comparable types. Strict equality compares both type and value without that conversion. For day‑to‑day logic, defaulting to === is the usual style guide recommendation; knowing == still matters when you read specs, minified libraries, or interviews.

Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.


Quick reference

Operator Coercion? Typical use
== Yes (abstract equality) Rare; intentional null/undefined checks
=== No Default for primitives and reference identity
Object.is No; SameValue NaN sameness, +0 vs -0

Types you compare with javascript equal and strict operators

Before comparing values, it helps to know what typeof reports. Strings, numbers, and booleans are primitives. Ordinary objects and arrays are both of type "object" in this check.

javascript
const str = 'JavaScript equals can be challenging.';
console.log(typeof str);
const myIntNumber = 15;
const myDoubleDiscount = 15.93;
console.log(typeof myIntNumber);
console.log(typeof myDoubleDiscount);
const literal = { a: 3, b: 7 };
const array = ['string', 19, true];
console.log(typeof literal, typeof array);
Output

You should see four lines: string, two number lines, then object object.

Wrapping a primitive in new String, new Number, or new Boolean produces an object wrapper. That distinction shows up immediately under typeof, and it also affects how == coerces values.

javascript
const stringLiteral = 'I like to compare strings.';
const stringObject = new String('I like to compare strings.');
const numberLiteral = 89;
const numberObject = new Number(89);
console.log(typeof stringLiteral);
console.log(typeof stringObject);
console.log(typeof numberLiteral);
console.log(typeof numberObject);
const booleanLiteral = false;
const booleanObject = new Boolean(null);
console.log(typeof booleanLiteral);
console.log(typeof booleanObject);
Output

You should see alternating string / object, number / object, and boolean / object—literals stay primitives; wrappers report object.

Custom constructors behave like other objects: the instance is an object, while a plain string literal stays a string.

javascript
function Person(name) {
  this.name = name;
}
const personLiteral = 'John Doe';
const personObject = new Person('John Doe');
console.log(typeof personLiteral);
console.log(typeof personObject);
Output

You should see string then object.

Truthiness still drives if statements; for a refresher on empty collections, see check if a JavaScript array is empty.


Loose equality when types already match

With the same types and no coercion path, == behaves like common sense: strings compare by character sequence and casing, numbers by numeric value, booleans by their true or false value.

javascript
console.log('doe' == 'doe');
console.log('doe' == 'Doe');
Output

You should see true then false.

javascript
console.log(3 == 3);
console.log(3 == 13);
Output

You should see true then false.

javascript
console.log(false == false);
console.log(true == false);
Output

You should see true then false.


When javascript equality converts operands

Strings compared to numbers

If one side is a string and the other a number, the string is converted to a number (or to NaN if it is not numeric) before the numeric comparison.

javascript
console.log(17 == '17');
console.log(17 == '37');
Output

You should see true then false.

Boxed numbers against primitives

Object wrappers participate in the abstract equality algorithm: the object is coerced to its primitive value before comparison.

javascript
const numObject = new Number(97);
const numPrimitive = 97;
console.log(numObject == numPrimitive);
Output

You should see true.

Booleans mixed with numbers

If either operand is a boolean, it is first converted to a number (true to 1, false to 0) before the rest of the comparison runs.

javascript
console.log(true == 1);
console.log(0 == false);
console.log(true == 19);
Output

You should see true, true, then false.

null, undefined, and a common pitfall

null and undefined are only loosely equal to each other, not to other types such as numbers.

javascript
console.log(null == undefined);
console.log(null === undefined);
console.log(null == 0);
Output

You should see true, false, false.

So null == undefined is true, but null === undefined is false, and null == 0 is false even though 0 == false is true—the algorithms are different branches.


Strict equality: the javascript equal sign pair ===

Strict equality, written with three equal signs, skips type conversion. Both type and value must already line up.

javascript
console.log(17 === '17');
console.log(17 === 17);
console.log(0 === false);
Output

You should see false, true, false.

That is why searches for javascript === land on this operator: it is the straightforward “same type and same value” check for primitives and object identity.


Why NaN === NaN is false (and how Object.is helps)

IEEE‑754 NaN is not equal to itself under strict equality, which surprises readers who expect “equals javascript” to behave symmetrically for every numeric edge case.

javascript
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
Output

You should see false then true.

Object.is follows SameValue semantics and treats two NaN values as a match, while still distinguishing +0 and -0 unlike ===.


Practical guidance

Default to === for application logic so equality javascript checks stay predictable. Reserve == for cases you truly intend to coerce, such as comparing against null and undefined together with (value == null), which some teams use as a shorthand for “either nullish value.” Document that pattern in code review so future readers know it was deliberate.


Summary

Use === by default; use == only when coercion is intentional; use Object.is when NaN or signed zero matter.

JavaScript equality questions usually start with javascript === versus loose equals javascript: strict equality === compares without coercion, while == applies the abstract equality comparison rules that surprise teams when "0" meets 0. Default to === for application logic, and reserve == for the narrow cases you truly intend to coerce—(value == null) is the most common intentional pattern because it matches both null and undefined.

Readers also ask why NaN === NaN is false and how Object.is differs: Object.is uses SameValue semantics, so two NaN values match while +0 and -0 remain distinguishable, which matters for certain math and animation code paths. When edge cases pile up, MDN’s equality comparisons and sameness guide is the authoritative cheat sheet.


Further reading

Steve Alila

Specializes in web design, WordPress development, and data analysis, with proficiency in Python, JavaScript, and data extraction tools. Additionally, he excels in web API development, AI integration, …