Object.prototype.toString()
Returns a bracketed type tag such as [object Array] describing the object's internal kind.
toString()- Added in JavaScript ES1
Return value
stringA string of the form "[object Type]", where Type comes from the value's Symbol.toStringTag or its internal kind.
Errors
Never throws for the default implementation; called via .call() it accepts every value, including null and undefined.
Changelog
- PHP ES2015The tag is now taken from a Symbol.toStringTag property when one is present, so any object can change what this method reports about it.
Called on a plain object, toString() produces the famous "[object Object]" — the string you see when an object is accidentally concatenated or interpolated. That is the same mechanism, not a bug.
The type-check idiom
Its real use is Object.prototype.toString.call(value), which invokes the base implementation on any value regardless of what that value's own toString does. It is the classic way to tell apart things typeof cannot:
| Value | typeof | toString.call() |
|---|---|---|
null | "object" | [object Null] |
[] | "object" | [object Array] |
new Date() | "object" | [object Date] |
/x/ | "object" | [object RegExp] |
Unlike instanceof, it also works across iframes and realms, where each realm has its own Array.
What trips people up
Since ES2015 the tag is read from Symbol.toStringTag, so any object can claim to be anything: set that symbol and the check reports whatever you chose. Treat the result as a strong hint, not a security boundary — for arrays specifically, Array.isArray() is exact and cannot be spoofed.
Objects with a null prototype have no toString at all; calling obj.toString() on them throws, though .call() still works.
Example
When to use it
- Distinguish arrays, dates and regexes when typeof reports plain "object"
- Identify values that arrived from another realm, where instanceof fails
- Explain a "[object Object]" appearing in log output or a template string
Frequently asked questions
Why do I see [object Object] in my output?
An object was converted to a string without a custom toString(), so the default Object.prototype.toString() ran; use JSON.stringify() or log the object itself instead of interpolating it.
Is Object.prototype.toString.call() a reliable type check?
It is reliable for built-ins across realms, but any object can override its tag with Symbol.toStringTag, so use Array.isArray() for arrays and treat the tag as a hint elsewhere.
Why does calling toString() on my object throw?
The object has a null prototype and therefore inherits no toString; call Object.prototype.toString.call(obj) or String(obj) alternatives that do not rely on inheritance.
More examples
A type() helper
Slicing off the "[object " prefix and the closing bracket leaves a usable name. The last line shows what this buys you: typeof collapses null, arrays and every other object into a single useless answer.
function type(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
console.log(type(null)); // "null"
console.log(type([])); // "array"
console.log(type(new Date(0))); // "date"
console.log(type(NaN)); // "number"
console.log(type(new Set())); // "set"
console.log(typeof null, typeof []); // "object" "object"The tag can be changed
A class can give itself a meaningful tag, which is useful in logs. The same mechanism lets any object impersonate a built-in, so a tag of [object Array] proves nothing while Array.isArray() stays correct. The last pair shows .call() working on a null-prototype object that has no toString of its own.
class Stream {
get [Symbol.toStringTag]() {
return "Stream";
}
}
console.log(Object.prototype.toString.call(new Stream())); // [object Stream]
const liar = { [Symbol.toStringTag]: "Array" };
console.log(Object.prototype.toString.call(liar)); // [object Array]
console.log(Array.isArray(liar)); // false — not fooled
const bare = Object.create(null);
console.log(Object.prototype.toString.call(bare)); // [object Object]
console.log(typeof bare.toString); // "undefined"
Discussion