Методы примитивов и боксинг в JavaScript
Maksym Pohribniak![](/file/0af02c915b505b5314002.png)
Из документации:
Примитив (значение примитивного типа, примитивный тип данных) это данные, которые не являются объектом и не имеют методов. В JavaScript 6 простых типов данных: string, number, boolean, null, undefined, symbol (новое в ECMAScript 2015).
...
Все примитивы неизменяемы (immutable), то есть они не могут быть изменены. Важно не путать сам примитив с переменной, которой присвоено значение примитивного типа.
Помните, что примитивный тип данных не является композиционным строительным блоком. По сути, это значение, и как таковое оно не имеет свойств. Однако рассмотрим следующий код:
var str = 'hello'; console.log(str.toUpperCase()); // --> HELLO
Похоже, что str явно имеет свойство toUpperCase. Может наш вывод был неверным? В частности, если strings не являются объектами, почему они имеют такие свойства, как toUpperCase, toLowerCase и т. д.?
Почему так происходит: создается временный объект-обёртка с использованием встроенных конструкторов, который предоставляет методы и после этого исчезает.
Примечание: эта концепция применима к числам (Number), стокам (String) и логическим значениям (Boolean).
По сути, это причина, по которой следующий фрагмент кода возвращает undefined:
var str = 'hello'; str.custom = 1; console.log(str.custom); // -> undefined
Рассмотрим это детальнее:
Step 1
var str = 'hello'; str.custom = 1;
JavaScript создает объект-обертку String, устанавливает в его свойство custom 1, а затем отбрасывает его. По сути, он работает примерно так:
var str = 'hello'; var temp = new String(str); // wrapper object temp.custom = 1;
Изучив переменную temp в Chrome Developer Toolbar или любом другом инструменте отладки, вы увидите что-то вроде следующего:
String: 0: "h" 1: "e" 2: "l" 3: "l" 4: "o" length: 5 custom: 1
Мы видим, что свойство custom устанавливается для переменной temp.
Step 2
console.log(str.custom);
И опять же, JavaScript создает объект-обертку String из исходного неизменяемого строкового значения , а затем пытается прочитать custom. Такого свойства, конечно, не существует, и выражение оценивается как неопределенное. И объект-обертка снова затем отбрасывается.
Точно так же, учитывая всю информацию выше, мы можем видеть, как
var str = 'hello'; var upper = str.toUpperCase(); console.log(upper); // --> HELLO
Интерпретируется как:
var upper = (new String(str)).toUpperCase()
Приведение типов
JavaScript при необходимости приводит объекты-обертки в значения примитивов.
Нестрогое (==) равенство будет обрабатывать объект-примитив и примитив как равные, а оператор строгого (===) равенства будет рассматривать их как разные объекты.
var a = 'hello'; // primitive var b = new String('hello'); // wrapper object typeof a; // "string" typeof b; // "object" a == b // true a === b // false
Числа
Тот же принцип применим к числам.
var x = 1; var y = new Number(1); typeof x; // "number" typeof y; // "object" x == y // true x === y // false
Однако, когда дело доходит до цифр, JavaScript дает вам больше свободы. Вы можете создавать свои собственные объекты, похожие на «числа», и позволить приведению типа разрешить любую числовую операцию за вас. Например:
var x = { num: 2, valueOf: function() { return this.num * 2; } } var y = { num: 3, valueOf: function() { return this.num * 2; } } console.log(x + y); // 10
Как видно из приведенного выше кода, мы не конвертировали явно x и y в числа, но смогли добавить их. Это потому, что сложение привело их к примитивным значениям. По сути, за кулисами JavaScript делал следующее:
var temp = Number(x) + Number(y); console.log(temp); // 10
Помните, что вызов конструктора Number оператора new в пытается преобразовать значение в его примитивное представление. То же самое относится к String и Boolean.
Любой объект в JS наследует по цепочке прототипов от Object
два метода — .valueOf
и .toString
. Как только объект подвергается операции, применимой только к примитивам (напр., сложение, сравнение и т.д.), то у него просто вызывается метод .valueOf
. Если этот метод не является собственным методом данного объекта, а унаследован по цепочке прототипов, то вызывается метод .toString
. Эта проверка продолжает идти вверх по цепочке прототипов, пока ей не встретится собственный метод объекта.
Боксинг
Боксинг (от англ. box – коробка) называется процесс замены примитивного типа соответствующим объектным типом.
Для трёх примитивных типов в JS определены так называемые, обёрточные О-типы:
string
↔String
number
↔Number
boolean
↔Boolean
Причина экспозирования обёрточного типа для boolean
вообще остаётся загадкой, т.к. для булевских значений в JS не определены никакие свойства.
function f() {
console.log(this);
console.log(typeof this);
}
f.call(2)
Number {2}
object
Явный пример когда срабатывает автоматический боксинг в JavaScript.
В нестрогом режиме примитив сначала оборачивается в объект и только потом записывается в this.