Ну там немного все сложнее.
Сначала ищется метод [Symbol.toPrimitive], и если он установлен, то вызывается с параметром, указывающем, что надо получить (число или строку), а если его нет, то в зависимости от ситуации если надо получить число, сначала ищется valueOf потом toString (если valueOf нет), а если надо получить строку, то сначала ищется toString, а если нет, то valueOf
Т.е можно было записать так
var ArrayWrapper = function(nums) {
this.nums = nums;
};
ArrayWrapper.prototype[Symbol.toPrimitive] = function (hint) {
if (hint==='default' || hint==='number') {
return this.nums.reduce((sum, num) => sum + num, 0);
}
return `[${this.nums.join(',')}]`;
}
const obj1 = new ArrayWrapper([1,2]);
const obj2 = new ArrayWrapper([3,4]);
console.log(obj1 + obj2); // 10
console.log(String(obj1)); // "[1,2]"
console.log(String(obj2)); // "[3,4]"