Object.prototype.toString()

Two jobs in one method. Directly it produces the least helpful string in JavaScript; borrowed with .call it is the most reliable type check the language has ever had.

Object methodES1 (1997)Live demo
Common call
Object.prototype.toString.call(v)
Returns
'[object Array]', '[object Null]', …
Replaces
typeof, which cannot distinguish arrays, dates or null
Watch out
Symbol.toStringTag lets any object lie about its type
Object.prototype.toString.call(value)
→ string

Demo

Live evaluation
Try:
Inputs
jsonstringany JSON value
Output
Object.prototype.toString.call(JSON.parse('{"a":1}'))
'[object Object]'

Each value reports its internal type, and the two marked cases are the ones typeof gets wrong. An array is '[object Array]' here while typeof says 'object'; null is '[object Null]' while typeof famously also says 'object'. That is why this borrowed call was the standard type check for two decades — it is the only mechanism that distinguishes arrays, dates, regexes and null from ordinary objects in a single expression. Note the demo has to use .call, because calling toString on the value directly would find that type own toString instead.

Common patterns

A general type tag
The classic helper, still used by libraries.
const typeOf = v =>
  Object.prototype.toString.call(v).slice(8, -1);
Prefer the specific check
Clearer and faster where one exists.
Array.isArray(v);
v instanceof Date;
v === null;
Give your class a tag
Symbol.toStringTag sets the reported name.
class Box { get [Symbol.toStringTag]() { return "Box"; } }

Examples

1. Plain object
Object.prototype.toString.call({})
Returns
'[object Object]'
2. Array
Object.prototype.toString.call([])
Returns
'[object Array]'
3. null
Object.prototype.toString.call(null)
Returns
'[object Null]'
4. typeof is wrong
typeof null
Returns
'object'
5. Date
Object.prototype.toString.call(new Date())
Returns
'[object Date]'
6. A custom tag
Object.prototype.toString.call({[Symbol.toStringTag]: "X"})
Returns
'[object X]'

Pitfalls

1. Calling it directly gives [object Object] and nothing else
This is where that string comes from: any plain object coerced to text produces it, because toString has no idea what your object contains. It appears in template literals, in string concatenation and as an alert body, and it always means the same thing — an object was converted to a string.
Useless output
`Value: ${{a: 1}}`
'Value: [object Object]'
Serialise it
`Value: ${JSON.stringify({a: 1})}`
'Value: {"a":1}'
2. Symbol.toStringTag lets objects lie
Since ES2015 any object can declare its own tag, so the result is no longer a trustworthy view of the internal type. It remains excellent for the built-ins, and is not a security check.
Claims to be an Array
Object.prototype.toString.call({[Symbol.toStringTag]: "Array"})
'[object Array]'
The real check
Array.isArray({[Symbol.toStringTag]: "Array"})
false
3. You must use .call
Calling value.toString() finds the type own override — an array joins with commas, a date formats itself, a number prints digits. Only borrowing the Object.prototype version gives the type tag.
Array override
[1, 2].toString()
'1,2'
Borrowed
Object.prototype.toString.call([1, 2])
'[object Array]'
4. valueOf and toLocaleString are near-useless here
Object.prototype.valueOf returns the object itself, so it does nothing during coercion and lets toString take over. toLocaleString simply calls toString. Both exist to be OVERRIDDEN by Date, Number and Array — on a plain object neither does anything interesting.
Returns itself
const o = {};
o.valueOf() === o
true
Override it
const money = {valueOf: () => 5};
money * 2
10

When to use

Use it
  • A single expression that distinguishes every built-in type
  • Writing a general-purpose type-tag helper
  • Supporting very old runtimes where Array.isArray is absent
  • Understanding where [object Object] came from
Reach for something else
  • Checking for an array → Array.isArray
  • Checking for null → v === null
  • Checking a class instance → instanceof
  • Anything security-relevant → the tag can be forged

Notes

Complexity
O(1)
Return
A new string each call
CPython impl
V8: Builtins-object-tostring
Memory
Allocates the result string
Thread-safe
Single-threaded; the value is only inspected

FAQ

Because string coercion calls toString, and the default implementation knows only the internal type — not your data. Use JSON.stringify for a readable form, or give the class a toString of its own.

JSON.stringify(obj);
class P { toString() { return `P(${this.x})`; } }

History

ES1
toString and valueOf present from the first version as the coercion hooks.
ES3
toLocaleString added, delegating to toString on plain objects.
ES2015
Symbol.toStringTag made the reported tag customisable, ending its reliability as a type check.