Почему при сложении массив превращается в строку, а объект в object Object?
[].toString() // "" (пустая строка)
{}.toString() // "[object Object]" (строка)
spoilerconst myObject = {}; // «тоже объект»
myObject.toString = () => "QnA!"; // свой метод преобразования в строку

myObject + "habr" //  "QnA!habr"
При сложении если хотя бы один из операндов - строка, то оба операнда приводятся с строке и выполняется конкатенация. ( https://262.ecma-international.org/11.0/#sec-addit... )
Для объектов (массив тоже объект) срабатывает приведение к примитиву ( https://262.ecma-international.org/11.0/#table-12 ). Оно, в свою очередь, вызывает встроенный метод toString().
Для массива, имеющего метод join, toString возвращает результат работы join ( https://developer.mozilla.org/ru/docs/Web/JavaScri... ).
Для объектов возвращается строка '[object тип]' ( https://developer.mozilla.org/ru/docs/Web/JavaScri... ).