Тайният живот на примитивите в JavaScript

Може и да не знаете, но в JavaScript всеки път, когато работите със стрингов, числов или булев примитив, навлизате в скрития свят на обектите с техните сенки и насилие. Затова, изтупайте праха от принадлежностите на своя Шерлок Холмс и четете нататък…

Основите

Обектите са съвкупности от свойства. Свойството може да се отнася до обект или до примитив. Примитивите са стойности, те нямат свойства.

В JavaScript има 5 примитивни типа: undefined, null, boolean, string и number. Всичко друго е обект. Примитивните типове, които са булева стойност, стринг или число, могат да се обграждат от своите обектни копия. Тези обекти са съответни инстанции на булев, стрингов или числов конструктор.

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 с готовност налага преминаването от примитиви към обекти. В случая стринговата стойност се предава на стрингов обект, за да се достъпи свойството length. Стринговият обект се използва само за части от секундата, след което пада в жертва на бога на боклуците (garbage collection-а) – но точно в духа на discovery канала по телевизията, ние ще уловим създанието, преди да се е изплъзнало, за да го подложим на по-нататъшен анализ…

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"

…и както при добре познатите научни изследвания, се натъкваме на естествения ход на нещата и пазим обекта от изхвърляне само за периода, в който факторът b също ще е в района. Хайзенберг си е жив и е съвсем добре. 😉

(обърнете внимание, че при strict mode създанието се изплъзва)

Ето един повече свързан със средата пример, който потвърждава типа на обекта, без да намесваме боклуджията (garbage collection-а):

Number.prototype.toString = function() {
    return typeof this;
}

(123).toString(); //"object"

По този начин, примитивите имат достъп до всички свойства (включително до методите), както са зададени от съответните конструктори на обектите.

Но тези обекти могат също да се сведат до стойности?

Да. Повечето. Обектите от този тип са просто опаковки, тяхната стойност е примитивът, който обграждат и като цяло те ще бъдат сведени до неговата стойност, когато е необходимо. За повече подробности, вижте статията JavaScript and valueOf.

//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"

За жалост, булевите обекти не се подчиняват така лесно. И, за да добавим още сол в раната, булевият обект се приравнява до true, освен ако стойността му не е null или undefined. Пробвайте например с това:

if(new Boolean(false)) {
    alert("true???");
}

Обичайно се налага да попитате изрично за стойността на булевите обекти. Следващото може да ви е от полза при определянето дали стойността е „истинна“ или „лъжлива“…

var a = "";
new Boolean(a).valueOf(); //false

…в практиката обаче е по-лесно да го проверите така:

var a = Boolean("");
a; //false

…и даже така:

vara = "";
!!a; //false

Възможността насила да променяме позволява ли ни да задаваме стойности на примитивите?

Не.

var primitive = "september";
primitive.vowels = 3;

primitive.vowels; //undefined;

Ако JavaScript засече опит да се задава свойство на примитив, наистина насила ще превърне примитива в обект. Но, както и при предните примери, този нов обект няма референции и веднага ще се превърне в храна за боклуджията.

Ето една псевдо репрезентация в код на горното, за да илюстрираме какво се случва всъщност:

var primitive = "september";
primitive.vowels = 3;
//new object created to set property
(newString("september")).vowels = 3;

primitive.vowels;
//another new object created to retrieve property
(newString("september")).vowels; //undefined

Както и сами ще забележите, не само е безсмислено, но си е направо загуба на време.

За финал

Оказва се, че възможността за приписване на свойства е в общи линии единственото предимство на обектите над техните примитивни копия, но на практика дори то е със съмнително качество. Стринговете, булевите стойности и числата имат специфични и добре определени цели и предефинирането им като титуляри по-вероятно ще обърка народа. Още повече, че примитивите са неизменчиви и не можете да ги модифицирате, пощипвайки свойствата на обекта, който ги обгръща:

var me = new String("Angus");
me.length = 2; //(error in strict mode)
me.length; //5 (not 2 - thanks @joseanpg)
me.valueOf(); "Angus"

И все пак, наистина си мисля, че едно добро разбиране за примитивите и за това, което се случва под повърхността, когато работите с тях, е важна крачка към едно по-задълбочено познаване на езика. Дано написаното тук да е помогнало.

JavaScript, JavaScript...

You may not know it but, in JavaScript, whenever you interact with string, number or boolean primitives you enter a hidden world of object shadows and coercion. So dust off your Sherlock Holmes outfit and read on…

View original post 535 more words

Advertisements

Вашият коментар

Попълнете полетата по-долу или кликнете върху икона, за да влезете:

WordPress.com лого

You are commenting using your WordPress.com account. Log Out / Промяна )

Twitter picture

You are commenting using your Twitter account. Log Out / Промяна )

Facebook photo

You are commenting using your Facebook account. Log Out / Промяна )

Google+ photo

You are commenting using your Google+ account. Log Out / Промяна )

Connecting to %s