Lexical grammar

Lexical grammar
This page describes JavaScript's lexical grammar. JavaScript source text is just a sequence of characters — in order for the interpreter to understand it, the string has to be parsed to a more structured representation. The initial step of parsing is called lexical analysis, in which the text gets scanned from left to right and is converted into a sequence of individual, atomic input elements. Some input elements are insignificant to the interpreter, and will be stripped after this step — they include white space and comments. The others, including identifiers, keywords, literals, and punctuators (mostly operators), will be used for further syntax analysis. Line terminators and multiline comments are also syntactically insignificant, but they guide the process for automatic semicolons insertion to make certain invalid token sequences become valid.
Format-control characters
Format-control characters have no visual representation but are used to control the interpretation of the text.
| Code point | Name | Abbreviation | Description |
|---|---|---|---|
| U+200C | Zero width non-joiner | <ZWNJ> | Placed between characters to prevent being connected into ligatures in certain languages (Wikipedia). |
| U+200D | Zero width joiner | <ZWJ> | Placed between characters that would not normally be connected in order to cause the characters to be rendered using their connected form in certain languages (Wikipedia). |
| U+FEFF | Byte order mark | <BOM> | Used at the start of the script to mark it as Unicode and to allow detection of the text's encoding and byte order (Wikipedia). |
In JavaScript source text, <ZWNJ> and <ZWJ> are treated as identifier parts, while <BOM> (also called a zero-width no-break space <ZWNBSP> when not at the start of text) is treated as white space.
White space
White space characters improve the readability of source text and separate tokens from each other. These characters are usually unnecessary for the functionality of the code. Minification tools are often used to remove whitespace in order to reduce the amount of data that needs to be transferred.
| Code point | Name | Abbreviation | Description | Escape sequence |
|---|---|---|---|---|
| U+0009 | Character tabulation | <TAB> | Horizontal tabulation | \t |
| U+000B | Line tabulation | <VT> | Vertical tabulation | \v |
| U+000C | Form feed | <FF> | Page breaking control character (Wikipedia). | \f |
| U+0020 | Space | <SP> | Normal space | |
| U+00A0 | No-break space | <NBSP> | Normal space, but no point at which a line may break | |
| U+FEFF | Zero-width no-break space | <ZWNBSP> | When not at the start of a script, the BOM marker is a normal whitespace character. | |
| Others | Other Unicode space characters | <USP> | Characters in the "Space_Separator" general category |
Note: Of those characters with the "White_Space" property but are not in the "Space_Separator" general category, U+0009, U+000B, and U+000C are still treated as white space in JavaScript; U+0085 NEXT LINE has no special role; others become the set of line terminators.
Note:
Changes to the Unicode standard used by the JavaScript engine may affect programs' behavior. For example, ES2016 upgraded the reference Unicode standard from 5.1 to 8.0.0, which caused U+180E MONGOLIAN VOWEL SEPARATOR to be moved from the "Space_Separator" category to the "Format (Cf)" category, and made it a non-whitespace. Subsequently, the result of "\u180E".trim().length
changed from 0
to 1
.
Line terminators
In addition to white space characters, line terminator characters are used to improve the readability of the source text. However, in some cases, line terminators can influence the execution of JavaScript code as there are a few places where they are forbidden. Line terminators also affect the process of automatic semicolon insertion.
Outside the context of lexical grammar, white space and line terminators are often conflated. For example, String.prototype.trim()
removes all white space and line terminators from the beginning and end of a string. The \s
character class escape in regular expressions matches all white space and line terminators.
Only the following Unicode code points are treated as line terminators in ECMAScript, other line breaking characters are treated as white space (for example, Next Line, NEL, U+0085 is considered as white space).
Comments
Comments are used to add hints, notes, suggestions, or warnings to JavaScript code. This can make it easier to read and understand. They can also be used to disable code to prevent it from being executed; this can be a valuable debugging tool.
JavaScript has two long-standing ways to add comments to code: line comments and block comments. In addition, there's a special hashbang comment syntax.
Line comments
The first way is the //
comment; this makes all text following it on the same line into a comment. For example:
function comment() {
// This is a one line JavaScript comment
console.log("Hello world!");
}
comment();
Block comments
The second way is the /* */
style, which is much more flexible.
For example, you can use it on a single line:
function comment() {
/* This is a one line JavaScript comment */
console.log("Hello world!");
}
comment();
You can also make multiple-line comments, like this:
function comment() {
/* This comment spans multiple lines. Notice
that we don't need to end the comment until we're done. */
console.log("Hello world!");
}
comment();
You can also use it in the middle of a line, if you wish, although this can make your code harder to read so it should be used with caution:
function comment(x) {
console.log("Hello " + x /* insert the value of x */ + " !");
}
comment("world");
In addition, you can use it to disable code to prevent it from running, by wrapping code in a comment, like this:
function comment() {
/* console.log("Hello world!"); */
}
comment();
In this case, the console.log()
call is never issued, since it's inside a comment. Any number of lines of code can be disabled this way.
Block comments that contain at least one line terminator behave like line terminators in automatic semicolon insertion.
Hashbang comments
There's a special third comment syntax, the hashbang comment. A hashbang comment behaves exactly like a single line-only (//
) comment, except that it begins with #!
and is only valid at the absolute start of a script or module. Note also that no whitespace of any kind is permitted before the #!
. The comment consists of all the characters after #!
up to the end of the first line; only one such comment is permitted.
Hashbang comments in JavaScript resemble shebangs in Unix which provide the path to a specific JavaScript interpreter that you want to use to execute the script. Before the hashbang comment became standardized, it had already been de-facto implemented in non-browser hosts like Node.js, where it was stripped from the source text before being passed to the engine. An example is as follows:
#!/usr/bin/env node
console.log("Hello world");
The JavaScript interpreter will treat it as a normal comment — it only has semantic meaning to the shell if the script is directly run in a shell.
Warning: If you want scripts to be runnable directly in a shell environment, encode them in UTF-8 without a BOM. Although a BOM will not cause any problems for code running in a browser — because it's stripped during UTF-8 decoding, before the source text is analyzed — a Unix/Linux shell will not recognize the hashbang if it's preceded by a BOM character.
You must only use the #!
comment style to specify a JavaScript interpreter. In all other cases just use a //
comment (or multiline comment).
Identifiers
An identifier is used to link a value with a name. Identifiers can be used in various places:
const decl = 1; // Variable declaration (may also be `let` or `var`)
function fn() {} // Function declaration
const obj = { key: "value" }; // Object keys
// Class declaration
class C {
#priv = "value"; // Private field
}
lbl: console.log(1); // Label
In JavaScript, identifiers are commonly made of alphanumeric characters, underscores (_
), and dollar signs ($
). Identifiers are not allowed to start with numbers. However, JavaScript identifiers are not only limited to ASCII — many Unicode code points are allowed as well. Namely:
- Start characters can be any character in the ID_Start category plus
_
and$
. - After the first character, you can use any character in the ID_Continue category plus U+200C (ZWNJ) and U+200D (ZWJ).
Note:
If, for some reason, you need to parse some JavaScript source yourself, do not assume all identifiers follow the pattern /[A-Za-z_$][\w$]*/
(i.e., ASCII-only)! The range of identifiers can be described by the regex /[$_\p{ID_Start}][$\p{ID_Continue}]*/u
(excluding unicode escape sequences).
In addition, JavaScript allows using Unicode escape sequences in the form of \u0000
or \u{000000}
in identifiers, which encode the same string value as the actual Unicode characters. For example, 你好
and \u4f60\u597d
are the same identifiers:
const 你好 = "Hello";
console.log(\u4f60\u597d); // Hello
Not all places accept the full range of identifiers. Certain syntaxes, such as function declarations, function expressions, and variable declarations require using identifiers names that are not reserved words.
function import() {} // Illegal: import is a reserved word.
Most notably, private elements and object properties allow reserved words.
const obj = { import: "value" }; // Legal despite `import` being reserved
class C {
#import = "value";
}
Keywords
Keywords are tokens that look like identifiers but have special meanings in JavaScript. For example, the keyword async
before a function declaration indicates that the function is asynchronous.
Some keywords are reserved, meaning that they cannot be used as an identifier for variable declarations, function declarations, etc. They are often called reserved words. A list of these reserved words is provided below. Not all keywords are reserved — for example, async
can be used as an identifier anywhere. Some keywords are only contextually reserved — for example, await
is only reserved within the body of an async function, and let
is only reserved in strict mode code, or const
and let
declarations.
Identifiers are always compared by string value, so escape sequences are interpreted. For example, this is still a syntax error:
const els\u{65} = 1;
// `els\u{65}` encodes the same identifier as `else`
Reserved words
These keywords cannot be used as identifiers for variables, functions, classes, etc. anywhere in JavaScript source.
break
case
catch
class
const
continue
debugger
default
delete
do
else
export
extends
false
finally
for
function
if
import
in
instanceof
new
null
return
super
switch
this
throw
true
try
typeof
var
void
while
with
The following are only reserved when they are found in strict mode code:
let
(also reserved inconst
,let
, and class declarations)static
yield
(also reserved in generator function bodies)
The following are only reserved when they are found in module code or async function bodies:
Future reserved words
The following are reserved as future keywords by the ECMAScript specification. They have no special functionality at present, but they might at some future time, so they cannot be used as identifiers.
These are always reserved:
enum
The following are only reserved when they are found in strict mode code:
implements
interface
package
private
protected
public
Future reserved words in older standards
The following are reserved as future keywords by older ECMAScript specifications (ECMAScript 1 till 3).
abstract
boolean
byte
char
double
final
float
goto
int
long
native
short
synchronized
throws
transient
volatile
Identifiers with special meanings
A few identifiers have a special meaning in some contexts without being reserved words of any kind. They include:
arguments
(not a keyword, but cannot be declared as identifier in strict mode)as
(import * as ns from "mod"
)async
eval
(not a keyword, but cannot be declared as identifier in strict mode)from
(import x from "mod"
)get
of
set
Literals
Note: This section discusses literals that are atomic tokens. Object literals and array literals are expressions that consist of a series of tokens.
Null literal
See also null
for more information.
null
Boolean literal
See also boolean type for more information.
true
false
Numeric literals
The Number and BigInt types use numeric literals.
Decimal
1234567890
42
Decimal literals can start with a zero (0
) followed by another decimal digit, but if all digits after the leading 0
are smaller than 8, the number is interpreted as an octal number. This is considered a legacy syntax, and number literals prefixed with 0
, whether interpreted as octal or decimal, cause a syntax error in strict mode — so, use the 0o
prefix instead.
0888 // 888 parsed as decimal
0777 // parsed as octal, 511 in decimal
Exponential
The decimal exponential literal is specified by the following format: beN
; where b
is a base number (integer or floating), followed by an E
or e
character (which serves as separator or exponent indicator) and N
, which is exponent or power number – a signed integer.
0e-5 // 0
0e+5 // 0
5e1 // 50
175e-2 // 1.75
1e3 // 1000
1e-3 // 0.001
1E3 // 1000
Binary
Binary number syntax uses a leading zero followed by a lowercase or uppercase Latin letter "B" (0b
or 0B
). Any character after the 0b
that is not 0 or 1 will terminate the literal sequence.
0b10000000000000000000000000000000 // 2147483648
0b01111111100000000000000000000000 // 2139095040
0B00000000011111111111111111111111 // 8388607
Octal
Octal number syntax uses a leading zero followed by a lowercase or uppercase Latin letter "O" (0o
or 0O)
. Any character after the 0o
that is outside the range (01234567) will terminate the literal sequence.
0O755 // 493
0o644 // 420
Hexadecimal
Hexadecimal number syntax uses a leading zero followed by a lowercase or uppercase Latin letter "X" (0x
or 0X
). Any character after the 0x
that is outside the range (0123456789ABCDEF) will terminate the literal sequence.
0xFFFFFFFFFFFFF // 4503599627370495
0xabcdef123456 // 188900967593046
0XA // 10
BigInt literal
The BigInt type is a numeric primitive in JavaScript that can represent integers with arbitrary precision. BigInt literals are created by appending n
to the end of an integer.
123456789123456789n // 123456789123456789
0o777777777777n // 68719476735
0x123456789ABCDEFn // 81985529216486895
0b11101001010101010101n // 955733
BigInt literals cannot start with 0
to avoid confusion with legacy octal literals.
0755n; // SyntaxError: invalid BigInt syntax
For octal BigInt
numbers, always use zero followed by the letter "o" (uppercase or lowercase):
0o755n;
For more information about BigInt
, see also JavaScript data structures.
Numeric separators
To improve readability for numeric literals, underscores (_
, U+005F
) can be used as separators:
1_000_000_000_000
1_050.95
0b1010_0001_1000_0101
0o2_2_5_6
0xA0_B0_C0
1_000_000_000_000_000_000_000n
Note these limitations:
// More than one underscore in a row is not allowed
100__000; // SyntaxError
// Not allowed at the end of numeric literals
100_; // SyntaxError
// Can not be used after leading 0
0_1; // SyntaxError
String literals
A string literal is zero or more Unicode code points enclosed in single or double quotes. Unicode code points may also be represented by an escape sequence. All code points may appear literally in a string literal except for these code points:
- U+005C \ (backslash)
- U+000D <CR>
- U+000A <LF>
- The same kind of quote that begins the string literal
Any code points may appear in the form of an escape sequence. String literals evaluate to ECMAScript String values. When generating these String values Unicode code points are UTF-16 encoded.
'foo'
"bar"
The following subsections describe various escape sequences (\
followed by one or more characters) available in string literals. Any escape sequence not listed below becomes an "identity escape" that becomes the code point itself. For example, \z
is the same as z
. There's a deprecated octal escape sequence syntax described in the Deprecated and obsolete features page. Many of these escape sequences are also valid in regular expressions — see Character escape.
Escape sequences
Special characters can be encoded using escape sequences:
| Escape sequence | Unicode code point |
|---|---|
\0 |
null character (U+0000 NULL) |
\' |
single quote (U+0027 APOSTROPHE) |
\" |
double quote (U+0022 QUOTATION MARK) |
\\ |
backslash (U+005C REVERSE SOLIDUS) |
\n |
newline (U+000A LINE FEED; LF) |
\r |
carriage return (U+000D CARRIAGE RETURN; CR) |
\v |
vertical tab (U+000B LINE TABULATION) |
\t |
tab (U+0009 CHARACTER TABULATION) |
\b |
backspace (U+0008 BACKSPACE) |
\f |
form feed (U+000C FORM FEED) |
\ followed by a line terminator |
empty string |
The last escape sequence, \
followed by a line terminator, is useful for splitting a string literal across multiple lines without changing its meaning.
const longString =
"This is a very long string which needs \
to wrap across multiple lines because \
otherwise my code is unreadable.";
Make sure there is no space or any other character after the backslash (except for a line break), otherwise it will not work. If the next line is indented, the extra spaces will also be present in the string's value.
You can also use the +
operator to append multiple strings together, like this:
const longString =
"This is a very long string which needs " +
"to wrap across multiple lines because " +
"otherwise my code is unreadable.";
Both of the above methods result in identical strings.
Hexadecimal escape sequences
Hexadecimal escape sequences consist of \x
followed by exactly two hexadecimal digits representing a code unit or code point in the range 0x0000 to 0x00FF.
"\xA9"; // "©"
Unicode escape sequences
A Unicode escape sequence consists of exactly four hexadecimal digits following \u
. It represents a code unit in the UTF-16 encoding. For code points U+0000 to U+FFFF, the code unit is equal to the code point. Code points U+10000 to U+10FFFF require two escape sequences representing the two code units (a surrogate pair) used to encode the character; the surrogate pair is distinct from the code point.
See also String.fromCharCode()
and String.prototype.charCodeAt()
.
"\u00A9"; // "©" (U+A9)
Unicode code point escapes
A Unicode code point escape consists of \u{
, followed by a code point in hexadecimal base, followed by }
. The value of the hexadecimal digits must be in the range 0 and 0x10FFFF inclusive. Code points in the range U+10000 to U+10FFFF do not need to be represented as a surrogate pair.
See also String.fromCodePoint()
and String.prototype.codePointAt()
.
"\u{2F804}"; // CJK COMPATIBILITY IDEOGRAPH-2F804 (U+2F804)
// the same character represented as a surrogate pair
"\uD87E\uDC04";
Regular expression literals
Regular expression literals are enclosed by two forward slashes (/
). The lexer consumes all characters up to the next unescaped forward slash or the end of the line, unless the forward slash appears within a character class ([]
). Some characters (namely, those that are identifier parts) can appear after the closing slash, denoting flags.
The lexical grammar is very lenient: not all regular expression literals that get identified as one token are valid regular expressions.
See also RegExp
for more information.
/ab+c/g;
/[/]/;
A regular expression literal cannot start with two forward slashes (//
), because that would be a line comment. To specify an empty regular expression, use /(?:)/
.
Template literals
One template literal consists of several tokens: `xxx${
(template head), }xxx${
(template middle), and }xxx`
(template tail) are individual tokens, while any expression may come between them.
See also template literals for more information.
`string text`;
`string text line 1
string text line 2`;
`string text ${expression} string text`;
tag`string text ${expression} string text`;
Automatic semicolon insertion
Some JavaScript statements' syntax definitions require semicolons (;
) at the end. They include:
var
,let
,const
,using
,await using
- Expression statements
do...while
continue
,break
,return
,throw
debugger
- Class field declarations (public or private)
import
,export
However, to make the language more approachable and convenient, JavaScript is able to automatically insert semicolons when consuming the token stream, so that some invalid token sequences can be "fixed" to valid syntax. This step happens after the program text has been parsed to tokens according to the lexical grammar. There are three cases when semicolons are automatically inserted:
1. When a token not allowed by the grammar is encountered, and it's separated from the previous token by at least one line terminator (including a block comment that includes at least one line terminator), or the token is "}", then a semicolon is inserted before the token.
{ 1
2 } 3
// is transformed by ASI into:
{ 1
;2 ;} 3;
// Which is valid grammar encoding three statements,
// each consisting of a number literal
The ending ")" of do...while
is taken care of as a special case by this rule as well.
do {
// …
} while (condition) /* ; */ // ASI here
const a = 1
However, semicolons are not inserted if the semicolon would then become the separator in the for
statement's head.
for (
let a = 1 // No ASI here
a < 10 // No ASI here
a++
) {}
Semicolons are also never inserted as empty statements. For example, in the code below, if a semicolon is inserted after ")", then the code would be valid, with an empty statement as the if
body and the const
declaration being a separate statement. However, because automatically inserted semicolons cannot become empty statements, this causes a declaration to become the body of the if
statement, which is not valid.
if (Math.random() > 0.5)
const x = 1 // SyntaxError: Unexpected token 'const'
2. When the end of the input stream of tokens is reached, and the parser is unable to parse the single input stream as a complete program, a semicolon is inserted at the end.
const a = 1 /* ; */ // ASI here
This rule is a complement to the previous rule, specifically for the case where there's no "offending token" but the end of input stream.
3. When the grammar forbids line terminators in some place but a line terminator is found, a semicolon is inserted. These places include:
expr <here> ++
,expr <here> --
continue <here> lbl
break <here> lbl
return <here> expr
throw <here> expr
yield <here> expr
yield <here> * expr
(param) <here> => {}
async <here> function
,async <here> prop()
,async <here> function*
,async <here> *prop()
,async <here> (param) <here> => {}
using <here> id
,await <here> using <here> id
Here ++
is not treated as a postfix operator applying to variable b
, because a line terminator occurs between b
and ++
.
a = b
++c
// is transformed by ASI into
a = b;
++c;
Here, the return
statement returns undefined
, and the a + b
becomes an unreachable statement.
return
a + b
// is transformed by ASI into
return;
a + b;
Note that ASI would only be triggered if a line break separates tokens that would otherwise produce invalid syntax. If the next token can be parsed as part of a valid structure, semicolons would not be inserted. For example:
const a = 1
(1).toString()
const b = 1
[1, 2, 3].forEach(console.log)
Because ()
can be seen as a function call, it would usually not trigger ASI. Similarly, []
may be a member access. The code above is equivalent to:
const a = 1(1).toString();
const b = 1[1, 2, 3].forEach(console.log);
This happens to be valid syntax. 1[1, 2, 3]
is a property accessor with a comma-joined expression. Therefore, you would get errors like "1 is not a function" and "Cannot read properties of undefined (reading 'forEach')" when running the code.
Within classes, class fields and generator methods can be a pitfall as well.
class A {
a = 1
*gen() {}
}
It is seen as:
class A {
a = 1 * gen() {}
}
And therefore will be a syntax error around {
.
There are the following rules-of-thumb for dealing with ASI, if you want to enforce semicolon-less style:
-
Write postfix
++
and--
on the same line as their operands.jsconst a = b ++ console.log(a) // ReferenceError: Invalid left-hand side expression in prefix operation
jsconst a = b++ console.log(a)
-
The expressions after
return
,throw
, oryield
should be on the same line as the keyword.jsfunction foo() { return 1 + 1 // Returns undefined; 1 + 1 is ignored }
jsfunction foo() { return 1 + 1 } function foo() { return ( 1 + 1 ) }
-
Similarly, the label identifier after
break
orcontinue
should be on the same line as the keyword.jsouterBlock: { innerBlock: { break outerBlock // SyntaxError: Illegal break statement } }
jsouterBlock: { innerBlock: { break outerBlock } }
-
The
=>
of an arrow function should be on the same line as the end of its parameters.jsconst foo = (a, b) => a + b
jsconst foo = (a, b) => a + b
-
The
async
of async functions, methods, etc. cannot be directly followed by a line terminator.jsasync function foo() {}
jsasync function foo() {}
-
The
using
keyword inusing
andawait using
statements should be on the same line as the first identifier it declares.jsusing resource = acquireResource()
jsusing resource = acquireResource()
-
If a line starts with one of
(
,[
,`
,+
,-
,/
(as in regex literals), prefix it with a semicolon, or end the previous line with a semicolon.js// The () may be merged with the previous line as a function call (() => { // … })() // The [ may be merged with the previous line as a property access [1, 2, 3].forEach(console.log) // The ` may be merged with the previous line as a tagged template literal `string text ${data}`.match(pattern).forEach(console.log) // The + may be merged with the previous line as a binary + expression +a.toString() // The - may be merged with the previous line as a binary - expression -a.toString() // The / may be merged with the previous line as a division expression /pattern/.exec(str).forEach(console.log)
js;(() => { // … })() ;[1, 2, 3].forEach(console.log) ;`string text ${data}`.match(pattern).forEach(console.log) ;+a.toString() ;-a.toString() ;/pattern/.exec(str).forEach(console.log)
-
Class fields should preferably always be ended with semicolons — in addition to the previous rule (which includes a field declaration followed by a computed property, since the latter starts with
[
), semicolons are also required between a field declaration and a generator method.jsclass A { a = 1 [b] = 2 *gen() {} // Seen as a = 1[b] = 2 * gen() {} }
jsclass A { a = 1; [b] = 2; *gen() {} }
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification |
Browser compatibility
See also
- Grammar and types guide
- Micro-feature from ES6, now in Firefox Aurora and Nightly: binary and octal numbers by Jeff Walden (2013)
- JavaScript character escape sequences by Mathias Bynens (2011)


JavaScript execution model

JavaScript execution model
This page introduces the basic infrastructure of the JavaScript runtime environment. The model is largely theoretical and abstract, without any platform-specific or implementation-specific details. Modern JavaScript engines heavily optimize the described semantics.
This page is a reference. It assumes you are already familiar with the execution model of other programming languages, such as C and Java. It makes heavy references to existing concepts in operating systems and programming languages.
The engine and the host
JavaScript execution requires the cooperation of two pieces of software: the JavaScript engine and the host environment.
The JavaScript engine implements the ECMAScript (JavaScript) language, providing the core functionality. It takes source code, parses it, and executes it. However, in order to interact with the outside world, such as to produce any meaningful output, to interface with external resources, or to implement security- or performance-related mechanisms, we need additional environment-specific mechanisms provided by the host environment. For example, the HTML DOM is the host environment when JavaScript is executed in a web browser. Node.js is another host environment that allows JavaScript to be run on the server side.
While we focus primarily on the mechanisms defined in ECMAScript in this reference, we will occasionally talk about mechanisms defined in the HTML spec, which is often mimicked by other host environments like Node.js or Deno. This way, we can give a coherent picture of the JavaScript execution model as used on the web and beyond.
Agent execution model
In the JavaScript specification, each autonomous executor of JavaScript is called an agent, which maintains its facilities for code execution:
- Heap (of objects): this is just a name to denote a large (mostly unstructured) region of memory. It gets populated as objects get created in the program. Note that in the case of shared memory, each agent has its own heap with its own version of a
SharedArrayBuffer
object, but the underlying memory represented by the buffer is shared. - Queue (of jobs): this is known in HTML (and also commonly) as the event loop which enables asynchronous programming in JavaScript while being single-threaded. It's called a queue because it's generally first-in-first-out: earlier jobs are executed before later ones.
- Stack (of execution contexts): this is what's known as a call stack and allows transferring control flow by entering and exiting execution contexts like functions. It's called a stack because it's last-in-first-out. Every job enters by pushing a new frame onto the (empty) stack, and exits by emptying the stack.
These are three distinct data structures that keep track of different data. We will introduce the queue and the stack in more detail in the following sections. To read more about how heap memory is allocated and freed, see memory management.
Each agent is analogous to a thread (note that the underlying implementation may or may not be an actual operating system thread). Each agent can own multiple realms (which 1-to-1 correlate with global objects) that can synchronously access each other, and thus needs to run in a single execution thread. An agent also has a single memory model, indicating whether it's little-endian, whether it can be synchronously blocked, whether atomic operations are lock-free, etc.
An agent on the web can be one of the following:
- A Similar-origin window agent, which contains various
Window
objects which can potentially reach each other, either directly or by usingdocument.domain
. If the window is origin-keyed, then only same-origin windows can reach each other. - A Dedicated worker agent containing a single
DedicatedWorkerGlobalScope
. - A Shared worker agent containing a single
SharedWorkerGlobalScope
. - A Service worker agent containing a single
ServiceWorkerGlobalScope
. - A Worklet agent containing a single
WorkletGlobalScope
.
In other words, each worker creates its own agent, while one or more windows may be within the same agent—usually a main document and its similar-origin iframes. In Node.js, a similar concept called worker threads is available.
The diagram below illustrates the execution model of agents:
Realms
Each agent owns one or more realms. Each piece of JavaScript code is associated with a realm when it's loaded, which remains the same even when called from another realm. A realm consists of the following information:
- A list of intrinsic objects like
Array
,Array.prototype
, etc. - Globally declared variables, the value of
globalThis
, and the global object - A cache of template literal arrays, because evaluation of the same tagged template literal expression always causes the tag to receive the same array object
On the web, the realm and the global object are 1-to-1 corresponded. The global object is either a Window
, a WorkerGlobalScope
, or a WorkletGlobalScope
. So for example, every iframe
executes in a different realm, though it may be in the same agent as the parent window.
Realms are usually mentioned when talking about the identities of global objects. For example, we need methods such as Array.isArray()
or Error.isError()
, because an array constructed in another realm will have a different prototype object than the Array.prototype
object in the current realm, so instanceof Array
will wrongly return false
.
Stack and execution contexts
We first consider synchronous code execution. Each job enters by calling its associated callback. Code inside this callback may create variables, call functions, or exit. Each function needs to keep track of its own variable environments and where to return to. To handle this, the agent needs a stack to keep track of the execution contexts. An execution context, also known generally as a stack frame, is the smallest unit of execution. It tracks the following information:
- Code evaluation state
- The module or script, the function (if applicable), and the currently executing generator that contains this code
- The current realm
- Bindings, including:
- Variables defined with
var
,let
,const
,function
,class
, etc. - Private identifiers like
#foo
which are only valid in the current context this
reference
- Variables defined with
Imagine a program consisting of a single job defined by the following code:
function foo(b) {
const a = 10;
return a + b + 11;
}
function bar(x) {
const y = 3;
return foo(x * y);
}
const baz = bar(7); // assigns 42 to baz
- When the job starts, the first frame is created, where the variables
foo
,bar
, andbaz
are defined. It callsbar
with the argument7
. - A second frame is created for the
bar
call, containing bindings for the parameterx
and the local variabley
. It first performs the multiplicationx * y
, then callsfoo
with the result. - A third frame is created for the
foo
call, containing bindings for the parameterb
and the local variablea
. It first performs the additiona + b + 11
, then returns the result. - When
foo
returns, the top frame element is popped out of the stack, and the call expressionfoo(x * y)
resolves to the return value. It then continues execution, which is just to return this result. - When
bar
returns, the top frame element is popped out of the stack, and the call expressionbar(7)
resolves to the return value. This initializesbaz
with the return value. - We reach the end of the job's source code, so the stack frame for the entrypoint is popped out of the stack. The stack is empty, so the job is considered completed.
Generators and reentry
When a frame is popped, it's not necessarily gone forever, because sometimes we need to come back to it. For example, consider a generator function:
function* gen() {
console.log(1);
yield;
console.log(2);
}
const g = gen();
g.next(); // logs 1
g.next(); // logs 2
In this case, calling gen()
first creates an execution context which is suspended—no code inside gen
gets executed yet. The generator g
saves this execution context internally. The current running execution context remains to be the entrypoint. When g.next()
is called, the execution context for gen
is pushed onto the stack, and the code inside gen
is executed until the yield
expression. Then, the generator execution context gets suspended and removed from the stack, which returns control back to the entrypoint. When g.next()
is called again, the generator execution context is pushed back onto the stack, and the code inside gen
resumes from where it left off.
Tail calls
One mechanism defined in the specification is proper tail call (PTC). A function call is a tail call if the caller does nothing after the call except return the value:
function f() {
return g();
}
In this case, the call to g
is a tail call. If a function call is in tail position, the engine is required to discard the current execution context and replace it with the context of the tail call, instead of pushing a new frame for the g()
call. This means that tail recursion is not subject to the stack size limits:
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
In reality, discarding the current frame causes debugging problems, because if g()
throws an error, f
is no longer on the stack and won't appear in the stack trace. Currently, only Safari (JavaScriptCore) implements PTC, and they have invented some specific infrastructure to address the debuggability issue.
Closures
Another interesting phenomenon related to variable scoping and function calls is closures. Whenever a function is created, it also memorizes internally the variable bindings of the current running execution context. Then, these variable bindings can outlive the execution context.
let f;
{
let x = 10;
f = () => x;
}
console.log(f()); // logs 10
Job queue and event loop
An agent is a thread, which means the interpreter can only process one statement at a time. When the code is all synchronous, this is fine because we can always make progress. But if the code needs to perform asynchronous action, then we cannot progress unless that action is completed. However, it would be detrimental to user experience if that halts the whole program—the nature of JavaScript as a web scripting language requires it to be never blocking. Therefore, the code that handles the completion of that asynchronous action is defined as a callback. This callback defines a job, which gets placed into a job queue—or, in HTML terminology, an event loop—once the action is completed.
Every time, the agent pulls a job from the queue and executes it. When the job is executed, it may create more jobs, which are added to the end of the queue. Jobs can also be added via the completion of asynchronous platform mechanisms, such as timers, I/O, and events. A job is considered completed when the stack is empty; then, the next job is pulled from the queue. Jobs might not be pulled with uniform priority—for example, HTML event loops split jobs into two categories: tasks and microtasks. Microtasks have higher priority and the microtask queue is drained first before the task queue is pulled. For more information, check the HTML microtask guide. If the job queue is empty, the agent waits for more jobs to be added.
"Run-to-completion"
Each job is processed completely before any other job is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be preempted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.
For example, consider this example:
const promise = Promise.resolve();
let i = 0;
promise.then(() => {
i += 1;
console.log(i);
});
promise.then(() => {
i += 1;
console.log(i);
});
In this example, we create an already-resolved promise, which means any callback attached to it will be immediately scheduled as jobs. The two callbacks seem to cause a race condition, but actually, the output is fully predictable: 1
and 2
will be logged in order. This is because each job runs to completion before the next one is executed, so the overall order is always i += 1; console.log(i); i += 1; console.log(i);
and never i += 1; i += 1; console.log(i); console.log(i);
.
A downside of this model is that if a job takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make job processing short and, if possible, cut down one job into several jobs.
Never blocking
Another important guarantee offered by the event loop model is that JavaScript execution is never blocking. Handling I/O is typically performed via events and callbacks, so when the application is waiting for an IndexedDB query to return or a fetch()
request to return, it can still process other things like user input. The code that executes after the completion of an asynchronous action is always provided as a callback function (for example, the promise then()
handler, the callback function in setTimeout()
, or the event handler), which defines a job to be added to the job queue once the action completes.
Of course, the guarantee of "never-blocking" requires the platform API to be inherently asynchronous, but some legacy exceptions exist like alert()
or synchronous XHR. It is considered good practice to avoid them to ensure the responsiveness of the application.
Agent clusters and memory sharing
Multiple agents can communicate via memory sharing, forming an agent cluster. Agents are within the same cluster if and only if they can share memory. There is no built-in mechanism for two agent clusters to exchange any information, so they can be regarded as completely isolated execution models.
When creating an agent (such as by spawning a worker), there are some criteria for whether it's in the same cluster as the current agent, or a new cluster is created. For example, the following pairs of global objects are each within the same agent cluster, and thus can share memory with each other:
- A
Window
object and a dedicated worker that it created. - A worker (of any type) and a dedicated worker it created.
- A
Window
object A and theWindow
object of a same-originiframe
element that A created. - A
Window
object and a same-originWindow
object that opened it. - A
Window
object and a worklet that it created.
The following pairs of global objects are not within the same agent cluster, and thus cannot share memory:
- A
Window
object and a shared worker it created. - A worker (of any type) and a shared worker it created.
- A
Window
object and a service worker it created. - A
Window
object A and theWindow
object of aniframe
element that A created that cannot be same origin-domain with A. - Any two
Window
objects with no opener or ancestor relationship. This holds even if the twoWindow
objects are same origin.
For the exact algorithm, check the HTML specification.
Cross-agent communication and memory model
As aforementioned, agents communicate via memory sharing. On the web, memory is shared via the postMessage()
method. The using web workers guide provides an overview of this. Typically, data is passed by value only (via structured cloning), and therefore does not involve any concurrency complications. To share memory, one must post a SharedArrayBuffer
object, which can be simultaneously accessed by multiple agents. Once two agents share access to the same memory via a SharedArrayBuffer
, they can synchronize executions via the Atomics
object.
There are two ways to access shared memory: via normal memory access (which is not atomic) and via atomic memory access. The latter is sequentially consistent (which means there is a strict total ordering of events agreed upon by all agents in the cluster), while the former is unordered (which means no ordering exists); JavaScript does not provide operations with other ordering guarantees.
The spec provides the following guidelines for programmers working with shared memory:
We recommend programs be kept data race free, i.e., make it so that it is impossible for there to be concurrent non-atomic operations on the same memory location. Data race free programs have interleaving semantics where each step in the evaluation semantics of each agent are interleaved with each other. For data race free programs, it is not necessary to understand the details of the memory model. The details are unlikely to build intuition that will help one to better write ECMAScript.
More generally, even if a program is not data race free it may have predictable behavior, so long as atomic operations are not involved in any data races and the operations that race all have the same access size. The simplest way to arrange for atomics not to be involved in races is to ensure that different memory cells are used by atomic and non-atomic operations and that atomic accesses of different sizes are not used to access the same cells at the same time. Effectively, the program should treat shared memory as strongly typed as much as possible. One still cannot depend on the ordering and timing of non-atomic accesses that race, but if memory is treated as strongly typed the racing accesses will not "tear" (bits of their values will not be mixed).
Concurrency and ensuring forward progress
When multiple agents cooperate, the never-blocking guarantee does not always hold. An agent can become blocked, or paused, while waiting for another agent to perform some action. This is different from waiting on a promise in the same agent, because it halts the entire agent and does not allow any other code to run in the meantime—in other words, it cannot make forward progress.
To prevent deadlocks, there are some strong restrictions on when and which agents can become blocked.
- Every unblocked agent with a dedicated executing thread eventually makes forward progress.
- In a set of agents that share an executing thread, one agent eventually makes forward progress.
- An agent does not cause another agent to become blocked except via explicit APIs that provide blocking.
- Only certain agents can be blocked. On the web, this includes dedicated workers and shared workers, but not similar-origin windows or service workers.
The agent cluster ensures some level of integrity over the activeness of its agents, in the case of external pauses or terminations:
- An agent may be paused or resumed without its knowledge or cooperation. For example, navigating away from a window may suspend code execution but preserve its state. However, an agent cluster is not allowed to be partially deactivated, to avoid an agent starving because another agent has been deactivated. For example, shared workers are never in the same agent cluster as the creator window or other dedicated workers. This is because a shared worker's lifetime is independent of documents: if a document is deactivated while its dedicated worker holds a lock, the shared worker is blocked from acquiring the lock until the dedicated worker is reactivated, if ever. Meanwhile other workers trying to access the shared worker from other windows will starve.
- Similarly, an agent may be terminated by factors external to the cluster. For example, operating systems or users killing a browser process, or the browser force-terminating one agent because it's using too many resources. In this case, all the agents in the cluster get terminated. (The spec also allows a second strategy, which is an API that allows at least one remaining member of the cluster to identify the termination and the agent that was terminated, but this is not implemented on the web.)
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification |
| ECMAScript® 2026 Language Specification |
| HTML |
See also
- Event loops in the HTML standard
- What is the Event Loop? in the Node.js docs


Warning: unreachable code after return statement
The JavaScript warning "unreachable code after return statement" occurs when using an
expression after a return
statement, or when using a
semicolon-less return statement but including an expression directly after.
Message
Warning: unreachable code after return statement (Firefox)
Error type
Warning
What went wrong?
Unreachable code after a return statement might occur in these situations:
- When using an expression after a
return
statement, or - when using a semicolon-less return statement but including an expression directly after.
When an expression exists after a valid return
statement, a warning is
given to indicate that the code after the return
statement is unreachable,
meaning it can never be run.
Why should I have semicolons after return
statements? In the case of
semicolon-less return
statements, it can be unclear whether the developer
intended to return the statement on the following line, or to stop execution and return.
The warning indicates that there is ambiguity in the way the return
statement is written.
Warnings will not be shown for semicolon-less returns if these statements follow it:
Examples
Invalid cases
function f() {
let x = 3;
x += 4;
return x; // return exits the function immediately
x -= 3; // so this line will never run; it is unreachable
}
function g() {
return // this is treated like `return;`
3 + 4; // so the function returns, and this line is never reached
}
Valid cases
function f() {
let x = 3;
x += 4;
x -= 3;
return x; // OK: return after all other statements
}
function g() {
return 3 + 4 // OK: semicolon-less return with expression on the same line
}


Warning: -file- is being assigned a //# sourceMappingURL, but already has one
The JavaScript warning "-file- is being assigned a //# sourceMappingURL, but already has one." occurs when a source map has been specified more than once for a given JavaScript source.
Message
Warning: -file- is being assigned a //# sourceMappingURL, but already has one.
Error type
A warning. JavaScript execution won't be halted.
What went wrong?
A source map has been specified more than once for a given JavaScript source.
JavaScript sources are often combined and minified to make delivering them from the server more efficient. With source maps, the debugger can map the code being executed to the original source files. There are two ways to assign a source map, either by using a comment or by setting a header to the JavaScript file.
Examples
Setting source maps
Setting a source map by using a comment in the file:
js
//# sourceMappingURL=http://example.com/path/to/your/sourcemap.map
Or, alternatively, you can set a header to your JavaScript file:
http
X-SourceMap: /path/to/file.js.map
See also
- Use a source map in the Firefox source docs
- Introduction to JavaScript source maps on developer.chrome.com (2012)


URIError: malformed URI sequence

URIError: malformed URI sequence
The JavaScript exception "malformed URI sequence" occurs when URI encoding or decoding wasn't successful.
Message
URIError: URI malformed (V8-based) URIError: malformed URI sequence (Firefox) URIError: String contained an illegal UTF-16 sequence. (Safari)
Error type
URIError
What went wrong?
URI encoding or decoding wasn't successful. An argument given to either the
decodeURI
, encodeURI
, encodeURIComponent
, or
decodeURIComponent
function was not valid, so that the function was unable
encode or decode properly.
Examples
Encoding
Encoding replaces each instance of certain characters by one, two, three, or four
escape sequences representing the UTF-8 encoding of the character. An
URIError
will be thrown if there is an attempt to encode a surrogate which
is not part of a high-low pair, for example:
encodeURI("\uD800");
// "URIError: malformed URI sequence"
encodeURI("\uDFFF");
// "URIError: malformed URI sequence"
A high-low pair is OK. For example:
encodeURI("\uD800\uDFFF");
// "%F0%90%8F%BF"
Decoding
Decoding replaces each escape sequence in the encoded URI component with the character that it represents. If there isn't such a character, an error will be thrown:
decodeURIComponent("%E0%A4%A");
// "URIError: malformed URI sequence"
With proper input, this should usually look like something like this:
decodeURIComponent("JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
// "JavaScript_шеллы"


TypeError: X.prototype.y called on incompatible type
The JavaScript exception "called on incompatible target (or object)" occurs when a
function (on a given object), is called with a this
not corresponding to
the type expected by the function.
Message
TypeError: Method Set.prototype.add called on incompatible receiver undefined (V8-based) TypeError: Bind must be called on a function (V8-based) TypeError: Illegal invocation (V8-based) TypeError: Function.prototype.toString requires that 'this' be a Function (V8-based) TypeError: this is not a Date object. (V8-based) TypeError: this is not a typed array. (V8-based) TypeError: Function.prototype.toString called on incompatible object (Firefox) TypeError: Function.prototype.bind called on incompatible target (Firefox) TypeError: 'addEventListener' called on an object that does not implement interface EventTarget. (Firefox) TypeError: Type error (Safari) TypeError: undefined is not an object (Safari)
Error type
TypeError
What went wrong?
When this error is thrown, a function (on a given object), is called with a
this
not corresponding to the type expected by the function.
This issue can arise when using the Function.prototype.call()
or
Function.prototype.apply()
methods, and providing a this
argument which does not have the expected type.
This issue can also happen when providing a function that is stored as a property of an
object as an argument to another function. In this case, the object that stores the
function won't be the this
target of that function when it is called by the
other function. To work-around this issue, you will either need to wrap the callback function in another function, or use the Function.prototype.bind()
method to
force the this
argument to the expected object.
Examples
Invalid cases
const mySet = new Set();
["bar", "baz"].forEach(mySet.add);
// mySet.add is a function, but "mySet" is not captured as this.
function myFun() {
console.log(this);
}
["bar", "baz"].forEach(myFun.bind);
// myFun.bind is a function, but "myFun" is not captured as this.
Valid cases
const mySet = new Set();
["bar", "baz"].forEach(mySet.add.bind(mySet));
// This works due to binding "mySet" as this.
function myFun() {
console.log(this);
}
["bar", "baz"].forEach((x) => myFun.bind(x));
// This works using the "bind" function. It creates a new function forwarding the argument.


TypeError: WeakSet key/WeakMap value 'x' must be an object or an unregistered symbol
The JavaScript exception "WeakSet key (or WeakMap value) 'x' must be an object or an unregistered symbol" occurs when a value of invalid type is used as a key in a WeakSet
or as a value in a WeakMap
.
Message
TypeError: Invalid value used as weak map key (V8-based) TypeError: WeakMap key 1 must be an object or an unregistered symbol (Firefox) TypeError: WeakMap keys must be objects or non-registered symbols (Safari) TypeError: Invalid value used in weak set (V8-based) TypeError: WeakSet value 1 must be an object or an unregistered symbol (Firefox) TypeError: WeakSet values must be objects or non-registered symbols (Safari)
Error type
TypeError
What went wrong?
WeakSet
and WeakMap
require the keys to be garbage collectable. Only objects and non-registered symbols (that is, symbols not returned by Symbol.for()
) are valid. For more information, see Memory management. If you want to add keys that are strings, numbers, or other primitive values, you should store them in a regular Set
or Map
instead.
Examples
Invalid cases
js
new WeakSet().add(1); // TypeError
new WeakMap().set(1, {}); // TypeError
new WeakSet([1]); // TypeError
new WeakMap([[1, {}]]); // TypeError
Valid cases
js
new WeakSet().add({}); // OK
new WeakMap().set({}, 1); // OK
new Set([1]); // OK
new Map([[1, {}]]); // OK


TypeError: setting getter-only property "x"
The JavaScript strict mode-only exception "setting getter-only property" occurs when there is an attempt to set a new value to a property for which only a getter is specified, or when setting a private accessor property that similarly only has a getter defined.
Message
TypeError: Cannot set property x of #<Object> which has only a getter (V8-based) TypeError: '#x' was defined without a setter (V8-based) TypeError: setting getter-only property "x" (Firefox) TypeError: Attempted to assign to readonly property. (Safari) TypeError: Trying to access an undefined private setter (Safari)
Error type
TypeError
in strict mode only.
What went wrong?
There is an attempt to set a new value to a property for which only a getter is specified.
While this will be silently ignored in non-strict mode, it will throw a
TypeError
in strict mode. Classes are always in strict mode, so assigning to a getter-only private element always throws this error.
Examples
Property with no setter
The example below shows how to set a getter for a property.
It doesn't specify a setter, so a
TypeError
will be thrown upon trying to set the temperature
property to 30
. For more details see also the
Object.defineProperty()
page.
"use strict";
function Archiver() {
const temperature = null;
Object.defineProperty(this, "temperature", {
get() {
console.log("get!");
return temperature;
},
});
}
const arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 30;
// TypeError: setting getter-only property "temperature"
To fix this error, you will either need to remove the arc.temperature = 30
line, which attempts to
set the temperature property, or you will need to implement a setter for it, for
example like this:
"use strict";
function Archiver() {
let temperature = null;
const archive = [];
Object.defineProperty(this, "temperature", {
get() {
console.log("get!");
return temperature;
},
set(value) {
temperature = value;
archive.push({ val: temperature });
},
});
this.getArchive = function () {
return archive;
};
}
const arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]


TypeError: Reduce of empty array with no initial value
The JavaScript exception "reduce of empty array with no initial value" occurs when a reduce function is used.
Message
TypeError: Reduce of empty array with no initial value (V8-based & Firefox & Safari)
Error type
TypeError
What went wrong?
In JavaScript, there are several reduce functions:
Array.prototype.reduce()
,Array.prototype.reduceRight()
andTypedArray.prototype.reduce()
,TypedArray.prototype.reduceRight()
).
These functions optionally take an initialValue
(which will be used as the
first argument to the first call of the callback
). However, if no initial
value is provided, it will use the first element of the Array
or
TypedArray
as the initial value. This error is raised when an empty array
is provided because no initial value can be returned in that case.
Examples
Invalid cases
This problem appears frequently when combined with a filter
(Array.prototype.filter()
, TypedArray.prototype.filter()
)
which will remove all elements of the list. Thus leaving none to be used as the initial
value.
const ints = [0, -1, -2, -3, -4, -5];
ints
.filter((x) => x > 0) // removes all elements
.reduce((x, y) => x + y); // no more elements to use for the initial value.
Similarly, the same issue can happen if there is a typo in a selector, or an unexpected number of elements in a list:
const names = document.getElementsByClassName("names");
const nameList = Array.prototype.reduce.call(
names,
(acc, name) => `${acc}, ${name}`,
);
Valid cases
These problems can be solved in two different ways.
One way is to actually provide an initialValue
as the neutral element of
the operator, such as 0 for the addition, 1 for a multiplication, or an empty string for
a concatenation.
const ints = [0, -1, -2, -3, -4, -5];
ints
.filter((x) => x > 0) // removes all elements
.reduce((x, y) => x + y, 0); // the initial value is the neutral element of the addition
Another way would be to handle the empty case, either before calling
reduce
, or in the callback after adding an unexpected dummy initial value.
const names = document.getElementsByClassName("names");
let nameList1 = "";
if (names.length >= 1) {
nameList1 = Array.prototype.reduce.call(
names,
(acc, name) => `${acc}, ${name}`,
);
}
// nameList1 === "" when names is empty.
const nameList2 = Array.prototype.reduce.call(
names,
(acc, name) => {
if (acc === "")
// initial value
return name;
return `${acc}, ${name}`;
},
"",
);
// nameList2 === "" when names is empty.


TypeError: property "x" is non-configurable and can't be deleted
The JavaScript exception "property is non-configurable and can't be deleted" occurs when it was attempted to delete a property, but that property is non-configurable.
Message
TypeError: Cannot delete property 'x' of #<Object> (V8-based) TypeError: property "x" is non-configurable and can't be deleted (Firefox) TypeError: Unable to delete property. (Safari)
Error type
TypeError
in strict mode only.
What went wrong?
It was attempted to delete a property, but that property is non-configurable. The
configurable
attribute controls whether the property can be deleted from
the object and whether its attributes (other than writable
) can be changed.
This error happens only in strict mode code. In
non-strict code, the operation returns false
.
Examples
Attempting to delete non-configurable properties
Non-configurable properties are not super common, but they can be created using
Object.defineProperty()
or Object.freeze()
.
"use strict";
const obj = Object.freeze({ name: "Elsa", score: 157 });
delete obj.score; // TypeError
"use strict";
const obj = {};
Object.defineProperty(obj, "foo", { value: 2, configurable: false });
delete obj.foo; // TypeError
"use strict";
const frozenArray = Object.freeze([0, 1, 2]);
frozenArray.pop(); // TypeError
There are also a few non-configurable properties built into JavaScript. Maybe you tried to delete a mathematical constant.
"use strict";
delete Math.PI; // TypeError


TypeError: null/undefined has no properties
The JavaScript exception "null (or undefined) has no properties" occurs when you
attempt to access properties of null
and undefined
. They
don't have any.
Message
TypeError: Cannot read properties of undefined (reading 'x') (V8-based) TypeError: Cannot destructure 'x' as it is undefined. (V8-based) TypeError: Cannot destructure property 'x' of 'y' as it is undefined. (V8-based) TypeError: null has no properties (Firefox) TypeError: undefined has no properties (Firefox) TypeError: undefined is not an object (evaluating 'undefined.x') (Safari) TypeError: Right side of assignment cannot be destructured (Safari)
Error type
What went wrong?
Both null
and undefined
have no properties you could access. Therefore, you cannot use property accessors on them, or destructure them.
Examples
null and undefined have no properties
js
null.foo;
// TypeError: null has no properties
undefined.bar;
// TypeError: undefined has no properties


TypeError: More arguments needed

TypeError: More arguments needed
The JavaScript exception "more arguments needed" occurs when there is an error with how a function is called. More arguments need to be provided.
Message
TypeError: Object prototype may only be an Object or null: undefined (V8-based) TypeError: Object.create requires at least 1 argument, but only 0 were passed (Firefox) TypeError: Object.setPrototypeOf requires at least 2 arguments, but only 0 were passed (Firefox) TypeError: Object.defineProperties requires at least 1 argument, but only 0 were passed (Firefox) TypeError: Object prototype may only be an Object or null. (Safari)
Error type
What went wrong?
There is an error with how a function is called. More arguments need to be provided.
Examples
Required arguments not provided
The Object.create()
method requires at least one argument and the
Object.setPrototypeOf()
method requires at least two arguments:
js
const obj = Object.create();
// TypeError: Object.create requires at least 1 argument, but only 0 were passed
const obj2 = Object.setPrototypeOf({});
// TypeError: Object.setPrototypeOf requires at least 2 arguments, but only 1 were passed
You can fix this by setting null
as the prototype, for example:
js
const obj = Object.create(null);
const obj2 = Object.setPrototypeOf({}, null);
See also
- Functions guide


TypeError: matchAll/replaceAll must be called with a global RegExp
The JavaScript exception "TypeError: matchAll/replaceAll must be called with a global RegExp" occurs when the String.prototype.matchAll()
or String.prototype.replaceAll()
method is used with a RegExp
object that does not have the global
flag set.
Message
TypeError: String.prototype.matchAll called with a non-global RegExp argument (V8-based) TypeError: String.prototype.replaceAll called with a non-global RegExp argument (V8-based) TypeError: matchAll must be called with a global RegExp (Firefox) TypeError: replaceAll must be called with a global RegExp (Firefox) TypeError: String.prototype.matchAll argument must not be a non-global regular expression (Safari) TypeError: String.prototype.replaceAll argument must not be a non-global regular expression (Safari)
Error type
TypeError
What went wrong?
The String.prototype.matchAll()
and String.prototype.replaceAll()
methods require a RegExp
object with the global
flag set. This flag indicates that the regular expression can match all locations of the input string, instead of stopping at the first match. Although the g
flag is redundant when using these methods (because these methods always do a global replacement), they are still required to make the intention clear.
It's worth noting that the g
flag validation is done in the matchAll
and replaceAll
methods. If you use the [Symbol.matchAll]()
method of RegExp
instead, you won't get this error, but there will only be a single match.
Examples
Invalid cases
"abc".matchAll(/./); // TypeError
"abc".replaceAll(/./, "f"); // TypeError
Valid cases
If you intend to do global matching/replacement: either add the g
flag, or construct a new RegExp
object with the g
flag, if you want to keep the original regex unchanged.
[..."abc".matchAll(/./g)]; // [[ "a" ], [ "b" ], [ "c" ]]
"abc".replaceAll(/./g, "f"); // "fff"
const existingPattern = /./;
const newPattern = new RegExp(
existingPattern.source,
`${existingPattern.flags}g`,
);
"abc".replaceAll(newPattern, "f"); // "fff"
If you only intend to do a single matching/replacement: use String.prototype.match()
or String.prototype.replace()
instead. You can also use the [Symbol.matchAll]()
method if you want an iterator like matchAll
returns that only contains one match, but doing so will be very confusing.
"abc".match(/./); // [ "a" ]
"abc".replace(/./, "f"); // "fbc"
[..././[Symbol.matchAll]("abc")]; // [[ "a" ]]


TypeError: Iterator/AsyncIterator constructor can't be used directly
The JavaScript exception "Iterator constructor can't be used directly" or "AsyncIterator constructor can't be used directly" occurs when you try to use the Iterator()
or AsyncIterator()
constructors directly to create instances. These constructors are abstract classes and should only be inherited from.
Message
TypeError: Abstract class Iterator not directly constructable (V8-based) TypeError: Iterator constructor can't be used directly (Firefox) TypeError: Iterator cannot be constructed directly (Safari) TypeError: Abstract class AsyncIterator not directly constructable (V8-based) TypeError: AsyncIterator constructor can't be used directly (Firefox) TypeError: AsyncIterator cannot be constructed directly (Safari)
Error type
TypeError
What went wrong?
The Iterator
and AsyncIterator
constructors are abstract classes and should not be used directly. They check the value of new.target
and throw if it is the same as the constructor itself. The only way to use these constructors is to inherit from them in a subclass and call super()
in the subclass constructor. The subclass must also define a next()
method to be useful.
Examples
Invalid cases
js
new Iterator();
Valid cases
js
class MyIterator extends Iterator {
#step;
#end;
constructor(start, end) {
// Implicitly calls new Iterator(), but with a different `new.target`
super();
this.#step = start;
this.#end = end;
}
next() {
if (this.#step >= this.#end) {
return { done: true };
}
return { value: this.#step++, done: false };
}
}
new MyIterator();


Object.prototype.__proto__

Object.prototype.__proto__
Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.
Warning:
Changing the [[Prototype]]
of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to the time spent in the obj.__proto__ = ...
statement, but may extend to any code that has access to any object whose [[Prototype]]
has been altered. You can read more in JavaScript engine fundamentals: optimizing prototypes.
Note:
The use of __proto__
is controversial and discouraged. Its existence and exact behavior have only been standardized as a legacy feature to ensure web compatibility, while it presents several security issues and footguns. For better support, prefer Object.getPrototypeOf()
/Reflect.getPrototypeOf()
and Object.setPrototypeOf()
/Reflect.setPrototypeOf()
instead.
The __proto__
accessor property of Object
instances exposes the [[Prototype]]
(either an object or null
) of this object.
The __proto__
property can also be used in an object literal definition to set the object [[Prototype]]
on creation, as an alternative to Object.create()
. See: object initializer / literal syntax. That syntax is standard and optimized for in implementations, and quite different from Object.prototype.__proto__
.
Syntax
obj.__proto__
Return value
If used as a getter, returns the object's [[Prototype]]
.
Exceptions
TypeError
-
Thrown if attempting to set the prototype of a non-extensible object or an immutable prototype exotic object, such as
Object.prototype
orwindow
.
Description
The __proto__
getter function exposes the value of the internal [[Prototype]]
of an object. For objects created using an object literal (unless you use the prototype setter syntax), this value is Object.prototype
. For objects created using array literals, this value is Array.prototype
. For functions, this value is Function.prototype
. You can read more about the prototype chain in Inheritance and the prototype chain.
The __proto__
setter allows the [[Prototype]]
of an object to be mutated. The value provided must be an object or null
. Providing any other value will do nothing.
Unlike Object.getPrototypeOf()
and Object.setPrototypeOf()
, which are always available on Object
as static properties and always reflect the [[Prototype]]
internal property, the __proto__
property doesn't always exist as a property on all objects, and as a result doesn't reflect [[Prototype]]
reliably.
The __proto__
property is just an accessor property on Object.prototype
consisting of a getter and setter function. A property access for __proto__
that eventually consults Object.prototype
will find this property, but an access that does not consult Object.prototype
will not. If some other __proto__
property is found before Object.prototype
is consulted, that property will hide the one found on Object.prototype
.
null
-prototype objects don't inherit any property from Object.prototype
, including the __proto__
accessor property, so if you try to read __proto__
on such an object, the value is always undefined
regardless of the object's actual [[Prototype]]
, and any assignment to __proto__
would create a new property called __proto__
instead of setting the object's prototype. Furthermore, __proto__
can be redefined as an own property on any object instance through Object.defineProperty()
without triggering the setter. In this case, __proto__
will no longer be an accessor for [[Prototype]]
. Therefore, always prefer Object.getPrototypeOf()
and Object.setPrototypeOf()
for setting and getting the [[Prototype]]
of an object.
Examples
Using __proto__
function Circle() {}
const shape = {};
const circle = new Circle();
// Set the object prototype.
// DEPRECATED. This is for example purposes only. DO NOT DO THIS in real code.
shape.__proto__ = circle;
// Get the object prototype
console.log(shape.__proto__ === Circle); // false
function ShapeA() {}
const ShapeB = {
a() {
console.log("aaa");
},
};
ShapeA.prototype.__proto__ = ShapeB;
console.log(ShapeA.prototype.__proto__); // { a: [Function: a] }
const shapeA = new ShapeA();
shapeA.a(); // aaa
console.log(ShapeA.prototype === shapeA.__proto__); // true
function ShapeC() {}
const ShapeD = {
a() {
console.log("a");
},
};
const shapeC = new ShapeC();
shapeC.__proto__ = ShapeD;
shapeC.a(); // a
console.log(ShapeC.prototype === shapeC.__proto__); // false
function Test() {}
Test.prototype.myName = function () {
console.log("myName");
};
const test = new Test();
console.log(test.__proto__ === Test.prototype); // true
test.myName(); // myName
const obj = {};
obj.__proto__ = Test.prototype;
obj.myName(); // myName
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-object.prototype.__proto__ |


JSON.parse()

JSON.parse()
Baseline
Widely available
*
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
* Some parts of this feature may have varying levels of support.
The JSON.parse()
static method parses a JSON string, constructing the JavaScript value or object described by the string. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned.
Try it
const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
console.log(obj.count);
// Expected output: 42
console.log(obj.result);
// Expected output: true
Syntax
JSON.parse(text)
JSON.parse(text, reviver)
Parameters
text
-
The string to parse as JSON. See the
JSON
object for a description of JSON syntax. reviver
Optional-
If a function, this prescribes how each value originally produced by parsing is transformed before being returned. Non-callable values are ignored. The function is called with the following arguments:
key
-
The key associated with the value.
value
-
The value produced by parsing.
context
Optional-
A context object that holds state relevant to the current expression being revived. It is a new object for each invocation of the reviver function. It is only passed when reviving primitive values, but not when
value
is an object or array. It contains the following property:source
-
The original JSON string representing this value.
Return value
The Object
, Array
, string, number, boolean, or null
value corresponding to the given JSON text
.
Exceptions
SyntaxError
-
Thrown if the string to parse is not valid JSON.
Description
JSON.parse()
parses a JSON string according to the JSON grammar, then evaluates the string as if it's a JavaScript expression. The only instance where a piece of JSON text represents a different value from the same JavaScript expression is when dealing with the "__proto__"
key — see Object literal syntax vs. JSON.
The reviver parameter
If a reviver
is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value and all its properties (in a depth-first fashion, beginning with the most nested properties and proceeding to the original value itself) are individually run through the reviver
.
The reviver
is called with the object containing the property being processed as this
(unless you define the reviver
as an arrow function, in which case there's no separate this
binding) and two arguments: key
and value
, representing the property name as a string (even for arrays) and the property value. For primitive values, an additional context
parameter is passed, which contains the source text of this value. If the reviver
function returns undefined
(or returns no value — for example, if execution falls off the end of the function), the property is deleted from the object. Otherwise, the property is redefined to be the return value. If the reviver
only transforms some values and not others, be certain to return all untransformed values as-is — otherwise, they will be deleted from the resulting object.
Similar to the replacer
parameter of JSON.stringify()
, for arrays and objects, reviver
will be last called on the root value with an empty string as the key
and the root object as the value
. For other valid JSON values, reviver
works similarly and is called once with an empty string as the key
and the value itself as the value
.
If you return another value from reviver
, that value will completely replace the originally parsed value. This even applies to the root value. For example:
const transformedObj = JSON.parse('[1,5,{"s":1}]', (key, value) =>
typeof value === "object" ? undefined : value,
);
console.log(transformedObj); // undefined
There is no way to work around this generically. You cannot specially handle the case where key
is an empty string, because JSON objects can also contain keys that are empty strings. You need to know very precisely what kind of transformation is needed for each key when implementing the reviver.
Note that reviver
is run after the value is parsed. So, for example, numbers in JSON text will have already been converted to JavaScript numbers, and may lose precision in the process. One way to transfer large numbers without loss of precision is to serialize them as strings, and revive them to BigInts, or other appropriate arbitrary precision formats.
You can also use the context.source
property to access the original JSON source text representing the value, as shown below:
const bigJSON = '{"gross_gdp": 12345678901234567890}';
const bigObj = JSON.parse(bigJSON, (key, value, context) => {
if (key === "gross_gdp") {
// Ignore the value because it has already lost precision
return BigInt(context.source);
}
return value;
});
Examples
Using JSON.parse()
JSON.parse("{}"); // {}
JSON.parse("true"); // true
JSON.parse('"foo"'); // "foo"
JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
JSON.parse("null"); // null
Using the reviver parameter
JSON.parse(
'{"p": 5}',
(key, value) =>
typeof value === "number"
? value * 2 // return value * 2 for numbers
: value, // return everything else unchanged
);
// { p: 10 }
JSON.parse('{"1": 1, "2": 2, "3": {"4": 4, "5": {"6": 6}}}', (key, value) => {
console.log(key);
return value;
});
// 1
// 2
// 4
// 6
// 5
// 3
// ""
Using reviver when paired with the replacer of JSON.stringify()
In order for a value to properly round-trip (that is, it gets deserialized to the same original object), the serialization process must preserve the type information. For example, you can use the replacer
parameter of JSON.stringify()
for this purpose:
// Maps are normally serialized as objects with no properties.
// We can use the replacer to specify the entries to be serialized.
const map = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const jsonText = JSON.stringify(map, (key, value) =>
value instanceof Map ? Array.from(value.entries()) : value,
);
console.log(jsonText);
// [[1,"one"],[2,"two"],[3,"three"]]
const map2 = JSON.parse(jsonText, (key, value) =>
Array.isArray(value) && value.every(Array.isArray) ? new Map(value) : value,
);
console.log(map2);
// Map { 1 => "one", 2 => "two", 3 => "three" }
Because JSON has no syntax space for annotating type metadata, in order to revive values that are not plain objects, you have to consider one of the following:
- Serialize the entire object to a string and prefix it with a type tag.
- "Guess" based on the structure of the data (for example, an array of two-member arrays)
- If the shape of the payload is fixed, based on the property name (for example, all properties called
registry
holdMap
objects).
Illegal JSON
When JSON.parse
receives a string that does not conform to the JSON grammar, it throws a SyntaxError
.
Arrays and objects cannot have trailing commas in JSON:
JSON.parse("[1, 2, 3, 4, ]");
// SyntaxError: Unexpected token ] in JSON at position 13
JSON.parse('{"foo": 1, }');
// SyntaxError: Unexpected token } in JSON at position 12
JSON strings must be delimited by double (not single) quotes:
JSON.parse("{'foo': 1}");
// SyntaxError: Unexpected token ' in JSON at position 1
JSON.parse("'string'");
// SyntaxError: Unexpected token ' in JSON at position 0
If you are writing JSON inside a JavaScript string literal, you should either use single quotes to delimit the JavaScript string literal, or escape the double quotes that delimit the JSON string:
JSON.parse('{"foo": 1}'); // OK
JSON.parse("{\"foo\": 1}"); // OK
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-json.parse |


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/iteration_protocols/index.md

# Original Wiki contributors
dd-pardal
sinanergin
mfuji09
RainSlide
fscholz
tim_cannon
Zearin_Galaurum
wbamberg
KennethKinLum
GKJCJG
dmitryrn
juraj
SIMON-Yann
conartist6
kdex
jackblackevo
Robg1
stephaniehobson
yackSpillsaps
Landmaster
rwaldron
CodeFry
mamacdon
wedaren
mksarge
XMO
bcherny
nmve
DarylPinto
jsbisht
Delapouite
danwellman
AshCoolman
teoli
Nickolay
stevekinney
arai
icold
SphinxKnight
ziyunfei
KevinLozandier
dbruant
Havvy


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/lexical_grammar/index.md

# Original Wiki contributors
iwhoisrishabh
hinell
wbamberg
fscholz
lucaswerkmeister
neverRare
LeonFrempong
mfuji09
Aljullu
rwaldron
jinbeomhong
alattalatta
Nux
chrisdavidmills
irenesmith
zhaoyingdu
myakura
Waldo
GSRyu
Erutuon
mistoken
nmve
kdex
lewisje
fasttime
Pointy
stevemao
michaelficarra
merih
Hywan
akinjide
KiraAndMaxim
realityking
jpmedley
LoTD
Minat
Penny
jensen
morello
jscape
RogerPoon
shvaikalesh
zzmp
williampower


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/execution_model/index.md

# Original Wiki contributors
fscholz
BoQsc
vegerot
wbamberg
PierBover
Zearin_Galaurum
Sheppy
divyanshu013
SphinxKnight
AnkitaSood
olegolegoleg
eyalChistik
metasean
MadEmperorYuri
harminho
Peiren
pirtleshell
antunmagdic
adrianosmond
doubleOrt
utkarshbhatt12
Jun711
aminoche
stephaniehobson
sautumn
sturmer
coolarun
perryjiang
broAhmed
roryokane
nmve
Martin-Ting
onurtemizkan
keshvari
jxjj
mprogers
nikjohn
raeesiqbal
masthandev
amilajack
inglor
hanu412
jswisher
PortableStick
suhajdab
kscarfone
chakkakuru
EddieFloresLive
dbruant


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/stmt_after_return/index.md

# Original Wiki contributors
fscholz
yhakina1114
rolfedh
jorendorff-moz
clarkbdubya


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/already_has_pragma/index.md

# Original Wiki contributors
fscholz


Function: length

Function: length
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
The length
data property of a Function
instance indicates the number of parameters expected by the function.
Try it
function func1() {}
function func2(a, b) {}
console.log(func1.length);
// Expected output: 0
console.log(func2.length);
// Expected output: 2
Value
A number.
Property attributes of Function: length | |
|---|---|
| Writable | no |
| Enumerable | no |
| Configurable | yes |
Description
A Function
object's length
property indicates how many arguments the function expects, i.e., the number of formal parameters:
- Only parameters before the first one with a default value are counted.
- A destructuring pattern counts as a single parameter.
- The rest parameter is excluded.
By contrast, arguments.length
is local to a function and provides the number of arguments actually passed to the function.
The Function
constructor is itself a Function
object. Its length
data property has a value of 1
.
Due to historical reasons, Function.prototype
is a callable itself. The length
property of Function.prototype
has a value of 0
.
Examples
Using function length
console.log(Function.length); // 1
console.log((() => {}).length); // 0
console.log(((a) => {}).length); // 1
console.log(((a, b) => {}).length); // 2 etc.
console.log(((...args) => {}).length);
// 0, rest parameter is not counted
console.log(((a, b = 1, c) => {}).length);
// 1, only parameters before the first one with
// a default value are counted
console.log((({ a, b }, [c, d]) => {}).length);
// 2, destructuring patterns each count as
// a single parameter
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-function-instances-length |


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/malformed_uri/index.md

# Original Wiki contributors
fscholz
PatrickKettner
thornedlove


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/called_on_incompatible_type/index.md

# Original Wiki contributors
fscholz
SphinxKnight
th0rgall
PatrickKettner
WangNianyi2001
nbp


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/key_not_weakly_held/index.md


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/getter_only/index.md

# Original Wiki contributors
fscholz
PatrickKettner


Object.prototype.valueOf()

Object.prototype.valueOf()
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
The valueOf()
method of Object
instances converts the this
value to an object. This method is meant to be overridden by derived objects for custom type conversion logic.
Try it
function MyNumberType(n) {
this.number = n;
}
MyNumberType.prototype.valueOf = function () {
return this.number;
};
const object = new MyNumberType(4);
console.log(object + 3);
// Expected output: 7
Syntax
valueOf()
Parameters
None.
Return value
The this
value, converted to an object.
Note:
In order for valueOf
to be useful during type conversion, it must return a primitive. Because all primitive types have their own valueOf()
methods, calling primitiveValue.valueOf()
generally does not invoke Object.prototype.valueOf()
.
Description
JavaScript calls the valueOf
method to convert an object to a primitive value. You rarely need to invoke the valueOf
method yourself; JavaScript automatically invokes it when encountering an object where a primitive value is expected.
This method is called in priority by numeric conversion and primitive conversion, but string conversion calls toString()
in priority, and toString()
is very likely to return a string value (even for the Object.prototype.toString()
base implementation), so valueOf()
is usually not called in this case.
All objects that inherit from Object.prototype
(that is, all except null
-prototype objects) inherit the toString()
method. The Object.prototype.valueOf()
base implementation is deliberately useless: by returning an object, its return value will never be used by any primitive conversion algorithm. Many built-in objects override this method to return an appropriate primitive value. When you create a custom object, you can override valueOf()
to call a custom method, so that your custom object can be converted to a primitive value. Generally, valueOf()
is used to return a value that is most meaningful for the object — unlike toString()
, it does not need to be a string. Alternatively, you can add a [Symbol.toPrimitive]()
method, which allows even more control over the conversion process, and will always be preferred over valueOf
or toString
for any type conversion.
Examples
Using valueOf()
The base valueOf()
method returns the this
value itself, converted to an object if it isn't already. Therefore its return value will never be used by any primitive conversion algorithm.
const obj = { foo: 1 };
console.log(obj.valueOf() === obj); // true
console.log(Object.prototype.valueOf.call("primitive"));
// [String: 'primitive'] (a wrapper object)
Overriding valueOf for custom objects
You can create a function to be called in place of the default valueOf
method. Your function should take no arguments, since it won't be passed any when called during type conversion.
For example, you can add a valueOf
method to your custom class Box
.
class Box {
#value;
constructor(value) {
this.#value = value;
}
valueOf() {
return this.#value;
}
}
With the preceding code in place, any time an object of type Box
is used in a context where it is to be represented as a primitive value (but not specifically a string), JavaScript automatically calls the function defined in the preceding code.
const box = new Box(123);
console.log(box + 456); // 579
console.log(box == 123); // true
An object's valueOf
method is usually invoked by JavaScript, but you can invoke it yourself as follows:
box.valueOf();
Using unary plus on objects
Unary plus performs number coercion on its operand, which, for most objects without [Symbol.toPrimitive]()
, means calling its valueOf()
. However, if the object doesn't have a custom valueOf()
method, the base implementation will cause valueOf()
to be ignored and the return value of toString()
to be used instead.
+new Date(); // the current timestamp; same as new Date().getTime()
+{}; // NaN (toString() returns "[object Object]")
+[]; // 0 (toString() returns an empty string list)
+[1]; // 1 (toString() returns "1")
+[1, 2]; // NaN (toString() returns "1,2")
+new Set([1]); // NaN (toString() returns "[object Set]")
+{ valueOf: () => 42 }; // 42
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-object.prototype.valueof |


Object.prototype.toString()

Object.prototype.toString()
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
The toString()
method of Object
instances returns a string representing this object. This method is meant to be overridden by derived objects for custom type coercion logic.
Try it
const map = new Map();
console.log(map.toString());
// Expected output: "[object Map]"
Syntax
toString()
Parameters
By default toString()
takes no parameters. However, objects that inherit from Object
may override it with their own implementations that do take parameters. For example, the Number.prototype.toString()
and BigInt.prototype.toString()
methods take an optional radix
parameter.
Return value
A string representing the object.
Description
JavaScript calls the toString
method to convert an object to a primitive value. You rarely need to invoke the toString
method yourself; JavaScript automatically invokes it when encountering an object where a primitive value is expected.
This method is called in priority by string conversion, but numeric conversion and primitive conversion call valueOf()
in priority. However, because the base valueOf()
method returns an object, the toString()
method is usually called in the end, unless the object overrides valueOf()
. For example, +[1]
returns 1
, because its toString()
method returns "1"
, which is then converted to a number.
All objects that inherit from Object.prototype
(that is, all except null
-prototype objects) inherit the toString()
method. When you create a custom object, you can override toString()
to call a custom method, so that your custom object can be converted to a string value. Alternatively, you can add a [Symbol.toPrimitive]()
method, which allows even more control over the conversion process, and will always be preferred over valueOf
or toString
for any type conversion.
To use the base Object.prototype.toString()
with an object that has it overridden (or to invoke it on null
or undefined
), you need to call Function.prototype.call()
or Function.prototype.apply()
on it, passing the object you want to inspect as the first parameter (called thisArg
).
const arr = [1, 2, 3];
arr.toString(); // "1,2,3"
Object.prototype.toString.call(arr); // "[object Array]"
Object.prototype.toString()
returns "[object Type]"
, where Type
is the object type. If the object has a Symbol.toStringTag
property whose value is a string, that value will be used as the Type
. Many built-in objects, including Map
and Symbol
, have a Symbol.toStringTag
. Some objects predating ES6 do not have Symbol.toStringTag
, but have a special tag nonetheless. They include (the tag is the same as the type name given below):
The arguments
object returns "[object Arguments]"
. Everything else, including user-defined classes, unless with a custom Symbol.toStringTag
, will return "[object Object]"
.
Object.prototype.toString()
invoked on null
and undefined
returns [object Null]
and [object Undefined]
, respectively.
Examples
Overriding toString for custom objects
You can create a function to be called in place of the default toString()
method. The toString()
function you create should return a string value. If it returns an object and the method is called implicitly during type conversion, then its result is ignored and the value of a related method, valueOf()
, is used instead, or a TypeError
is thrown if none of these methods return a primitive.
The following code defines a Dog
class.
class Dog {
constructor(name, breed, color, sex) {
this.name = name;
this.breed = breed;
this.color = color;
this.sex = sex;
}
}
If you call the toString()
method, either explicitly or implicitly, on an instance of Dog
, it returns the default value inherited from Object
:
const theDog = new Dog("Gabby", "Lab", "chocolate", "female");
theDog.toString(); // "[object Object]"
`${theDog}`; // "[object Object]"
The following code overrides the default toString()
method. This method generates a string containing the name
, breed
, color
, and sex
of the object.
class Dog {
constructor(name, breed, color, sex) {
this.name = name;
this.breed = breed;
this.color = color;
this.sex = sex;
}
toString() {
return `Dog ${this.name} is a ${this.sex} ${this.color} ${this.breed}`;
}
}
With the preceding code in place, any time an instance of Dog
is used in a string context, JavaScript automatically calls the toString()
method.
const theDog = new Dog("Gabby", "Lab", "chocolate", "female");
`${theDog}`; // "Dog Gabby is a female chocolate Lab"
Using toString() to detect object class
toString()
can be used with every object and (by default) allows you to get its class.
const toString = Object.prototype.toString;
toString.call(new Date()); // [object Date]
toString.call(new String()); // [object String]
// Math has its Symbol.toStringTag
toString.call(Math); // [object Math]
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
Using toString()
in this way is unreliable; objects can change the behavior of Object.prototype.toString()
by defining a Symbol.toStringTag
property, leading to unexpected results. For example:
const myDate = new Date();
Object.prototype.toString.call(myDate); // [object Date]
myDate[Symbol.toStringTag] = "myDate";
Object.prototype.toString.call(myDate); // [object myDate]
Date.prototype[Symbol.toStringTag] = "prototype polluted";
Object.prototype.toString.call(new Date()); // [object prototype polluted]
Specifications
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-object.prototype.tostring |


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/reduce_of_empty_array_with_no_initial_value/index.md

# Original Wiki contributors
fscholz
lmmfranco
om-ganesh
nbp


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/cant_delete/index.md

# Original Wiki contributors
fscholz
PatrickKettner
nmve


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/no_properties/index.md

# Original Wiki contributors
fscholz
PatrickKettner


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/more_arguments_needed/index.md

# Original Wiki contributors
fscholz
Bzbarsky
PatrickKettner


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/requires_global_regexp/index.md


# Contributors by commit history
https://github.com/mdn/content/commits/main/files/en-us/web/javascript/reference/errors/constructor_cant_be_used_directly/index.md