Функция toString в языке JavaScript наверно самая "неявно" обсуждаемая как среди самих js-разработчиков, так и среди внешних наблюдателей. Она — причина многочисленных шуток и мемов про многие подозрительные арифметические операции, преобразования, вводящие в ступор 'ы. Уступает, возможно, лишь удивлениям при работе с float64. Интересные случаи, которые мне приходилось наблюдать, использовать или преодолевать, мотивировали меня написать настоящий разбор полетов. Мы галопом проскочим по спецификации языка и на примерах разберем неочевидные особенности toString.  Если вы ожидаете полезного и достаточного руководства, то вам больше подойдет этот, этот и тот материалы. Если же ваше любопытство все таки преобладает над прагматичностью, то прошу под кат. Функция toString — свойство объекта-прототипа Object, простыми словами — его метод. Используется при строковом преобразовании объекта и по-хорошему должна возвращать примитивное значение. Свои реализации также имеют объекты-прототипы: Function, Array, String, Boolean, Number, Symbol, Date, RegExp, Error. Если вы реализуете свой объект-прототип (класс), то хорошим тоном будет определить для него и toString. JavaScript — язык со слабой системой типов: а значит, позволяет нам смешивать разные типы, выполняет многие операции неявно. В преобразованиях toString работает в паре с valueOf, чтобы свести объект к нужному для операции примитиву. Например, оператор сложения оборачивается конкатенацией при наличии среди операторов хотя бы одной строки. Некоторые стандартные функции языка перед своей работой приводят аргумент к строке: parseInt, decodeURI, JSON.parse, btoa и проч. Про неявное приведение типов сказано и высмеяно уже довольно много. Мы же рассмотрим реализации toString ключевых объектов-прототипов языка. Если мы обратимся к соответствующему разделу спецификации, то обнаружим, что основная задача дефолтного toString — это получить так называемый tag для конкатенации в результирующую строку: Для этого: Болеющие рефлексией сразу отметят возможность получить тип объекта простой операцией (не рекомендуется спецификацией, но можно): Особенностью дефолтного toString является то, что он работает с любым значением this. Если это примитив, то он будет приведен к объекту (null и undefined проверяются отдельно). Никаких TypeError: Как это может пригодиться? Например, при разработке инструментов динамического анализа кода. Имея импровизированный пул используемых в процессе работы приложения переменных, в run-time можно собирать полезную однородную статистику. У этого подхода есть один существенный недостаток: пользовательские типы. Не трудно догадаться, что для их экземпляров мы просто получим "Object". ООП в JavaScript базируется на прототипах, а не на классах (как например в Java), и готового метода getClass() у нас нет. Решить возникшую проблему поможет явное определение символа toStringTag для пользовательского типа: или в прототипном стиле: Есть альтернативное решение через read-only свойство Function.name, которое пока не является частью спецификации, но поддерживается большинством браузеров. Каждый экземпляр объекта-прототипа/класса имеет ссылку на функцию-конструктор, с помощью которой он был создан. А значит мы можем узнать название типа: или в прототипном стиле: Разумеется, это решение не работает для объектов, созданных с помощью анонимной функции ("anonymous") или Object.create(null), а также для примитивов без объекта-обертки (null, undefined). Таким образом, для надежной манипуляции типами переменных стоит комбинировать известные приемы, в первую очередь отталкиваясь от решаемой задачи. В подавляющем большинстве случаев достаточно typeof и instanceof. Мы немного отвлеклись, но в результате добрались до функций, у которых есть свой интересный toString. Для начала взглянем на следующий код: Многие наверно догадались, что это пример куайна. Если загрузить скрипт с таким содержимым в тело страницы, то в консоль будет выведена точная копия исходного кода. Это происходит благодаря вызову toString от функции arguments.callee. Используемая реализация toString объекта-прототипа Function возвращает строковое представление исходного кода функции, сохраняя используемый при ее определении синтаксис: FunctionDeclaration, FunctionExpression, ClassDeclaration, ArrowFunction и проч. Например, мы имеем стрелочную функцию: Вызов bind.toString() вернет нам строковое представление ArrowFunction: А вызов toString от обернутой функции — это уже строковое представление FunctionExpression: Этот пример с bind не случаен, так как у нас есть готовое решение с привязкой контекста Function.prototype.bind, и касательно нативных bound functions есть особенность работы Function.prototype.toString с ними. В зависимости от реализации может быть получено представление как самой обернутой функции, так и оборачиваемой (target) функции. V8 и SpiderMonkey последних версий хрома и ff: Таким образом, стоит проявлять осторожность с нативно-декорируемыми функциями. Вариантов использования рассматриваемого toString очень много, но настоятельно только в качестве инструмента метапрограммирования или дебага. Наличие подобного в бизнес-логике типового приложения рано или поздно приведет к неподдерживаемому разбитому корыту. Самое простое, что приходит на ум — это определение длины функции: Расположение и количество пробельных символов результата toString отдается спецификацией на откуп конкретной реализации, поэтому для чистоты мы предварительно убираем лишнее, приводя к общему виду. К слову, в старых версиях движка Gecko у функции был специальный параметр indentation, помогающий с форматированием отступов. Сразу на ум приходит и определение имен параметров функции, что может пригодится для рефлексии: Это коленочное решение подойдет для синтаксиса FunctionDeclaration и FunctionExpression. Если потребуется более развернутое и точное, то рекомендую обратиться за примерами к исходному коду вашего любимого фреймворка, который наверняка имеет под капотом какое-нибудь внедрение зависимостей, основанное именно на именах объявленных параметров.  Опасный и интересный вариант переопределения функции через eval: Зная структуру исходной функции, мы создали новую, подменив используемый в ее теле перед аргументами оператор сложения — умножением. В случае программно-генерируемого кода или отсутствия интерфейса расширения функции это может быть волшебным образом полезно. Например, если вы исследуете некоторую математическую модель, подбирая подходящую функцию, играясь с операторами и коэффициентами. Более практичное использование — это компиляция и дистрибьюция шаблонов. Многие реализации шаблонизаторов компилируют исходный текст шаблона и предоставляют функцию от данных, которая уже формирует конечный HTML (или другое). Далее на примере функции _.template: А что, если компиляция шаблона требует аппаратных ресурсов или клиент ну очень тонкий? В этом случае мы можем компилировать шаблон на серверной стороне и отдавать клиентам уже не текст шаблона, а строковое представление готовой функции. Тем более, вам не нужно грузить на клиент библиотеку шаблонизатора. Теперь нам необходимо выполнить этот код на клиенте перед использованием. Чтобы при компиляции не было SyntaxError из-за синтаксиса FunctionExpression: или так: Или как вам больше нравится. В любом случае: Это может быть не самая лучшая практика компиляции шаблонов на серверной стороне и их дальнейшего распространения на клиенты. Просто пример с использованием связки Function.prototype.toString и eval. Наконец, старая задача про определение имени функции (до появления свойства Function.name) через toString: Разумеется, это хорошо работает в случае синтаксиса FunctionDeclaration. Более интеллектуальное решение потребует хитрого регулярного выражения или использования сопоставления с образцом. В интернетах полно интересных решений на базе Function.prototype.toString, достаточно лишь поинтересоваться. Делитесь своим опытом в комментариях: очень интересно. Реализация toString объекта-прототипа Array является обобщенной и может быть вызвана для любого объекта. Если объект имеет метод join, то результатом toString будет его вызов, иначе — Object.prototype.toString. Array, логично, имеет метод join, который конкатенирует строковое представление всех своих элементов через переданный в качестве параметра separator (по умолчанию это запятая). Допустим, нам надо написать функцию, сериализующую список своих аргументов. Если все параметры — примитивы, то во многих случаях мы можем обойтись без JSON.stringify: или так: Только помните, что строка '10' и число 10 будут сериализованы одинаково. В задаче про кратчайший мемоизатор на одном из этапом использовалось это решение. Нативный джойн элементов массива работает через арифметический цикл от 0 до length и не фильтрует отсутствующие элементы (null и undefined). Вместо этого происходит конкатенация с separator. Это приводит к следующему: Поэтому, если вы по той или иной причине добавляете в массив элемент с большим индексом (например, это сгенерированный натуральный id), ни в коем случае не джойните и, соответственно, не приводите к строке без предварительной подготовки. Иначе могут быть последствия: Invalid string length, out of memory или просто повисший скрипт. Используйте функции объекта Object values и keys, чтобы итерироваться только по собственным перечислимым свойствам объекта: Но гораздо лучше избегать подобного обращения с массивом: скорее всего в качестве хранилища вам подошел бы простой key-value объект. К слову, такая же опасность есть и при сериализации через JSON.stringify. Только еще серьезнее, так как пустые и неподдерживаемые элементы представлены уже как "null": Завершая раздел хотелось бы напомнить, что вы можете определить для пользовательского типа свой метод join и вызывать Array.prototype.toString.call в качестве альтернативного приведения к строке, но я сомневаюсь, что это имеет какое-то практическое применение. Одна из моих любимых задач для js-викторин — Что вернет следующий вызов parseInt? Первое, что делает parseInt — это неявное приведение аргумента к строке через вызов абстрактной функции ToString, которая в зависимости от типа аргумента выполняет нужную ветку приведения. Для типа number осуществляется следующее: Я не буду дублировать здесь алгоритм определения предпочтительной формы, только отмечу следующее: если количество цифр числа в десятичной записи превышает 21, то будет выбрана экспоненциальная форма. А это значит, что в нашем случае parseInt работает не с "100...000" а с "1e30". Поэтому ответ совсем не ожидаемый 2^30. Кто знает природу этого магического числа 21 — пишите! Далее parseInt смотрит на используемое основание системы счисления radix (по умолчанию 10, у нас — 2) и проверяет символы полученной строки на совместимость с ним. Встретив 'e', отсекает весь хвост, оставляя только "1". Результатом же будет целое число, полученное путем перевода из системы с основанием radix в десятичную — в нашем случае, это 1. Обратная процедура: Здесь происходит вызов функции toString от объекта-прототипа Number, который использует тот же алгоритм приведения number к строке. Он тоже имеет опциональный параметр radix. Только он бросает RangeError для невалидного значения (должно быть целое от 2 до 36 включительно), тогда как parseInt возвращает NaN.  Стоит помнить о верхней границе системы счисления, если планируете реализовывать экзотическую хэш-функцию: вам может не подойти этот toString. Задачка, чтобы отвлечься на минутку: Что вернет и как исправить? Мы рассмотрели toString далеко не всех даже нативных объектов-прототипов. Отчасти, потому что лично мне не приходилось попадать с ними в передряги, да и интересного в них не много. Также мы не затронули функцию toLocaleString, так как про нее хорошо бы поговорить отдельно. Если я все таки что-то зря обделил вниманием, упустил из виду или недопонял — обязательно пишите! Приведенные мною примеры ни в коем случае не являются готовыми рецептами — только пищей для размышлений. Кроме того я нахожу бессмысленным и немного бестолковым обсуждать подобное на технических собеседованиях: для этого есть вечные темы про замыкания, хойстинг, event loop, паттерны модуль/фасад/медиатор и "конечно" вопросы про [используемый фреймворк].  Настоящая статья получилась сборной солянкой, и я надеюсь вы нашли что-то интересное для себя. PS Язык JavaScript — удивителен! Подготавливая настоящий материал к публикации, я пользовался Google Переводчиком. И совершенно случайно обнаружил занимательный эффект. Если выбрать перевод с русского на английский, ввести "toString" и начать его стирать через клавишу Backspace, то мы будем наблюдать:  Такая вот ирония! Думаю, я далеко не первый такой, но на всякий случай отправил им скриншот со сценарием воспроизведения. Выглядит как безобидный self-XSS, поэтому и делюсь. JavaScript Apologist