I am studying prototyping in Javascript, and for an experiment was trying to strip some basic objects of their information as much as possible. However, I ran into an issue in the following code segment:
let x = [];
Object.setPrototypeOf(x, null);
console.log(Object.prototype.toString.call(x));
>>> [object Array]
My confusion here: after removing the prototype, how does Object.prototype.toString
still know that x
is of Array
type? Where is this information coming from, if not any of the properties? Running Object.getOwnPropertyNames(x)
and Object.getOwnPropertySymbols(x)
, I do not see any properties left on x
which contain any path to the string Array
. Where is this information stored?
And if this is one of those "internal slots" (although I'm not sure which it would be), is there no way to modify it? Can I at least access it directly?
Update:
Yousaf has noted that the property String.toStringTag
is normally accessed by Object.prototype.toString
to provide the string which goes in place of "Array" in the above example. Indeed, setting x[Symbol.toStringTag] = 'my_string'
allows us to change the .toString()
value. However, I think it still does not solve the mystery, as x[Symbol.toStringTag]
is undefined
before we set it, not containing the desired "Array" string.
When .toString()
is called on any object, it creates a string of the form [object x]
where x
is the name of the constructor in-case of built-in constructors like Date
and Array
.
When .toString()
is called, there are several steps taken and one of those steps is checking if the value of this
inside .toString()
is an array. If its an array, tag is set to the string 'Array'
.
From the Spec - 19.1.3.6 Object.prototype.toString ( ):
- If isArray is true, let builtinTag be "Array".
Before ES6, when .toString()
was called on the instances of user defined constructors, [object Object]
was logged. ES6 provided a way to override the tag using a well-known Symbol Symbol.toStringTag
.