Методы примитивов в JavaScript
Узнайте, как примитивы JavaScript — строки, числа, boolean, символы и bigint — вызывают методы через временные объекты-обёртки, с примерами и типичными ошибками.
Введение в примитивы и объекты JavaScript
В JavaScript почти всё, что вы пишете, является либо примитивом, либо объектом. Понимание разницы между ними — и хитрого механизма, позволяющего примитивам вести себя как объекты, — это одна из основ языка.
Примитив — это простейшее, неделимое значение. Существует семь примитивных типов: числа, строки, boolean, undefined, null, символы и bigint. Объект — это коллекция свойств и методов.
Вот задача, которую решает эта страница: string является примитивом без методов, однако "hello".toUpperCase() работает. Как значение без методов может вызвать метод? Ответ — объекты-обёртки, и эта глава объясняет, как именно они работают.
На этой странице рассматривается, что делает примитивы особенными, как JavaScript кратковременно оборачивает их, чтобы можно было вызывать методы, какие методы доступны для каждого примитивного типа, и распространённая ошибка использования new с конструкторами примитивов.
Что делает примитивы уникальными
Примитивы отличаются от объектов в трёх важных отношениях.
1. Неизменяемость. После создания примитивное значение нельзя изменить. Операция над string никогда не редактирует оригинальную строку — она возвращает совершенно новую строку. Попытка присвоить значение символу ничего не даёт (а в строгом режиме выбрасывает исключение):
2. Хранение по значению. Примитив хранится непосредственно в переменной. При копировании копируется само значение, поэтому две переменные полностью независимы:
Объекты, напротив, хранятся по ссылке — копирование переменной копирует лишь указатель на тот же object.
3. Простота и лёгковесность. Примитив не несёт собственных свойств или методов, что делает его быстрым в создании и сравнении. boolean вроде let flag = true; — это просто значение, без накладных расходов, которые были бы у объекта.
Объекты в JavaScript: сравнение
Объекты — более сложная структура. В отличие от примитивов, они:
- Изменяемы — их содержимое можно менять после создания.
- Ссылочные типы — объекты хранятся и копируются по ссылке, а не по значению.
- Универсальны — они могут содержать функции, массивы и другие объекты.
Как примитивы вызывают методы: объекты-обёртки
Так как же работает "hello".toUpperCase(), если у строки нет методов? Когда вы обращаетесь к свойству или методу примитива, JavaScript:
- Создаёт временный объект-обёртку соответствующего типа, содержащий это примитивное значение.
- Считывает из этой обёртки запрошенный метод или свойство.
- Выполняет его и возвращает результат.
- Немедленно уничтожает объект-обёртку.
Весь этот процесс происходит за кулисами и сильно оптимизирован движком, поэтому он практически бесплатен. Главный вывод: сам примитив никогда не изменяется и никогда не превращается в object постоянно — обёртка существует лишь на время одного выражения.
Конструкторы-обёртки
Пять примитивных типов имеют соответствующий встроенный конструктор-обёртку, предоставляющий их методы:
- String — для строковых примитивов.
- Number — для числовых значений.
- Boolean — для boolean-значений.
- Symbol — для символов.
- BigInt — для bigint.
null и undefined не имеют никакой обёртки, поэтому обращение к свойству на них выбрасывает TypeError (например, null.toString() не работает).
Методы string
Строки предоставляют богатый набор методов через обёртку String. Вот пример, где несколько методов объединяются для приведения предложения к заглавному виду:
Обратите внимание: length — это свойство, а не метод, поэтому у него нет скобок. toUpperCase() возвращает новую строку и оставляет greeting неизменной, в соответствии с правилом неизменяемости выше.
Методы number
Числа получают методы от обёртки Number. Часто используется toFixed(), который округляет до заданного числа десятичных знаков:
Два момента, на которые стоит обратить внимание. Во-первых, toFixed() возвращает string, а не число — typeof (3.14159).toFixed(2) равно "string". Во-вторых, чтобы вызвать метод непосредственно на числовом литерале, нужны дополнительные скобки или пробел: 255..toString(16) тоже работает, но 255.toString(16) является синтаксической ошибкой, поскольку парсер читает первую точку как десятичную.
Методы boolean
Булевы значения могут использовать обёртку Boolean, чаще всего просто toString():
Методы Symbol
Символы, уникальный примитивный тип, получают методы от обёртки Symbol:
Здесь необходимо явно вызывать toString() — символы намеренно не выполняют автоматическое преобразование в string в строковых контекстах (например, "" + sym), что в противном случае выбросило бы TypeError.
Методы BigInt
BigInt, предназначенный для целых чисел, слишком больших для обычного типа Number, получает методы от обёртки BigInt:
Значение передаётся здесь как string, потому что запись его в виде числового литерала превысила бы диапазон безопасных целых чисел и привела бы к потере точности ещё до того, как BigInt его увидит.
Ловушка: никогда не используйте new с обёртками примитивов
JavaScript позволяет создать объект-обёртку вручную с помощью new Number(1), new String("x") или new Boolean(false) — но почти никогда не стоит этого делать. Обёртка, созданная с new, является настоящим object, а каждый object является истинным (truthy), включая тот, что оборачивает false:
Это классический источник ошибок. Вызов конструктора без new — совершенно нормален и полезен: Number("42"), String(123) и Boolean(value) выполняют обычное преобразование типов и возвращают примитивы, а не объекты.
Итоги
- Примитивы — это простые, неизменяемые значения, хранящиеся по значению; объекты — изменяемые и хранятся по ссылке.
- Вызов метода на примитиве работает потому, что JavaScript оборачивает его во временный object, выполняет метод, а затем уничтожает обёртку.
String,Number,Boolean,SymbolиBigIntпредоставляют методы;nullиundefinedне имеют обёртки и выбрасывают ошибку при обращении к свойству.- Используйте функции-обёртки без
newдля преобразования типов. Никогда не используйтеnewдля создания объектов-обёрток в обычном коде — они всегда истинны и ведут себя неожиданно.
Далее изучите методы каждого типа подробнее в разделах строки, числа и bigint.