Array.prototype.toString()

You rarely call it, and you invoke it constantly — every template literal, every string concatenation and every loose comparison against a string goes through here.

Array methodES1 (1997)Live demo
Common call
String(array)
Returns
the elements joined with ',' — no brackets, no quotes
Replaces
array.join(',')
Watch out
nesting is flattened and null becomes empty — data is lost
array.toString()
→ string

Demo

Live evaluation
Try:
Inputs
jsonstringa JSON array, e.g. [1,[2,3]]
Output
JSON.parse('[1,2,3]').toString()
'1,2,3'

The output has no brackets and no quotes, which is the first surprise — an array does not stringify to anything resembling its literal form. The nested case is the important one: [1,[2,[3,4]]] becomes "1,2,3,4", because each element is itself converted to a string and nested arrays recurse all the way down. Nothing in the result tells you where one array ended and another began. null and undefined become empty strings, so two nulls produce a lone comma, and an object becomes the useless "[object Object]". If you want a representation you can read or parse back, you want JSON.stringify.

Common patterns

Be explicit instead
If you meant to join, say so — the separator becomes visible.
const csv = items.join(',');
Serialise properly
The only form that survives a round trip.
const text = JSON.stringify(items);
Readable output for humans
Comma-space, and holes made explicit.
const label = items.map(x => x ?? '-').join(', ');

Examples

1. Commas, no brackets
[1, 2, 3].toString()
Returns
'1,2,3'
2. Nesting is flattened
[1, [2, [3, 4]]].toString()
Returns
'1,2,3,4'
3. Nulls become empty
[null, undefined].toString()
Returns
','
4. Objects are useless
[{a: 1}].toString()
Returns
'[object Object]'
5. Implicit on concat
'x' + [1, 2]
Returns
'x1,2'
6. JSON keeps the shape
JSON.stringify([1, [2]])
Returns
'[1,[2]]'

Pitfalls

1. Nested arrays flatten irreversibly
Every element is converted to a string in turn, and an array element converts by this same method — so nesting recurses away entirely. The result cannot tell you the original shape, which makes it worthless for anything you intend to read back.
Shape is gone
[1, [2, [3, 4]]].toString()
'1,2,3,4'
Keep the shape
JSON.stringify([1, [2, [3, 4]]])
'[1,[2,[3,4]]]'
2. It fires implicitly, where you did not ask for it
Template literals, string concatenation, alert, and object keys all coerce via toString. An array used where a string was expected does not throw — it quietly becomes a comma-joined string, so the bug shows up somewhere far away.
Silent coercion
const key = 'user:' + [1, 2];
'user:1,2'
Say what you mean
const key = 'user:' + items.join('-');
'user:1-2'
3. null, undefined and holes all become empty strings
They are not written out as "null" — they contribute nothing at all, leaving bare separators. Two adjacent nulls are indistinguishable from two adjacent empty strings, and from a hole in a sparse array.
Ambiguous
[null, undefined].toString()
',' // same as ['',''].toString()
Make them visible
[null, undefined].map(x => x ?? 'null').join(',')
'null,null'
4. It powers a loose-equality surprise
Comparing an array to a string with == coerces the array via toString first, so a one-element array can equal its own element. Another reason to use === everywhere.
Loosely equal
[1, 2] == '1,2'
true
Strict is false
[1, 2] === '1,2'
false
5. A Symbol element throws
Almost everything coerces silently, but Symbols refuse. One Symbol anywhere in the array turns an innocent template literal into a TypeError at runtime.
Throws
[Symbol('x')].toString()
TypeError: Cannot convert a Symbol value to a string
Convert explicitly
[Symbol('x')].map(String).join(',')
'Symbol(x)'

When to use

Use it
  • Almost never directly — it runs implicitly on coercion
  • A throwaway debug string for a flat array of primitives
  • Understanding why an array appeared inside a string you built
Reach for something else
  • You want a specific separator → join
  • You want to parse it back → JSON.stringify
  • The array is nested → the structure is destroyed
  • Elements may be null, undefined or objects → map them first

Notes

Complexity
O(n) over all elements, recursing into nested arrays
Return
A new string; the array is not modified
CPython impl
V8: Builtins-array-tostring.tq
Memory
Allocates the result string
Thread-safe
Single-threaded; the array is only read

FAQ

None, except that join lets you pick the separator and toString is locked to a comma. toString is what runs when an array is coerced to a string, and calling join(',') explicitly produces exactly the same output — with the advantage that the next reader can see the separator.

[1, 2].toString();   // '1,2'
[1, 2].join(',');    // '1,2'
[1, 2].join(' | ');  // '1 | 2'

History

ES1
Present from the first version of the language as the array string-coercion hook.
ES3
toLocaleString added alongside it, applying locale formatting per element.
ES5
Specified to delegate to join when join is callable, formalising the long-standing behaviour.