Binary + follows one rule, from the spec's ApplyStringOrNumericBinaryOperator:
- Convert both operands with
ToPrimitive(no hint, sovalueOfthentoStringfor ordinary objects;Dateuses string). - If either primitive is a String, convert the other with
ToStringand concatenate. - Otherwise convert both with
ToNumeric(Number or BigInt) and add. If one is a BigInt and the other a Number, throw aTypeError.
For [] + {}: [] calls valueOf (returns the array itself, not a primitive), then toString, which joins no elements and gives "". {} gives "[object Object]" the same way. One side is a string, so the result is "" + "[object Object]".
The rule explains the common cases: 1 + "2" is "12", 1 + 2 + "3" is "33" (left to right), "3" + 1 + 2 is "312", true + 1 is 2, null + 1 is 1, and undefined + 1 is NaN.
Only + concatenates. -, *, / and the others always convert to numbers, so "5" - 2 is 3. The unary +x is also numeric: it is the idiomatic short form of Number(x).
{} + [] printed in a console often shows 0. That is a parsing artifact: at the start of a statement, {} is parsed as an empty block, leaving the expression +[], which is 0. In expression position (such as an argument, console.log({} + [])) it is "[object Object]" again.