JavaScript is dynamically typed: a value has a type, and a variable does not. Every value is one of eight types (ECMA-262, ECMAScript Language Types). Seven are primitive, and one, Object, is not. Operators and built-in functions convert values as needed, through a set of abstract operations described below.
The eight types
| Type | Values | typeof | Added |
|---|---|---|---|
| Undefined | undefined only | "undefined" | ES1 |
| Null | null only | "object" | ES1 |
| Boolean | true, false | "boolean" | ES1 |
| Number | IEEE 754 doubles, with NaN and two infinities | "number" | ES1 |
| String | Sequences of 16-bit code units | "string" | ES1 |
| Symbol | Unique, unforgeable keys | "symbol" | ES2015 |
| BigInt | Integers of arbitrary size | "bigint" | ES2020 |
| Object | Collections of properties | "object" or "function" | ES1 |
Primitives are immutable. A method call on one works through a temporary wrapper
object, so 'abc'.length reads from a String object that is
then discarded. Writing a property to a primitive does nothing in sloppy mode and
throws a TypeError in strict mode.
Undefined and Null
undefined is the value of a variable that has been declared but not
initialised, of a missing argument and of a function that returns nothing.
null is a value code assigns to mean “no object”. Both are treated as
absent by ?? and ?., and both throw a
TypeError on property access.
undefined is a read-only property of the global object rather than a
keyword, so it can be shadowed by a local variable of that name. The
void operator always gives the real value: void 0.
Number
A Number is an IEEE 754 binary64 value (ECMA-262, The Number Type). JavaScript has no separate integer or float type.
| Constant | Value | Meaning |
|---|---|---|
Number.MAX_SAFE_INTEGER | 253 − 1 = 9007199254740991 | Largest integer that cannot be confused with a neighbour |
Number.MIN_SAFE_INTEGER | −(253 − 1) | The negative counterpart |
Number.MAX_VALUE | 1.7976931348623157e308 | Largest finite value |
Number.MIN_VALUE | 5e-324 | Smallest positive value, which is subnormal |
Number.EPSILON | 2−52 | The gap between 1 and the next value |
Infinity, -Infinity | Overflow, and division of non-zero by zero | |
NaN | Not a number. Not equal to itself | |
0, -0 | Two distinct zeros, equal under === |
- Arithmetic follows IEEE 754, so
0.1 + 0.2is0.30000000000000004. - All the bit patterns for NaN look the same to a program. There is only one NaN, although the bytes of a typed array can show others.
- Bitwise operators work on 32-bit integers: see integer conversions.
BigInt
A BigInt is an integer of any size, written with an n suffix or created
by BigInt(value).
- Arithmetic may not mix Number and BigInt:
1n + 1throws aTypeError. - Comparison may mix them:
1n < 2and1n == 1are true, though1n === 1is false. - Division truncates towards zero:
7n / 2nis3n. - Unary
+and>>>throw aTypeErroron a BigInt.
String
A String is a finite sequence of 16-bit code units, at most 253 − 1 long.
Text is normally UTF-16, but a string need not be valid UTF-16: a lone surrogate is
allowed, and isWellFormed() and toWellFormed() (ES2024) test
and repair that.
| Access | Works in |
|---|---|
s.length, s[i], s.charCodeAt(i) | Code units |
s.codePointAt(i) | The code point starting at that code unit |
for…of, spread, Array.from(s) | Code points |
So '😀'.length is 2 while [...'😀'].length is 1. Comparison
with < is by code unit value, not by locale; use
localeCompare or Intl.Collator for that.
Symbol
A Symbol is a unique value used as a property key. Symbol('name') creates
a new one each time; the argument is only a description. new Symbol()
throws. Symbol.for(key) gets a symbol from a registry shared across
realms, and Symbol.keyFor(sym) returns its key.
A symbol is not converted to a string implicitly, so '' + sym throws a
TypeError. String(sym) and sym.description
work.
Well-known symbols
| Symbol | Used by |
|---|---|
Symbol.iterator | for…of, spread, destructuring: Iterators |
Symbol.asyncIterator | for await…of |
Symbol.toPrimitive | ToPrimitive |
Symbol.toStringTag | Object.prototype.toString |
Symbol.hasInstance | instanceof |
Symbol.species | The constructor used for derived objects, by methods such as map |
Symbol.isConcatSpreadable | Array.prototype.concat |
Symbol.unscopables | Names hidden from with |
Symbol.match, matchAll, replace, search, split | The string methods of the same names |
Symbol.dispose, Symbol.asyncDispose | using and await using (current draft) |
Object
An Object is a collection of properties, keyed by strings or symbols, with a link to
a prototype. Functions, arrays, dates and regular expressions are all objects. An
object’s behaviour is defined by its internal methods, and an
exotic object is one where some of those differ from the ordinary ones, such
as an array’s length. See
Objects and prototypes.
The typeof operator
| Operand | Result |
|---|---|
| Undefined | "undefined" |
| An unresolvable name | "undefined" (no error) |
| Null | "object" |
| Boolean | "boolean" |
| Number | "number" |
| String | "string" |
| Symbol | "symbol" |
| BigInt | "bigint" |
| An object that is callable | "function" |
| Any other object | "object" |
typeof null being "object" is a mistake kept for
compatibility, so test with x === null. typeof on a name in
its temporal dead zone throws a
ReferenceError. The Annex B object document.all reports
"undefined".
Type conversion
Conversions are defined as abstract operations (ECMA-262, Type Conversion). They are not callable from a program, but they describe what operators and built-ins do.
ToPrimitive
ToPrimitive(input, preferredType) returns non-objects as they are. For
an object, it asks the object for a primitive, using a hint of
"string", "number" or "default":
- If the object has a
Symbol.toPrimitivemethod, it is called with the hint. The result is returned if it is not an object; otherwise aTypeErroris thrown. - Otherwise OrdinaryToPrimitive runs, with
"default"treated as"number". For hint"string"it triestoStringthenvalueOf; for hint"number"it triesvalueOfthentoString. The first that returns a non-object wins, and if neither does, aTypeErroris thrown.
const money = {
[Symbol.toPrimitive](hint) {
return hint === 'string' ? '£5.00' : 5;
},
};
`${money}`; // "£5.00" (hint "string")
money * 2; // 10 (hint "number")
money + 1; // 6 (hint "default")
Date has its own Symbol.toPrimitive that treats "default" as "string", so a date added to a string reads as textToBoolean
These values are falsy, and everything else is truthy, including
"0", [] and {}:
| Type | Falsy values |
|---|---|
| Undefined | undefined |
| Null | null |
| Boolean | false |
| Number | 0, -0, NaN |
| BigInt | 0n |
| String | "" |
| Object | Only document.all, by Annex B |
ToNumber and ToNumeric
| Input | ToNumber result |
|---|---|
| Undefined | NaN |
| Null | 0 |
true, false | 1, 0 |
| Number | Unchanged |
| String | Parsed as below |
| Symbol, BigInt | TypeError |
| Object | ToPrimitive with hint "number", then ToNumber |
A string is trimmed of white space and line terminators. An empty result gives
0. Otherwise the text must be a decimal literal, optionally signed, or
0x, 0o or 0b digits, or
Infinity with an optional sign; anything else gives
NaN. Numeric separators and the n suffix are not accepted,
nor is a sign before the prefixed forms, so Number('-0x10') is
NaN. parseInt and parseFloat follow looser
rules and ignore trailing text.
ToNumeric is ToNumber except that a BigInt stays a BigInt. It is what
arithmetic operators use.
ToString
| Input | Result |
|---|---|
undefined, null | "undefined", "null" |
true, false | "true", "false" |
| Number | Shortest digits that round-trip. NaN, Infinity; -0 gives "0"; exponent form from 1e21 and below 1e-6 |
| BigInt | The decimal digits, no n |
| Symbol | TypeError |
| Object | ToPrimitive with hint "string", then ToString |
Other conversions
| Operation | Result | Used by |
|---|---|---|
ToObject | Wrapper object for a primitive; the same object for an object; TypeError for undefined and null | Property access, Object() |
ToPropertyKey | A symbol, or else a string | Computed access, obj[key] |
ToIntegerOrInfinity | Truncated integer; NaN and ±0 give 0 | Index arguments |
ToLength | Clamped to 0 through 253 − 1 | Array-like length |
ToIndex | An integer from 0 to 253 − 1, or RangeError | Typed arrays, DataView |
ToBigInt | A BigInt from a BigInt, boolean or numeric string; TypeError for a Number, undefined, null or Symbol | BigInt.asIntN |
Integer conversions
ToInt32, ToUint32, ToInt16,
ToUint16, ToInt8, ToUint8 and
ToUint8Clamp reduce a number to a fixed-size integer. NaN and ±Infinity
become 0, and the value is truncated. Only ToUint8Clamp clamps
and rounds, rounding halves to even; the others wrap modulo the size.
2 ** 32 | 0; // 0 (ToInt32 wraps)
-1 >>> 0; // 4294967295 (ToUint32)
~~3.7; // 3 (truncation through ToInt32)
Equality
There are four equality algorithms in the specification (Testing and Comparison Operations):
| Algorithm | Used by | NaN and NaN | +0 and -0 | Converts types |
|---|---|---|---|---|
IsStrictlyEqual | ===, !==, switch, indexOf | Different | Same | No |
IsLooselyEqual | ==, != | Different | Same | Yes |
SameValue | Object.is | Same | Different | No |
SameValueZero | Map, Set, includes | Same | Same | No |
Strict equality
Values of different types are never equal. For two Numbers, the comparison is numeric, with the NaN and zero rules above. For two BigInts it is mathematical, two Strings compare code unit by code unit, and two Objects are equal only if they are the same object.
Loose equality
x == y works through these steps (IsLooselyEqual):
- If the types are the same, return the strict result.
- If one is
nulland the otherundefined, returntrue. - (Annex B) If one is an object with
[[IsHTMLDDA]]and the other isnullorundefined, returntrue. - If one is a Number and the other a String, convert the string with ToNumber and compare.
- If one is a BigInt and the other a String, convert the string to a BigInt; if
that fails return
false, otherwise compare. - If one is a Boolean, convert it with ToNumber and compare again.
- If one is an Object and the other a String, Number, BigInt or Symbol, convert the object with ToPrimitive and compare again.
- If one is a BigInt and the other a Number, compare them mathematically; NaN and the infinities are never equal to a BigInt.
- Otherwise return
false.
0 == ''; // true (ToNumber("") is 0)
'' == '0'; // false (both strings)
null == 0; // false (null equals only undefined)
[1] == 1; // true (ToPrimitive gives "1")
[] == false; // true (both reach 0)
NaN == NaN; // false
Relational comparison
<, >, <= and >= use
IsLessThan. Each operand goes through ToPrimitive with hint
"number". If both are strings, they compare by code unit. Otherwise they
compare numerically, with a BigInt and a string compared by converting the string;
NaN, or a string that cannot convert, makes the comparison false.
>= is therefore not simply the opposite of <.