Методы примитивов и боксинг в JavaScript

Методы примитивов и боксинг в JavaScript

Maksym Pohribniak
Строки в JavaScript

Из документации:

Примитив (значение примитивного типа, примитивный тип данных) это данные, которые не являются объектом и не имеют методов. В JavaScript 6 простых типов данных: stringnumberbooleannullundefinedsymbol (новое в 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.

Report Page