An expression evaluates to a value. ECMA-262 defines precedence through the shape of its grammar, with a separate production for each level (ECMAScript Language: Expressions). The table below flattens that grammar into levels.
Precedence and associativity
Higher levels bind first. Associativity decides how operators of the same level
group: left means a - b - c is (a - b) - c.
| Level | Operator | Meaning | Associativity |
|---|---|---|---|
| 18 | (x) | Grouping | n/a |
| 17 | a.b a?.b a[b] new F(args) f(args) import(x) | Member access, optional chaining, computed access, construct with arguments, call, dynamic import | Left for access and calls |
| 16 | new F | Construct, no arguments | n/a |
| 15 | a++ a-- | Postfix increment and decrement | n/a |
| 14 | ++a --a !a ~a +a -a typeof a void a delete a.b await a | Prefix increment and decrement, not, bitwise not, unary plus, negation, type name, void, property removal, awaiting a promise | n/a |
| 13 | ** | Exponentiation | Right |
| 12 | * / % | Multiply, divide, remainder | Left |
| 11 | + - | Add or concatenate, subtract | Left |
| 10 | << >> >>> | Shifts | Left |
| 9 | < <= > >= in instanceof | Relational | Left |
| 8 | == != === !== | Equality | Left |
| 7 | & | Bitwise and | Left |
| 6 | ^ | Bitwise exclusive or | Left |
| 5 | | | Bitwise or | Left |
| 4 | && | Logical and | Left |
| 3 | || ?? | Logical or, nullish coalescing | Left |
| 2 | = += -= *= /= %= **= <<= >>= >>>= &= |= ^= &&= ||= ??= a ? b : c yield yield* => ... | Assignment, conditional, yield, arrow functions, spread | Right for assignment and conditional; n/a for the rest |
| 1 | , | Comma | Left |
Three rules cut across the table:
- A unary expression may not be the left operand of
**. So-2 ** 2is a syntax error; write(-2) ** 2or-(2 ** 2). ??may not be mixed with&&or||without parentheses:a ?? b || cis a syntax error.- In the head of a
forstatement, a bareinis not an operator unless parenthesised, because of thefor-inform.
Evaluation order
Operands are evaluated left to right, each fully before the next, and before the operator is applied. In an assignment, the left-hand side’s reference is worked out first, then the right-hand side.
let i = 0;
const a = [];
a[i++] = i; // a[0] = 1
f() + g() * h(); // calls f, g, h in that order
Primary expressions
| Form | Meaning |
|---|---|
this | The current this binding |
| An identifier | The value of a binding, or a ReferenceError |
| A literal | See Literals |
[ ], { } | Array and object literals |
function, class, function*, async function | Function and class expressions |
| A template | A template literal |
( expression ) | Grouping. It keeps the kind of reference, so (a.b)() still calls with this as a |
Object and array literals
const key = 'dynamic', x = 1;
const obj = {
x, // shorthand for x: x
[key + 'Name']: 2, // computed key
method() { return 3; }, // method
get value() { return 4; }, // getter
set value(v) {}, // setter
async go() {}, // async method
*gen() {}, // generator method
async *agen() {}, // async generator method
__proto__: Array.prototype, // sets the prototype
...other, // copy another object’s properties
};
const arr = [1, , 3, ...more]; // holes and spread
- Trailing commas are allowed in both.
- Duplicate keys are allowed, and the last one wins, in strict code too (ES2015).
__proto__: valuesets the prototype, by Annex B. Only the plain form does this, not a computed or shorthand key, and a second one is an error.- A key is an identifier name, a string, a number, or a computed expression in brackets.
- A hole is a missing element: it counts in
length, but no property exists.
Property access and calls
- The left of
.or[]goes through ToObject, so accessing a property ofnullorundefinedthrows aTypeError. a[b]applies ToPropertyKey tob.new Fandnew F()are the same; the first is lower in the table, sonew F.gmeansnew (F.g), andnew F().gmeans(new F()).g.- A call on a member expression sets
this; see Functions. new.target,superandimport.metaare special forms: see Functions, Classes and Modules.
Optional chaining
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. Arguments
?. [ Expression ]
?. IdentifierName
?. TemplateLiteral (early error)
?. PrivateIdentifier
OptionalChain Arguments
OptionalChain [ Expression ]
OptionalChain . IdentifierName
OptionalChain TemplateLiteral (early error)
OptionalChain . PrivateIdentifier
a?.b, a?.[b] and a?.(b) evaluate to
undefined if a is null or
undefined, without evaluating the rest (ES2020).
- The whole chain stops at the first nullish link, so
a?.b.c(d)does not read.cor evaluatedwhenais nullish. - It tests only the left value.
a.b?.()callsbwiththisasa, ifbis not nullish. a?.b = 1is a syntax error. So arenew a?.b()and a template directly after?..delete a?.bis allowed, and givestrueif the chain stops.
Unary operators
| Operator | Behaviour |
|---|---|
typeof x | The type’s name: see Types |
void x | Evaluates x, gives undefined |
!x | ToBoolean, then not |
~x | ToNumeric; bitwise not, as ToInt32 for a Number |
+x | ToNumber, so a BigInt throws |
-x | ToNumeric, then negate |
delete x.y | Removes the property. Gives true, unless it cannot be deleted: then false in sloppy code and TypeError in strict code |
In strict code, delete on a plain name is a syntax error.
delete of a private field is a syntax error everywhere, and
delete super.x throws a ReferenceError at run time.
++ and -- need a target that can be assigned, apply
ToNumeric, and work on BigInts too. Postfix returns the old value and prefix the new
one.
Arithmetic
+applies ToPrimitive with hint"default"to both operands. If either is then a string, the other goes through ToString and they are joined. Otherwise both go through ToNumeric and are added.-,*,/,%and**apply ToNumeric to both.- Mixing Number and BigInt throws a
TypeError. %is a truncated remainder, with the sign of the dividend:-7 % 3is-1.**is right-associative, and2 ** 3 ** 2is512(ES2016). A negative BigInt exponent throws aRangeError.
Bitwise and shift
For Numbers, &, |, ^, ~ and
<< work on ToInt32, and >>> on ToUint32. The
result is a Number. The shift count is taken modulo 32. On BigInts they operate
without a width limit, except that >>> throws a
TypeError, since the sign has no fixed position.
Relational and equality
The comparison algorithms are on the Types page.
x in objis true ifxis a key ofobjor its prototype chain. The right operand must be an object.#x in objtests whetherobjhas the private element#x(ES2022). It is valid only inside the class body.x instanceof FusesF[Symbol.hasInstance]if present, and otherwise walksx’s prototype chain looking forF.prototype. The right operand must be callable.
Logical and nullish operators
| Operator | Returns | Right side is skipped when the left is |
|---|---|---|
a && b | a if falsy, else b | Falsy |
a || b | a if truthy, else b | Truthy |
a ?? b | a unless null or undefined, else b (ES2020) | Not nullish |
They return the operand itself, not a boolean. a || b falls through
for 0 and "", which a ?? b does not.
Assignment
AssignmentExpression:
ConditionalExpression
YieldExpression
ArrowFunction
AsyncArrowFunction
LeftHandSideExpression = AssignmentExpression
LeftHandSideExpression AssignmentOperator AssignmentExpression
LeftHandSideExpression &&= AssignmentExpression
LeftHandSideExpression ||= AssignmentExpression
LeftHandSideExpression ??= AssignmentExpression
- An assignment’s value is the assigned value, so
a = b = 1works. - A compound operator such as
+=evaluates the target once. &&=,||=and??=(ES2021) evaluate and assign the right side only when needed. If they short-circuit, no write happens, so a setter is not called.- Assignment to a name that does not resolve creates a global property in sloppy
code, and throws a
ReferenceErrorin strict code. - An anonymous function or class takes its name from the target, so in
const f = () => 1,f.nameis"f".
Destructuring
Destructuring reads values out of an object or iterable into bindings or targets.
Patterns work in var, let and const
declarations, parameters, catch clauses, for heads and
assignments.
const { a, b: renamed, c = 10, [key]: d, ...others } = obj;
const [x, , y = 2, ...rest] = iterable;
function draw({ width = 100, height = 100 } = {}) { }
let m, n;
[m, n] = [n, m]; // swap, as an assignment
({ m, n } = source); // needs parentheses at statement start
- An object pattern reads named properties. Keys can be
computed. A source of
nullorundefinedthrows aTypeError. - An array pattern uses the iteration protocol, so it works on any iterable. Commas skip elements.
- A default applies only when the value is
undefined, notnull. - A rest element must be last, with no trailing comma after it. In an object pattern it copies own enumerable properties not already picked out, and in an array pattern it collects the remaining values in an array.
- A declaration’s pattern binds names. An assignment’s pattern may target any
reference:
[o.x, a[0]] = pair. - Patterns can nest.
Spread
| Context | Mechanism | Since |
|---|---|---|
Array literal, [...a] | Iteration protocol | ES2015 |
Call, f(...a) | Iteration protocol | ES2015 |
Object literal, {...a} | Copies own enumerable properties, ignoring null and undefined | ES2018 |
Spread is shallow. Object spread defines properties with CopyDataProperties, and does not run setters on the new object. It is not an operator, so it has no value of its own.
Conditional, comma and others
a ? b : cevaluates only the chosen branch. It is right-associative, soa ? b : c ? d : eisa ? b : (c ? d : e).a, bevaluates both and givesb. The comma that separates arguments or declarators is not this operator.yieldandyield*are valid in generators, andawaitin async code, as explained in Iterators and generators and Async.