toString() turns a value into a string. Almost every value has one, and JavaScript quietly calls it for you whenever a value has to become text: when you join it to a string, put it in a template literal, or show it on a page. Knowing what each type produces explains a lot of strange-looking output.
On a number, toString() returns the digits as text. Give it a radix (base) from 2 to 36 to write the number in another base:
const x = 123;
console.log(x.toString()); // base 10
console.log(x.toString(2)); // binary
console.log((255).toString(16)); // hexadecimal
console.log((-10).toString(2));
To go back, parseInt takes the same base: parseInt("1111011", 2) is 123. A base outside 2 to 36 is an error: (10).toString(1) is RangeError: toString() radix argument must be between 2 and 36.
123.toString() is SyntaxError: Invalid or unexpected token. JavaScript reads 123. as the start of a decimal number. Wrap the number in parentheses, (123).toString(), or use a variable.
An array becomes its items separated by commas, with no spaces. Nested arrays are flattened, and null and undefined become empty:
const fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log(fruits.toString());
console.log([1, [2, 3]].toString());
console.log([null, undefined, 1].toString());
console.log(fruits.join(" | ")); // join lets you pick the separator
A date becomes a long, human-readable string:
const d = new Date(2026, 0, 5, 14, 30);
console.log(d.toString().slice(0, 24));
console.log(d.toDateString());
The full d.toString() continues with your time zone, like GMT-0500 (Eastern Standard Time), so it differs from computer to computer. For display, toLocaleString() (next lesson) gives you much more control.
function add(a, b) { return a + b; }
console.log(true.toString(), false.toString());
console.log(add.toString());
console.log(((x) => x * 2).toString());
console.log(Math.max.toString());
A function gives back its own source code. Built-in functions are written inside the browser, not in JavaScript, so they only show [native code].
A plain object has a toString(), but it is not useful (you met this in Object Display):
const person = { firstname: "John", lastname: "Doe" };
console.log(person.toString());
console.log("Hello " + person);
Give an object its own toString method, and JavaScript uses it every time the object has to become text:
const person = {
firstname: "John",
lastname: "Doe",
toString() {
return this.firstname + " " + this.lastname;
}
};
console.log(person.toString());
console.log("Hello " + person);
console.log(`Welcome, ${person}!`);
It works the same in a class, and then every object made from the class prints nicely:
class Temp {
constructor(c) { this.c = c; }
toString() { return this.c + "°C"; }
}
const readings = [new Temp(21), new Temp(5)];
console.log(`Now: ${readings[0]}`);
console.log(readings.join(", "));
toString() "can be overridden in the object definition," but its example never overrides it: the person it shows has no toString method, so the result is just [object Object]. The example above is what overriding looks like.
They are the two values with no properties at all, so calling a method on them crashes. String() handles every value safely:
console.log(String(null), String(undefined));
console.log(null.toString());
| Way | Handles | Use when |
|---|---|---|
| x.toString() | Throws for null and undefined | You know x is a real value, or you need a radix |
| String(x) | Works for every value | Safe general conversion |
| `${x}` or "" + x | Works except for Symbols | Building a larger string |
String(x) when you just need text. Use toString() when you want its extra power: a radix for numbers, or your own custom method on an object.
What does this log?
console.log([1, [2, [3]], null].toString());
1,2,3,. Nested arrays are flattened into the list, and null becomes an empty item, which leaves a trailing comma.Which one throws a TypeError?
String(null)`${null}`null.toString()null + ""null has no methods. The other three all produce the text "null".(255).toString(16) gives "ff". How do you turn "ff" back into 255?
parseInt("ff", 16). The second argument tells parseInt the text is in base 16.[1, [2, 3], [], null].toString() and explain each commatoString method and use it inside a template literalsafeText(x) that never crashes, even for null and undefined