Знаете ли вы, что скрывает от ваших глаз javascript при работе с числами, строками или boolean'ами (aka данные примитивных типов)? Ниже находится вольный перевод познавательной статьи на эту тему.
Введение в типы javascript
Всем известно, что в javascript все данные представляется в виде объектов. Объекты состоят из произвольного набора свойств. Свойства могут ссылаться либо на объекты, либо на данные одного из из 5 примитивных типов (undefined, null, boolean, string, number). Благодаря умелой маскировке примитивных типов под объекты, мало кто знает, что данные примитивных типов не могут иметь свойств. Данные boolean, string и number могут быть обернуты в соответствующие объекты Boolean, String и Number. Посмотрите, какой тип возвращает typeof для следующих данных, чтобы иметь представление, что, к примеру, boolean и Boolean - это не одно и то же в javascript:
typeof true; //"boolean"
typeof Boolean(true); //"boolean"
typeof new Boolean(true); //"object"
typeof (new Boolean(true)).valueOf(); //"boolean"
typeof "abc"; //"string"
typeof String("abc"); //"string"
typeof new String("abc"); //"object"
typeof (new String("abc")).valueOf(); //"string"
typeof 123; //"number"
typeof Number(123); //"number"
typeof new Number(123); //"object"
typeof (new Number(123)).valueOf(); //"number"
Почему 'abc'.length возвращает значение, если примитивные типы не могут иметь свойств?
Потому что javascript производит автоматическую конвертацию типов у вас за спиной. В данном случае он преобразует строку 'abc' в объект String, возвращает значение свойства length для этого объекта, после чего только что созданный объект отправляется на съедение garbage collector'ом. Попробуем отловить этот короткоживущий объект:
String.prototype.returnMe= function() {
return this;
}
var a = "abc";
var b = a.returnMe();
a; //"abc"
typeof a; //"string" (still a primitive)
b; //"abc"
typeof b; //"object"
Как видно из вышеприведенного кода, returnMe() возвращает объект String, который был создан при обращении к этому методу.
(По непроверенным слухам в ECMAScript5 под strict mode returnMe() вернет строку 'abc' примитивного типа вместо объекта String).
Есть более короткий код, доказывающий существование автоматического преобразования типов в javascript:
Number.prototype.getType = function() {
return typeof this;
}
(123).getType(); //"object"
За счет дополнительных расходов памяти и процессорного времени, необходимого на автоматическое преобразование типов, мы можем обращаться к "свойствам" примитивных типов.
Могут ли объекты автоматически преобразовываться в данные примитивных типов?
Да. Почти. Объекты Boolean, Number и String являются фактически обертками над данными соответствующих примитивных типов. В основном они автоматически преобразуются к обернутым значениям в случае необходимости. Более подробно об этом можно прочесть в отдельной статье.
//object coerced to primitive
var Twelve = new Number(12);
var fifteen = Twelve + 3;
fifteen; //15
typeof fifteen; //"number" (primitive)
typeof Twelve; //"object"; (still object)
//another object coerced to primitive
new String("hippo") + "potamus"; //"hippopotamus"
//object not coerced (because 'typeof' operator can work with objects)
typeof (new String("hippo")) + "potamus"; //"objectpotamus"
К сожалению, объекты Boolean иногда ведут себя весьма странно. Например:
if (new Boolean(false)) {
alert("true???"); // this won't be executed
}
Обычно вы вынуждены явно вытягивать значение из объектов Boolean с помощью valueOf():
var a = "";
new Boolean(a).valueOf(); //false
Но на практике все немного проще:
var a = Boolean("");
a; //false
Или даже:
var a = "";
!!a; //false
Можно ли с помощью автоматического преобразования типов создавать свойства у значений примитивных типов?
Нет.
var primitive = "september";
primitive.vowels = 3;
primitive.vowels; //undefined;
При попытке создания свойства у примитивного типа javascript автоматически создаст временный объект-обертку для этого типа, создаст данное свойство у этого объекта, после чего объект будет уничтожен. Примитивный тип же останется нетронутым. Ниже приведен псевдокод того, что происходит у нас за спиной при выполнении вышеприведенного кода:
var primitive = "september";
primitive.vowels = 3;
//new object created to set property
(new String("september")).vowels = 3;
primitive.vowels;
//another new object created to retrieve property
(new String("september")).vowels; //undefined
Из этого можно сделать вывод, что операции обращения и присваивания "свойств" у примитивных типов являются не только бесполезными, но и затратными с точки зрения производительности и потребления ресурсов.
Заключение
Оказывается, что единственное преимущество объектов Boolean, Number и String перед соответствующими примитивными типами - это возможность работы со свойствами. На практике даже это преимущество является весьма сомнительным. Строки, числа и boolean'ы имеют устоявшуюся семантику, так что нет особой необходимости в объектах-обертках, позволяющих делать надстройки над этими типами. Более того, т.к. значения примитивных типов являются неизменяемыми, они не могут быть изменены посредством манипуляций со свойствами объекта-обертки:
var me = new String("Angus");
me.length = 2; //(error in strict mode)
me.length; //5 (not 2 - thanks @joseanpg)
me.valueOf(); "Angus"
Хорошее понимание отличий между примитивными типами и объектами в javascript, а также того, как и при каких обстоятельствах используется автоматическое преобразование типов, является важной ступенью к более глубокиму познанию данного языка. Надеюсь, эта статья показалась кому-нибудь интересной :)
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.