Язык программирования javascript (JS) — это мультипарадигменный язык программирования. Обучающая статья посвящена языку программирования javascript (JS), хотя вернее будет сказать – описывает работу примитивов javascript. В материале рассматриваются основные принципы работы примитивных типов данных в javascript и, в дополнение, то, что происходит под прикрытием. С использованием методики принуждения примитивов к объектам был выполнен анализ работы JS Engine и свойств примитивов. Автором была изучена способность назначать свойства объектов перед их примитивными аналогами.
На основании полученных данных установлена способность назначать свойства — почти единственное преимущество объектов. Если javascript обнаруживает попытку назначить свойство примитиву, Js Engine приведет его к объекту. Но, этот новый объект не имеет ссылок и сразу станет кормом для сборки мусора. Хорошее понимание примитивов и того, что происходит «под капотом», является важным шагом на пути к более глубокому изучению языка.
Язык программирования javascript — примитивы javascript и их тайная жизнь
Предположу, что для вас будет в новинку факт того, что в JavaScript, взаимодействуя со строковыми, числовыми или логическими примитивами, вы попадаете в скрытый мир Зазеркалья.
Язык программирования javascript — типы данных
Логические, числовые и строковые примитивы могут быть обернуты их объектными аналогами. Такие объекты станут экземплярами Boolean, String и Number конструкторов объектов соответственно.
Пример использование конструктора new:
typeof new Boolean(false); // return «object»
typeof (new Boolean(false)).valueOf(); // return «boolean»
typeof false; //»boolean»
typeof Boolean(false); // return «boolean»
typeof new String(«word»); // return «object»
typeof (new String(«word «)).valueOf(); // return «string»
typeof » word «; // return «string»
typeof String(«word «); // return «string»
typeof 321; // return «number»
typeof Number(321); //return «number»
typeof new Number(431); // return «object»
typeof (new Number(431)).valueOf(); // return «number»
Если примитивы не имеют свойств, почему «string» .length возвращает значение?
Все просто – JavaScript насильно принуждает примитив стать объектом. В этом случае строковое значение приводится к строковому объекту для доступа к свойствам наподобие length (длина). Строковый объект используется исключительно на доли секунды, после чего приносится в жертву богам мусоросборки.
a; //»cba»
typeof a; //» return string» (still a primitive)
b; //»cba»
typeof b; // return «object»
Идем дальше. Как и во всех значимых работах, мы выворачивали наизнанку устоявшееся положение дел, не позволяя объекту собирать мусор, пока присутствует b.
(! В строгом режиме неуловимое существо устраняется)
Безусловно, существуют варианты проверить тип объекта, не затрагивая сборку мусора:
(123).toString(); // return “object»
Здесь можно отметить однозначный факт – примитивы имеют доступ ко всем свойствам (включая методы), определенным соответствующими конструкторами объектов.
JavaScript и valueOf
В JavaScript, valueOf и toString являются дочерними методами, перешедшими по наследству каждому объекту. Один из этих методов будет вызываться всякий раз, когда выражение встречает сложный объект, где ожидалось примитивное значение. Например:
alert(myCat);
let result = 2 + myCat;
Подведем черту. Если выражение намекает на необходимость строки, вызывается toString, в противном случае – valueOf. Если один из методов возвращает не примитив, шанс получает другой. В примерах выше ожидается, что myCat будет и строкой, и числом, поэтому они будут оцениваться как:
alert(myCat.toString()); //интерпретатор ожидал строку
let result = 2 + myCat.valueOf(); //ожидал число
Основываясь на этих правилах, вполне логично, что valueOf будет возвращать не строковое представление объекта.
Существует возможность воспользоваться значением valueOf для создания сжатого синтаксиса текущей даты, выраженной в миллисекундах:
(new Date()).valueOf(); //1588962243724 (date in ms)
+ new Date();//15889622488
+ 95(ожидается нестроковый примитив после знака+)
+new Date; //1588962256511 (то же самое, но даже более короткий синтаксис)
Если есть необходимость накатывать собственные показатели профилирования, можно воспользоваться информацией, представленной далее.
Большинство других реализаций valueOf по умолчанию не интересны:
Boolean(true).valueOf(); //true
Number(‘123’).valueOf(); //123
«aaa».valueOf(); //»aaa»
Но ведь куда интереснее определить свои собственные значения реализаций, не так ли?
var toDollarRate = {
rubles: 0.014,
pounds: 1.5
}
var Drink = function(name, cost, currency) {
this.name = name;
this.cost = cost;
this.currency = currency;
}
Drink.prototype.costInDollars = function() {
return this.cost * (toDollarRate[this.currency] || 1);
}
var boddingtons = new Drink(«Boddingtons», 2.50, ‘pounds’);
var peroni = new Drink(«Peroni», 3.50, ‘rubles’);
var anchorSteam = new Drink(«Anchor Steam», 3.50, ‘dollars’);
Drink.prototype.valueOf = Drink.prototype.costInDollars;
‘$’ + (boddingtons + peroni + anchorSteam).toFixed(2); //$7.30
Теоретически, приводя объект в логическое значение, он может представлять запрос, потенциально заканчивающийся как положительным, так и отрицательным результатом.
var SystemRequest = function(name) {
this.name = name;
}
SystemRequest.prototype.run = function() {
//симуляция результатов теста
this.success = Math.random(1)>0.5;
return this;
}
SystemRequest.prototype.valueOf = function() {
return this.success;
}
var request1 = new SystemRequest(‘request1’);
var request2 = new SystemRequest(‘request2’);
var request3 = new SystemRequest(‘request3’);
request1.run() + request2.run() + request3.run(); //2
request1.run() + request2.run() + request3.run(); //1
request1.run() + request2.run() + request3.run(); //3 (all passed!)
Здесь valueOf возвращает логическое значение, но в инструкциях окончательного выполнения используется конкатенация для преобразования логических значений в числа (1 для передачи, 0 для отказа).
Неплохим подспорьем может служить переопределение valueOf. И совершенно не имеет значения, использовать его так, или нет, знание того, как и почему JavaScript выбирает методы по умолчанию toString и valueOf, поможет вам лучше узнать код.
Язык программирования javascript — могут ли эти объекты быть приведены к значениям?
Вполне. Объекты этого типа являются не более чем обертками, их значение – примитив, который они обертывают, и чаще всего приводят к этому значению по мере необходимости.
//объект приведен к примитиву
var Twelve = new Number(12);
var fifteen = Twelve + 3;
fifteen; //15
typeof fifteen; //»number» (примитиву)
typeof Twelve; //»object»; (все еще объект)
//другой объект приведен к примитиву
new String(«salli») + «slet»; //» sallister»
//объект не приведен (потому что оператор typeof может работать с объектами)
typeof new String(«salli «) + » slet «; //»object slet »
Увы, Boolean объекты приводятся не так легко. И, чтобы провернуть нож в ране, Boolean объект оценивается как true, если его значение не равно null или undefined. Можно воспользоваться:
if (new Boolean(true)) {
alert(«false???»);
}
Чаще всего имеет смысл явно запрашивать логические объекты для их значения. Следующее поможет определить, является значение «truthy» или «falsey»….
var empty = «»;
new Boolean(empty).valueOf(); //false
… но на практике легче сделать так…
var empty = Boolean(«»);
empty; //false
… или даже так …
var empty = «»;
!!empty; //false
Язык программирования javascript — позволяет ли принуждение присваивать значения примитивам?
Нет
const monthNum = «october «;
primitive.monthNum = 11;
primitive.monthNum; //undefined;
Если JavaScript обнаруживает попытку назначить свойство примитиву, он действительно приведет к примитиву объекта. Но, как и в предыдущих примерах, этот новый объект не имеет ссылок и сразу пойдет в сборщик мусора.
Вот псевдокодовое представление того же примера, чтобы
проиллюстрировать, что на самом деле происходитconst primitive = «october»;
primitive.monthNum = 11;
// новый объект, созданный для установки свойства(new String(«october «)). monthNum = 11;
primitive.monthNum;
// еще один новый объект, созданный для получения свойства(new String(«october»)).monthNum; //undefined
Заметим, все это не только бесполезно, но и довольно иррационально.
В результате
Оказывается, что способность назначать свойства — почти единственное преимущество объектов перед их примитивными аналогами. Строки, логические значения и числа имеют конкретные и четко определенные цели.
Тем не менее хорошее понимание примитивов и того, что происходит “под капотом” когда вы взаимодействуете с ними, является важным шагом на пути к более глубокому изучению языка.