Keywords: JavaScript | Type Coercion | ECMAScript Specification | Operator Precedence | String Concatenation
Abstract: This article provides a comprehensive analysis of why the JavaScript expression ++[[]][+[]]+[+[]] returns the string "10", focusing on type coercion mechanisms. It breaks down the expression step by step, explaining array-to-number conversion, increment operator behavior, and string concatenation rules, with references to the ECMAScript specification. By reconstructing code examples and offering detailed explanations, the article elucidates the intricacies of implicit type conversion in JavaScript, aiding developers in writing more robust code and understanding esoteric programming patterns.
Expression Decomposition and Initial Simplification
To comprehend why the expression ++[[]][+[]]+[+[]] returns the string "10", it is essential to decompose it into manageable parts. Based on JavaScript operator precedence, the expression is equivalent to:
(++[[]][+[]]) + ([+[]])Here, +[] is a critical sub-expression. In JavaScript, the unary plus operator + converts its operand to a number. When applied to an empty array [], per the ECMAScript specification, the array is first converted to a primitive value. Specifically, the ToPrimitive operation invokes the array's toString method, and for an empty array, toString returns an empty string "". This empty string is then converted to a number, resulting in 0. Thus, +[] === 0.
Based on this, the expression simplifies to:
(++[[]][0]) + ([0])Array Access and Increment Operation
Next, consider the ++[[]][0] portion. Here, [[]] is an array containing one element, which is another empty array. Accessing index 0 yields this inner empty array, denoted as A (note that due to reference issues, A === [] is false, but semantically equivalent).
The prefix increment operator ++ converts its operand to a number, increments it by one, and returns the incremented value. For array A, the number conversion process is similar: A is converted to a string "", then to a number 0. Therefore, ++A is equivalent to +A + 1, i.e., 0 + 1, resulting in 1.
At this stage, the expression further simplifies to:
1 + [0]Addition Operation and Type Coercion
In JavaScript, the behavior of the addition operator + depends on the operand types. If either operand is a string, string concatenation is performed; otherwise, numeric addition occurs. Here, the left operand is the number 1, and the right operand is the array [0].
The array [0] is coerced to a string in the addition context. According to the specification, the array's toString method calls the join method. For a single-element array [0], join returns the string form of the element 0, i.e., "0". Hence, [0] == "0" is true.
Now, the expression becomes 1 + "0". Since the right operand is a string, the left operand number 1 is coerced to a string "1", and string concatenation yields "1" + "0" = "10".
In-Depth ECMAScript Specification Analysis
To gain a fuller understanding, we refer to relevant sections of the ECMAScript specification. For +[], under the Unary + Operator (11.4.6), the operand is converted to a number via ToNumber(GetValue(expr)). ToNumber for object types (like arrays) first invokes ToPrimitive with hint String, which triggers the [[DefaultValue]] internal method. For arrays, [[DefaultValue]] with hint String calls the toString method.
The array's toString method (15.4.4.2) delegates to the join method. An empty array's join returns an empty string "". Finally, ToNumber rules for an empty string specify its mathematical value as 0, so +[] === 0.
In the increment operation, ++[[]][0] involves ToNumber conversion of the inner array, again resulting in 0, then incremented to 1. During addition, ToPrimitive for array [0] returns the string "0", causing the number 1 to be coerced to a string, ultimately concatenated as "10".
Code Examples and Reconstruction
To visually demonstrate this process, we can reconstruct the code, simulating each step of type coercion. The following example uses explicit conversions to decompose the original expression:
// Step 1: Compute +[]
const step1 = +[]; // Result: 0
// Step 2: Access array [[]][0]
const innerArray = [[]][0]; // Yields []
// Step 3: Increment operation ++innerArray
const step3 = ++innerArray; // Equivalent to Number(innerArray) + 1, result: 1
// Step 4: Compute [+[]]
const step4 = [+[]]; // Yields [0]
// Step 5: Addition 1 + [0]
const step5 = 1 + [0]; // String concatenation, result: "10"
console.log(step5); // Output: "10"This reconstructed code stepwise illustrates type coercion, aiding in understanding implicit behaviors. In practical development, avoiding reliance on such implicit conversions enhances code readability and maintainability.
Supplementary Applications and Best Practices
Referring to the Excel formula example in the auxiliary article, type coercion and string operations are ubiquitous in programming. For instance, using SUMPRODUCT in Excel for multi-condition lookups involves implicit data type conversions (e.g., date comparisons). Similarly, in JavaScript, grasping type coercion mechanisms is vital for debugging complex expressions and preventing unintended behaviors.
Best practices include: using explicit conversion functions (e.g., Number(), String()) to avoid relying on operator implicit behaviors; employing strict equality === in conditionals to prevent type coercion; and for array operations, explicitly calling join or toString rather than depending on contextual conversion.
In summary, by deeply analyzing ++[[]][+[]]+[+[]], we uncover the complexities of JavaScript's type system. Mastering these principles facilitates writing more reliable and efficient code, and aids in identifying potential issues in obfuscated code like JSFuck.