Глава 1
Стойности, типове и оператори.

Под повърхността на устройството, програмата се движи. Без усилие тя се разширява и свива. В голяма хармония, електрони се разпръскват и групират. Формите на монитора са, като вълни във водата. Същността остава невидима от долу.”

Master Yuan-Ma, Книга за програмиране

Във вътрешния свят на компютъра има само данни. Можете да четете данни, да променяте и създавате данни, но всичко, което не е данни просто не съществува. Всички тези данни се съхраняват като дълги поредици от битове и всичко е основано на това.

Бит е двустойностно нещо обикновено се описва, като нули и единици. Във вътрешността на компютъра, те са под формата на по-висок или нисък електрически заряд, силен или слаб сигнал, лъскаво или матово място по повърхността на CD. Всяко парче от обособена информация може да се намали до последователност от нули и единици представени в битове.

На пример, как може да се представи числото 13 в битове. То работи по същия начин, като десетичните числа, но вместо 10 различни цифри имаме само 2, а степента на всяка се увеличава с коефициент 2 от дясно на ляво. Това са битовете, които съхраняват числото 13 със степента на цифрите показани по-долу от тях:

   0   0   0   0   1   1   0   1
 128  64  32  16   8   4   2   1

Така че, това е бинарен номер 00001101 или 8+4+1, което се равнява на 13.

Стойности

Представете си море от битове. Океан от тях. Един типичен модерен компютър има повече от 30 милиарда бита в своите летливно съхранени данни. Nonvolatile storage (твърд диск или еквивалент) има тенденция да има няколко магнитута повече.

The Ocean of Bits

За да може да се работи с такива количества битове без да се обърква, се разделят на парчета, които представляват части от информацията. В средата на JavaScript, тези парчета се наричат стойности. Въпреки, че всички стойности са направени от битове, те играят различни роли. Всяка стойност има тип, който определя неговата роля. Има шест основни типа стойности в JavaScript: numbers, strings, Booleans, objects, functions и undefined.

За създаване на стойност, просто трябва да се позове на името си. Това е удобно. На вас не ви се налага да изграждате вашите стойности. Просто извикайте една и хоп я има. Те не са създадени от нищото, разбира се. Всяка стойност трябва да се съхранява някъде. И ако искате да използвате една гигантска сума от тях едновременно, може да изчерпите наличните битове. За щастие, това е проблем само ако имате нужда от всички тях едновременно. Веднага след, като вече не използвате стойност, тя ще се разсее, оставяйки след себе си битове, които се рециклират, като строителен материал за следващото поколение стойности.

Тази глава ни запознава с атомните елементи на програмите в JavaScript, това са видовете прости стойности и операторите, които действат върху тях.

Числа

Стойносттите на вида number са изненадващо, цифрови стойности. В програмите на JavaScript, се записват както следва:

13

Използвайки тази стойност в програма, ще доведе до моделът на битовете за числото 13, вътре в компютърната памет.

JavaScript използва фиксиран брой битове, а именно 64 от тях за съхранение на една цифрова стойност. Това са толкова модели колкото могат да се направят с 64 бита, което означава, че количеството на различните номера, които могат да бъдат представени е ограничен. За N десетични числа, сумата от числата, които могат да бъдат представени е 10N. По същия начин имайки в предвид 64-те бинарни цифри, можем да представим 264 различни номера, което е около 18 квинталиона (18 с 18 нули след него), а това е много.

Компютърната памет използва много по-малко и хората са склонни да използват групи от 8 и 16 бита, когато представят техните номера. Преди беше лесно случайно да се препълнят такива малки номера, които да не могат да се впишат в даден размер на бита. Днес, дори и персоналните компютри имат достатъчно памет, за да може свободно да се използват 64 битови парчета, което означава, че трябва да се притесняваме от препълване на паметта само, когато се занимаваме с наистина астрономически цифри.

Не всички цели числа под 18 квинталиона се вписват в номер на JavaScript, все пак. В битовите също се съхраняват и отрицателни числа, така че един бит се използва за да показва знака на числото. По-голям проблем обаче е, че и не-цели числа същото трябва да бъдат представени. За да се направи това някои от битовете се използват за съхранение на позицията на десетичната запетая. Действителното максимално цяло число, което може да се съхранява е от порядъка на 9 квадрилиона (15 нули), което все още е огромно.

Дробни числа се изписват с помощта на точка:

9.81

За много големи или много малки номера, може да използвате и научна нотация, чрез добавяне на “e” (за “exponent”) следвана от експонентата на броя:

2.998e8

Това е 2.998 × 108 = 299,800,000.

Изчисления с цели числа (наричани integers) по-малки от посочените по-горе 9 квадрилиона са гарантирани да бъдат винаги точни. За съжаление изчисленията с дробни числа обикновено не са. Точно, както π (пи) не може да бъде точно изразено от краен брой десетични цифри, така и много номера губят част от своята прецизност, когато само 64 бита са на разположение да ги съхраняват. Това е жалко, но причинява практически проблеми само в определени случаи. Важно е да сме наясно с това и да мислим за дробните числа като цифрови приближения, а не като точни стойности.

Аритметика

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

100 + 4 * 11

Символите + и * се наричат оператори. Първият е за събиране, а вторият е за умножение. Поставянето на оператор между две стойности се прилага с цел тези стойности да произведат нова стойност.

Означава ли този пример - събери 4 и 100 и резултатът умножи по 11 или се извършва първо умножението преди събирането? Както може би се досещате умножението се извършва първо. Но в областта на математиката, можете да промените това, като заградите операцията, която искате да се извърши първа в скоби.

(100 + 4) * 11

За изваждане е оператора - , а за делене е / .

Когато операторите се появяват заедно без скоби, редът на тяхното прилагане се определя от предимството на операторите. Примерът показва, че умножението се извършва преди събирането. Операторът за делене / има същото предимство, както за умножение *. По същия начин се определят и + и - . Когато няколко оператора с едно и също предимство се появят един до друг, като 1 - 2 + 1, те се прилагат от ляво на дясно: (1 - 2) + 1.

Тези правила за предимство не са нещо, за което трябва да се притеснявате. Когато се съмнявате, просто добавете скоби.

Има още един оператор, който може да се призове незабавно. Символът % се използва за представяне на остатъка от операцията. Х % Y е остатъка от деленето на Х от Y. Например, остатъка от 314 % 100 е 14 и остатъка от 144 % 12 е 0. Предимство има остатъка, също както при делене и умножение. Често ще виждате този оператор посочен като модул, макар че технически погледнато остатък е по-точно наименование.

Специални номера

Има три специални стойности в JavaScript, които се смятат за числа, но не се държат като нормални такива.

Първите две са Infinity и -Infinity, които представляват положителна и отрицателна безкрайност. Infinity - 1 все още е Infinity и т.н. Не влагайте прекалено голямо доверие в изчисления базирани на безкрайност. Те не са математически солидни и бързо ще доведат до следващия специален номер: NaN.

NaN означава “not a number” макар и да е стойност от типа номер. NaN се получава, например, когато се опитаме да разделим 0 / 0 (нула делено на нула), Infinity - Infinity или произволен брой цифрови операции, които не дават точен, смислен резултат.

Strings

Следващият основен тип данни е string. Той се използва за представяне на текст, който е написан в кавички.

"Patch my boat with chewing gum"
'Monkeys wave goodbye'

И двете единични и двойни кавички могат да се използват за отбелязване на strings , но не и двата вида едновременно.Текстовете могат да бъдат толкова дълги, колкото са отбелязани с кавички от началото до края на string.

Почти всичко може да се постави между кавичките и JavaScript ще направи string стойност от него. Но няколко характера са по-трудни. Може да си представите поставянето на кавички между кавичките, колко може да бъде трудно. Новите редове (характерите, които се получават, когато натиснете Enter) също не могат да се поставят между кавички. String трябва да остане на един ред.

За да може да се включат такива характери в string, се използва следната нотация: когато една обратно наклонена черта (\) се намира вътре в цитирания текст, това показва, че характера след нея има специално значение. Това се нарича escaping (бягство) на характера. Цитат, който се предхожда от обратно наклонена черта \ няма да сложи край на string, а e част от него. Когато се постави n характера след обратно наклонената черта \, това се тълкува като знак за нов ред \n. По същия начин, t след обратно наклонена черта \ означава табулация \t.

Погледнете следния string:

"This is the first line\nAnd this is the second"

В действителност текста изглежда така:

This is the first line
And this is the second

Има разбира се ситуации в които искате обратно наклонената черта в string да бъде само една обратно наклонена черта, а не специален код. Ако две обратно наклонени черти са една след друга, strings ще се разпадне и само една ще бъде оставена в получения string без стойност. Например текста: “A newline character is written like "\n".” може да се изрази в string по следния начин:

"A newline character is written like \"\\n\"."

String не могат да бъдат разделяни, умножавани или изваждани, но + оператора може да се използва върху тях. Той не събира, но конкатенира - слепва два strings заедно. Следващия ред ще произведе string - "concatenate":

"con" + "cat" + "e" + "nate"

Има още начини за манипулиране на strings, които ще обсъдим, когато стигнем методи в Глава 4.

Унарни оператори

Не всички оператори са символи. Някои от тях са написани като думи. Един такъв пример е оператора typeof, който произвежда string стойност: името на типа на стойността, която си му подал.

edit & run code by clicking it
console.log(typeof 4.5) // → number console.log(typeof "x") // → string

Ще използваме console.log в примерния код, за да можем да видим резултата от оценката на нещо. Когато стартирате такъв код, произведената стойност трябва да се покаже на екрана, макар и да изглежда, че зависи от заобикалящата среда на JavaScript, която използвате за да стартирате кода.

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

console.log(- (10 - 2))
// → -8

Булеви стойности - Boolean values

Често, ще ви е необходима стойност, която да прави разграничение между две възможности, като “yes” и “no” или “on” и “off”. За това в JavaScript има Булев тип, който има само две стойности: вярно и невярно (които са написани просто като думи true и false).

Сравнения.

Това е един от начините за произвеждане на булеви стойности:

console.log(3 > 2)
// → true
console.log(3 < 2)
// → false

The > и < са символите за “по-голямо от ” и ”по-малко от”. Те са двукомпонентни оператори. Прилагането им води до булева стойност, която показва дали е верен този случай.

Strings могат да бъдат сравнени по същия начин.

console.log("Aardvark" < "Zoroaster")
// → true

Начинът на подредба на strings е повече или по-малко азбучен: главните букви са винаги “по-малки”от малките такива, така че "Z" < "a" е вярно, други характери като (!, -, и т.н.) също са включени в подредбата. Действието на сравнението се основава на стандарта Unicode. Този стандарт определя номер за почти всеки характер, от който ще имате някога нужда, включително характери от Гръцки, Арабски, Японски и т.н. Tакива номера са полезни за съхраняване на strings вътре в компютъра, защото така ще бъде възможно да се представят, като последоватленост от цифри. При сравняване на strings, JavaScript ги проверява от ляво на дясно, като се сравняват цифровите кодове на характерите един по един.

Други подобни: >= (по-голямо или равно), <= (по-малко или равно), == (равно), и != (различно).

console.log("Itchy" != "Scratchy")
// → true

Има само една стойност в JavaScript, която не е равна на себе си, това е NaN, което означава “not a number”.

console.log(NaN == NaN)
// → false

С NaN се означава резултата от безсмислено изчисление и като такова не е равно на резултата от други безсмислени изчисления.

Логически оператори

Има също някои операции, на които могат да се прилагат булеви стойности. JavaScript поддържа три логически оператора: и, или, и не. Те могат да се използват, като причина за Булев тип.

Оператора && представлява логическо и. Той е бинарен оператор и неговия резултат е вярно само ако и двете подадени стойности са верни.

console.log(true && false)
// → false
console.log(true && true)
// → true

Операторът || означава логическо или. Неговия резултат е вярно, ако поне една от подадените му стойности е вярна.

console.log(false || true)
// → true
console.log(false || false)
// → false

Операторът не означава логическо не и се изписва, като удивителен знак (!). Той е унарен оператор, който променя подадената му стойност: !true произвежда false и !false произвежда true.

При смесване на тези булеви оператори с аритметика и други оператори не винаги е очевидно, кога са необходими скоби. На практика може да научите това знаейки от операторите, които сме виждали досега, || има най-ниско предимство, след това идва &&, след него операторите за сравнение >, == и т.н., а след това останалите. Те са избрани така с цел, когато при типични изрази, като следващия, са необходими няколко скоби.

1 + 1 == 2 && 10 * 10 > 50

Последният логически оператор не е унарен, не е бинарен, а трикомпонентен, работещ с три стойности. Той се изписва с въпросителен знак и две точки ( ? :)

console.log(true ? 1 : 2);
// → 1
console.log(false ? 1 : 2);
// → 2

Той се нарича условен оператор ( или понякога трикомпонентен оператор, тъй като той е единствения такъв оператор в езика). Стойността в ляво от въпросителния знак е “избор” коя от другите две стойности да извади. Когато е true се избира първата стойност след въпросителния знак, когато е false се избира стойността от дясно на двете точки.

Undefined стойности

Има две специални стойности, null и undefined, които се използват за обозначаване на липсата на смислена стойност. Те самите са стойности, но не носят информация.

Много операции в езика, които не произвеждат смислена стойност (ще видите някои по-късно) дават undefined просто, защото те трябва да върнат някаква стойност.

Разликата в значението между undefined и null е инцидент в дизайна на JavaScript, но това няма значение през по-голямата част от времето. В случаите, когато трябва да се занимавате с тези стойности, аз препоръчвам да ги третирате, като взаимозаменяеми (повече за това след малко).

Автоматично преобразуване на типа

В увода споменах, че JavaScript излиза от пътя си, за да приеме почти всяка програма, дори и програми, които правят странни неща. Това е добре демонстрирано в следните изрази:

console.log(8 * null)
// → 0
console.log("5" - 1)
// → 4
console.log("5" + 1)
// → 51
console.log("five" * 2)
// → NaN
console.log(false == 0)
// → true

Когато се прилага оператор върху “неправилен” тип стойност, JavaScript тихо ще преобразува тази стойност до типа, който иска с помощта на набор от правила, които често не са това, което искате или очаквате. Това се нарича корекция на тип. Така че null в първия израз става 0, а "5" във втория става 5 от string в номер. И все пак в третия израз, + опитва да конкатенира номер към string, така че 1 се превръща в "1" (от номер в string).

Когато нещо, което не се вписва в картата на номерата по очевиден начин (като "five" или undefined) се преобразува в номер и се произвежда NaN. Други аритметични операции върху NaN пазят резултата NaN, така че ако откриете един от тези изрази на неочаквано място, погледнете за случайно преобразуване на типа.

При сравняване на стойности от един и същи тип с използване на ==, резултатът е лесно предвидим: трябва да получите вярно, когато и двете стойности са едни и същи, с изключение на случаите с NaN. Но когато типовете се различават, JavaScript използва сложни и объркващи набори от правила, за да реши, какво да прави. В повечето случаи просто се опитва да преобразува една от стойностите в типа на другата стойност. Въпреки това, когато null или undefined са от двете страни на оператора, той произвежда вярно само ако и двете стани са от един и същи тип null или undefined.

console.log(null == undefined);
// → true
console.log(null == 0);
// → false

Поведението в последния код често е полезно. Когато искате да проверите дали дадена стойност е реална, вместо null или undefined, може просто да се сравни null с == (или с !=) оператора.

Но какво ще стане ако искате да тествате нещо, което се отнася до точната стойност false? Правилата за преобразуване на string и числа до Булеви стойности посочват, че 0, NaN и празен string ("") се отчитат, като false, а всички останали стойности като true. Поради това, изрази като 0 == false и "" == false също са верни. За случаи, като този, когато не искате автоматично преобразуване на типа, има два допълнителни оператора: === и !==. Първият тества дали дадена стойност е точно равна на другата стойност, а другия дали е точно различна. Така "" === false е false, както се очаква.

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

Късо съединение на логическите оператори

Логическите оператори && и || се справят със стойностите на различните типове по един особен начин. Те ще преобразуват стойността от лявата им страна в Булев тип, за да решат какво да правят, но в зависимост от оператора и резултата от това преобразуване, те връщатат оригиналната стойност от лявата или от дясната страна.

Оператора ||, например ще върне, като резултат стойността на лявата страна, когато може да я преобразува до true и ако не може ще върне, като резултат дясната страна. Това преобразуване работи, както бихте очаквали, за Булеви стойности и трябва да се направи нещо аналогично за стойности на другите типове.

console.log(null || "user")
// → user
console.log("Karl" || "user")
// → Karl

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

Оператора && работи по подобен начин, но малко по-различно. Когато стойността от ляво е нещо, което се преобразува до false, той връща, като резултат тази стойност в противен случай връща стойността от дясно.

Друго важно свойство на тези два оператора е, че израза от дясно се оценява само, когато е необходимо. В случай на true || X без значение какво е X дори да е израз, който прави нещо ужасно, резултата ще бъде верен и X никога не се оценява. Същото важи и за false && X, което не е вярно и ще игнорира X. Това се нарича проверка на късо съединение.

Условния оператор работи по подобен начин. Първият израз винаги се оценява, но втората или третата стойност, която включва не.

Резюме

Разгледахме четири типа стойности на JavaScript в настоящата глава: numbers, strings, Booleans, и undefined.

Тези стойности се създават, чрез въвеждане на тяхното име (true, null) или стойност (13, "abc"). Можете да комбинирате и трансформирате стойности с операторите. Видяхме бинарни оператори за аритметика (+, -, *, / и %), за конкатинация на string (+), за сравнение (==, !=, ===, !==, <, >, <=, >=), за логика (&&, ||), както и няколко унарни оператора (-) за изваждане, (!) за логическо отрицание и (typeof) за определяне на типа на стойността. И трикомпонентния оператор (?:) за избиране на една от две стойности въз основа на трета стойност.

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