The `toString()` Function in JavaScript
Most JavaScript objects and primitive values have a toString() function that converts the value to a string. Many built-in methods use toString() under the hood, like the browser’s alert() function.
Primitives
JavaScript number primitives have a toString() function that converts the number to a string. This is one of the most common uses for toString() :
const num = 42; num.toString(); // '42' typeof num.toString(); // 'string' // Can also use `toString()` on a number literal as long as you // use parentheses. (42).toString(); // '42'
All primitive values except null and undefined have a toString() function: strings, numbers, booleans, BigInts, and symbols.
// String: 'Hello'.toString(); // 'Hello' // Number: (42).toString(); // '42' // Boolean: true.toString(); // 'true' // BigInt: 42n.toString(); // '42' // Symbol: Symbol('test').toString(); // 'Symbol(test)'
The important takeaway is that it is safe to call toString() on an arbitrary JavaScript value as long as that value is not null or undefined . The easiest way to check is to use == null : the most common use for == is that v == null is shorthand for v === null || v === undefined .
if (v != null) < // Won't throw an error, unless you wrote a custom `toString()` that throws v.toString(); >
Objects
The Object class in JavaScript is the base class for all objects, and it has a simple toString() method that usually prints [object Object] :
// Equivalent to `const obj = <>;` const obj = new Object(); obj.toString(); // '[object Object]'
The [object Object] output is often confusing to beginners because they want to see the object’s keys and values. You can loop over the object’s keys and values yourself, but the easiest one-liner is to use JSON.stringify() .
const obj = < name: 'Jean-Luc Picard', rank: 'Captain' >; // '' console.log(JSON.stringify(obj));
If you define a JavaScript class, you can overwrite the toString() function to return whatever you want:
class MyClass < toString() < return 'Hello, World!'; > > const obj = new MyClass(); obj.toString(); // 'Hello, World!'
toString() Parameters
Some toString() functions take parameters, most notably numbers and Node.js buffers.
The toString() function for JavaScript numbers takes a radix parameter that defines the base of the numeral system. In other words, num.toString(2) converts the number to a binary number string, num.toString(10) converts the number to a base-10 string, and num.toString(16) converts the number to a hexadecimal string.
(3).toString(2); // '11' (42).toString(10); // '42' (29).toString(16); // '1d'
The Node.js buffer toString() function takes an encoding parameter that is usually one of ‘utf8’, ‘hex’, or ‘base64’. This determines how the raw data in the buffer is encoded.
const fs = require('fs'); const buf = fs.readFileSync('./package.json'); buf.toString('utf8'); // '< "name": "masteringjs.io", . >'
More Fundamentals Tutorials
Преобразование объектов: toString и valueOf
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/object-toprimitive.
Ранее, в главе Преобразование типов для примитивов мы рассматривали преобразование типов для примитивов. Теперь добавим в нашу картину мира объекты.
Бывают операции, при которых объект должен быть преобразован в примитив.
- Строковое преобразование – если объект выводится через alert(obj) .
- Численное преобразование – при арифметических операциях, сравнении с примитивом.
- Логическое преобразование – при if(obj) и других логических операциях.
Рассмотрим эти преобразования по очереди.
Логическое преобразование
Проще всего – с логическим преобразованием.
Любой объект в логическом контексте – true , даже если это пустой массив [] или объект <> .
Строковое преобразование
Строковое преобразование проще всего увидеть, если вывести объект при помощи alert :
var user = < firstName: 'Василий' >; alert( user ); // [object Object]
Как видно, содержимое объекта не вывелось. Это потому, что стандартным строковым представлением пользовательского объекта является строка «[object Object]» .
Такой вывод объекта не содержит интересной информации. Поэтому имеет смысл его поменять на что-то более полезное.
Если в объекте присутствует метод toString , который возвращает примитив, то он используется для преобразования.
var user = < firstName: 'Василий', toString: function() < return 'Пользователь ' + this.firstName; >>; alert( user ); // Пользователь Василий
Метод toString не обязан возвращать именно строку.
Его результат может быть любого примитивного типа. Например, это может быть число, как в примере ниже:
var obj = < toString: function() < return 123; >>; alert( obj ); // 123
Поэтому мы и называем его здесь «строковое преобразование», а не «преобразование к строке».
Все объекты, включая встроенные, имеют свои реализации метода toString , например:
alert( [1, 2] ); // toString для массивов выводит список элементов "1,2" alert( new Date ); // toString для дат выводит дату в виде строки alert( function() <> ); // toString для функции выводит её код
Численное преобразование
Для численного преобразования объекта используется метод valueOf , а если его нет – то toString :
var room = < number: 777, valueOf: function() < return this.number; >, toString: function() < return this.number; >>; alert( +room ); // 777, вызвался valueOf delete room.valueOf; // valueOf удалён alert( +room ); // 777, вызвался toString
Метод valueOf обязан возвращать примитивное значение, иначе его результат будет проигнорирован. При этом – не обязательно числовое.
У большинства встроенных объектов такого valueOf нет, поэтому численное и строковое преобразования для них работают одинаково.
Исключением является объект Date , который поддерживает оба типа преобразований:
alert( new Date() ); // toString: Дата в виде читаемой строки alert( +new Date() ); // valueOf: кол-во миллисекунд, прошедших с 01.01.1970
Если посмотреть в стандарт, то в пункте 15.2.4.4 говорится о том, что valueOf есть у любых объектов. Но он ничего не делает, просто возвращает сам объект (непримитивное значение!), а потому игнорируется.
Две стадии преобразования
Итак, объект преобразован в примитив при помощи toString или valueOf .
Но на этом преобразования не обязательно заканчиваются. Вполне возможно, что в процессе вычислений этот примитив будет преобразован во что-то другое.
Например, рассмотрим применение к объекту операции == :
var obj = < valueOf: function() < return 1; >>; alert( obj == true ); // true
Объект obj был сначала преобразован в примитив, используя численное преобразование, получилось 1 == true .
Далее, так как значения всё ещё разных типов, применяются правила преобразования примитивов, результат: true .
То же самое – при сложении с объектом при помощи + :
var obj = < valueOf: function() < return 1; >>; alert( obj + "test" ); // 1test
Или вот, для разности объектов:
var a = < valueOf: function() < return "1"; >>; var b = < valueOf: function() < return "2"; >>; alert( a + b ); // "12" alert( a - b ); // "1" - "2" = -1
Объект Date по историческим причинам является исключением.
Бинарный оператор плюс + обычно использует численное преобразование и метод valueOf . Как мы уже знаем, если подходящего valueOf нет (а его нет у большинства объектов), то используется toString , так что в итоге преобразование происходит к строке. Но если есть valueOf , то используется valueOf . Выше в примере как раз a + b это демонстрируют.
У объектов Date есть и valueOf – возвращает количество миллисекунд, и toString – возвращает строку с датой.
…Но оператор + для Date использует именно toString (хотя должен бы valueOf ).
// бинарный плюс для даты toString, для остальных объектов valueOf alert( new Date + "" ); // "строка даты"
Других подобных исключений нет.
В языке Java (это не JavaScript, другой язык, здесь приведён для примера) логические значения можно создавать, используя синтаксис new Boolean(true/false) , например new Boolean(true) .
В JavaScript тоже есть подобная возможность, которая возвращает «объектную обёртку» для логического значения.
Эта возможность давно существует лишь для совместимости, она и не используется на практике, поскольку приводит к странным результатам. Некоторые из них могут сильно удивить человека, не привыкшего к JavaScript, например:
var value = new Boolean(false); if (value) < alert( true ); // сработает! >
Почему запустился alert ? Ведь в if находится false … Проверим:
var value = new Boolean(false); alert( value ); // выводит false, все ок.. if (value) < alert( true ); // ..но тогда почему выполняется alert в if . >
Дело в том, что new Boolean – это не примитивное значение, а объект. Поэтому в логическом контексте он преобразуется к true , в результате работает первый пример.
А второй пример вызывает alert , который преобразует объект к строке, и он становится «false» .
В JavaScript вызовы new Boolean/String/Number не используются, а используются простые вызовы соответствующих функций, они преобразуют значение в примитив нужного типа, например Boolean(val) === !!val .
Итого
- В логическом контексте объект – всегда true .
- При строковом преобразовании объекта используется его метод toString . Он должен возвращать примитивное значение, причём не обязательно именно строку.
- Для численного преобразования используется метод valueOf , который также может возвратить любое примитивное значение. У большинства объектов valueOf не работает (возвращает сам объект и потому игнорируется), при этом для численного преобразования используется toString .
Полный алгоритм преобразований есть в спецификации ECMAScript, смотрите пункты 11.8.5, 11.9.3, а также 9.1 и 9.3.
Заметим, для полноты картины, что некоторые тесты знаний в интернет предлагают вопросы типа:
Если вы запустите эти выражения в консоли, то результат может показаться странным. Подвох здесь в том, что если фигурные скобки <. >идут не в выражении, а в основном потоке кода, то JavaScript считает, что это не объект, а «блок кода» (как if , for , но без оператора просто группировка команд вместе используется редко).
А если команду изъять, то будет пустой блок <> , который ничего не делает. Два примера выше как раз содержат пустой блок в начале, который ничего не делает. Иначе говоря:
<>[0] // то же что и: [0] <> + <> // то же что и: + <>
То есть, такие вопросы – не на преобразование типов, а на понимание, что если < . >находится вне выражений, то это не объект, а блок.