Object property names can ONLY be strings or Symbols. We can ignore Symbols here. If you actually want to use objects as a key, then you can use a Map or a Set.
[u1]: { [posts]: [ p1, p2 ] },
This ☝️ line adds a property named [Object object] to the database object. It sets the value associated with that property to a new object that itself is initialised with a property named [Object object] whose value is set to be an array containing p1 and p2.
[u2]: { [posts]: [ p3 ] },
This ☝️ line overwrites the value of the property named [Object object] with a reference to a new object that has one property named [Object object] that has a value of an array containing p3.
When referring to an object property with the bracket syntax [<expression>], the expression is first evaluated, and the result is then coerced (because JS is weakly typed) to a string, if need be, to form the property name.
u1 and u2 are objects. Using an object as a property key will, by default, result in a call to the abstract internal operation OrdinaryToPrimitive with the hint 'string'. This will invoke Object.prototype.toString(), and this will, by default, result in the string '[Object object]'.
Note that you can override various aspects of this coercion behavior; for example, by implementing your own Symbol.toStringTag getter method:
class Association {}
class Document {}
class Post extends Document {}
class User extends Document {}
let [ u1, u2 ] = [new User(), new User()]
let [ p1, p2, p3 ] = [ new Post(), new Post(), new Post() ]
let posts = new Association(User, Post, "posts")
Object.defineProperty(u1, Symbol.toStringTag, {
get() { return 'u1' }
})
let database = {
[u1]: { [posts]: [ p1, p2 ] },
[u2]: { [posts]: [ p3 ] },
}
console.log("HERE", JSON.stringify(database))
More details here.