skip to content

In JavaScript, ([] + {}) evaluates to the string '[object Object]', but a script line that starts with {} + [] evaluates to 0. Explain both results.

level: middleimportance: nice to knowfreq 30%

answer

  1. two different mechanisms, not one
  2. empty array stringifies to nothing
  3. braces mean different things by position
  4. the second plus is unary
  5. parentheses change the parse, not the values

basics

~20 s

Two different mechanisms. In expression position both operands convert to primitives — '' and '[object Object]' — and + concatenates. At the start of a statement, {} parses as an empty block, so what remains is unary +[], which converts the empty array to '' and then to the number 0.

solid answer

~40 s

`[] + {}` is pure coercion: binary `+` converts each operand with the default hint, so the array becomes `''` (its `join`) and the object becomes `'[object Object]'` (from `Object.prototype.toString`); one operand is a string, so `+` concatenates and you get `'[object Object]'`. The second case is a **parsing** result, not a coercion one. At the start of a statement a `{` begins a block, so `{}` is an empty block statement and the rest of the line is the separate expression statement `+[]` — unary plus, which does `ToNumber([])`, giving `''` and then `0`. Wrap it in parentheses or assign it and `{}` is unambiguously an object literal again, so `({} + [])` is `'[object Object]'`. The lesson is that the same characters mean different things in statement and expression position.

code

javascript · 9 lines
javascript
// expression position: binary + and coercion
console.log([] + {});        // '[object Object]'
console.log(({} + []));      // '[object Object]'
console.log(typeof ({} + [])); // 'string'

// statement position: empty block, then unary +
// (run as a script, not typed into a console)
{} + [];                     // completion value 0
console.log(+[]);            // 0  — the unary part on its own

go deeper

for a junior

Recall that an empty array stringifies to the empty string and a plain object to '[object Object]', and that seeing '[object Object]' in output means an object reached a string context.

for a middle

Separate the two mechanisms cleanly: coercion in expression position versus a leading brace parsed as a block plus a unary operator. Show that parentheses restore the object-literal reading.

for a senior

Connect it to bugs you have shipped — an arrow function returning undefined because its object literal was parsed as a block, or '[object Object]' turning up in a cache key — and say how linting and explicit conversion prevent both.

for a principal

Treat puzzles like this as a signal about house style: ban implicit object-to-string conversion at boundaries, standardise on explicit serialisation, and judge whether such trivia belongs in your interview loop at all versus questions with production consequences.

## Result one: ordinary coercion `[] + {}` in expression position exercises nothing exotic. Binary `+` runs `ToPrimitive` on each operand with the **default** hint, which for ordinary objects means "try `valueOf()`, then `toString()`". - `[]`: `Array.prototype.valueOf` is inherited from `Object.prototype` and returns the array itself — not a primitive — so `Array.prototype.toString` runs, which is `join(',')` over zero elements: the empty string `''`. - `{}`: same fall-through, and `Object.prototype.toString` returns `'[object Object]'`. Now `+` applies its rule: at least one primitive is a string, so both are stringified and concatenated. `'' + '[object Object]'` is `'[object Object]'`. ```js String([]); // '' String({}); // '[object Object]' [] + {}; // '[object Object]' [] + []; // '' [1,2] + [3]; // '1,23' ``` ## Result two: it is a parsing question JavaScript's grammar is ambiguous about `{`. At the point where the parser expects a **statement**, `{` starts a *block*; where it expects an **expression**, `{` starts an *object literal*. A line beginning `{} + []` is at statement position, so the parser sees: 1. `{}` — an empty block statement, which produces no value; 2. `+[]` — a separate expression statement using **unary** plus. Unary `+` is a different operator from binary `+`: it performs `ToNumber` and can never concatenate. `ToNumber([])` goes through `ToPrimitive` with the number hint, gets `''`, and `Number('')` is `0`. So the completion value of the line is `0`. The moment you put the same characters somewhere an expression is expected, `{}` is an object literal again and the binary operator returns: ```js ({} + []); // '[object Object]' — parenthesised, expression position const x = {} + []; // '[object Object]' — after '=', expression position console.log({} + []); // '[object Object]' — argument position ``` Be careful drawing conclusions from a REPL or a browser console: these tools decide for themselves whether to evaluate your input as a statement or wrap it as an expression, and they have differed on exactly this input. If you want to know what the language does, put the code in a script and use an explicit position. ## The same ambiguity elsewhere This is not a one-off curiosity; the statement-versus-expression rule for `{` shows up in normal code: ```js // Arrow function returning an object needs parentheses, // otherwise the body is read as a block: const make = () => ({ ok: true }); // returns an object const broken = () => { ok: true }; // returns undefined ``` In `broken`, `{ ok: true }` is a block containing a labelled statement `ok:` whose body is the expression `true` — valid syntax, no value returned. The same grammar rule explains why an IIFE is written `(function(){})()` or `!function(){}()`: a bare `function` at statement position is a declaration, not an expression. ## What a candidate should take away The interesting half of this puzzle is not `'[object Object]'` — it is that a JavaScript answer can hinge on parsing rather than on values. When output surprises you, ask two questions in order: *how did the parser read this text*, and only then *what did the operators do with the values*. A frequent real-world instance is an arrow function whose object literal silently became a block, returning `undefined` with no error anywhere. And the coercion half has a practical edge too: `'[object Object]'` appearing in a log line, a URL or a cache key always means an object reached a string context without an intended conversion. That string is a defect signature — track it back to the `+`, template literal or `String()` call that produced it, and convert deliberately with `JSON.stringify`, a real `toString`, or an explicit field.

  • Why is +[] equal to 0 but +{} equal to NaN?
    Unary `+` performs `ToNumber`, which first takes a primitive. `[]` stringifies to `''` via `join`, and `Number('')` is `0`. `{}` stringifies to `'[object Object]'`, which is not a valid numeric literal, so `ToNumber` yields `NaN`. Both take the identical path; only the intermediate string differs.
  • Where else does the statement-versus-expression rule for braces bite in real code?
    Most often in a concise arrow function meant to return an object literal: `() => { ok: true }` parses the braces as a block and returns `undefined`, so you must write `() => ({ ok: true })`. The same rule is why a function expression used immediately needs wrapping, as in `(function () { ... })()` — a bare `function` at statement position is a declaration.

saying these in an interview costs you the question

  • Explains both results with coercion alone, ignoring parsing
  • Says the difference is operand order in binary +
  • Claims JavaScript evaluates + right to left here
  • Thinks {} always means an object literal
  • Believes a browser console proves the language's parse

context