Выразительный JavaScript

Хавербеке Марейн

1. Величины, типы и операторы

 

 

В компьютерном мире есть только данные. Можно читать данные, изменять данные, создавать новые – но кроме данных ничего нет. Все данные хранятся как длинные последовательности бит, этим они сходны между собой.

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

К примеру, номер 13. Вместо десятичной системы, состоящей из 10 цифр, у вас есть двоичная система с двумя цифрами. Значение каждой позиции числа удваивается при движении справа налево. Биты, составляющие число 13, вместе с их весами:

0   0   0   0 1 1 0 1

128 64 32  16 8 4 2 1

Получается двоичное число 00001101, или 8 + 4 + 1, что равно 13.

 

Величины

Представьте океан бит. Типичный современный компьютер хранит более 30 миллиардов бит в оперативной памяти. Постоянная память (жёсткий диск) обычно ещё на пару порядков объёмнее.

Чтобы работать с ними и не заблудиться, вы можете делить их на куски, представляющие единицы информации. В JavaScript эти куски называются величинами. Все они состоят из бит, но играют разные роли. У каждой величины есть тип, определяющий её роль. Всего есть шесть основных типов: числа, строки, булевы величины, объекты, функции и неопределённые величины.

Для создания величины вам нужно указать её имя. Это удобно. Вам не надо собирать стройматериалы или платить за них. Нужно просто позвать – и оп-па, готово. Они не создаются из воздуха – каждая величина где-то хранится, и если вы хотите использовать огромное их количество, у вас могут закончиться биты. К счастью, это только если они все нужны вам одновременно. Когда величина вам станет не нужна, она растворяется, и использованные ею биты поступают в переработку как стройматериал для новых величин.

В этой главе мы знакомимся с атомами программ JavaScript – простые типы величин и операторы, которые к ним применимы.

 

Числа

Величины числовых типов, это – сюрприз – числа. В программе JavaScript они записываются как

13

Используйте эту запись в программе, и она вызовет к жизни в компьютерной памяти цепочку бит, представляющую число 13.

JavaScript использует фиксированное число бит (64) для хранения численных величин. Число величин, которые можно выразить при помощи 64 бит, ограниченно – то есть и сами числа тоже ограниченны. Для N десятичных цифр количество чисел, которые ими можно записать, равно 10 в степени N. Аналогично, 64 битами можно выразить 2 в 64 степени чисел. Это довольно много.

Раньше у компьютеров памяти было меньше, и тогда для хранения чисел использовали группы из 8 или 16 бит. Было легко случайно превысить максимальное число для таких небольших чисел – то есть, использовать число, которое не помещалось в этот набор бит. Сегодня у компьютеров памяти много, можно использовать куски по 64 бит, и значит, вам надо беспокоиться об этом только, если вы работаете с астрономическими числами.

Правда, не все числа меньше 264 помещаются в число JavaScript. В этих битах также хранятся отрицательные числа – поэтому, один бит хранит знак числа. Кроме того, нам нужно иметь возможность хранить дроби. Для этого часть бит используется для хранения позиции десятичной точки. Реальный максимум для чисел – примерно 1015, что в общем всё равно довольно много.

Дроби записываются с помощью точки.

9.81

Очень большие или маленькие числа записываются научной записью с буквой “e” (exponent), за которой следует степень:

2.998e8

Это 2,998 × 108 = 299800000.

Вычисления с целыми числами (которые также называются integer), меньшими, чем 1015, гарантированно будут точными. Вычисления с дробями обычно нет. Так же, как число π (пи) нельзя представить точно при помощи конечного числа цифр, так и многие дроби нельзя представить в случае, когда у нас есть только 64 бита. Плохо, но это мешает в очень специфических случаях. Важно помнить об этом и относиться к дробям как к приближённым значениям.

 

Арифметика

Главное, что можно делать с числами – это арифметические вычисления. Сложения и умножения используют два числа и выдают третье. Как это записывается в JavaScript:

100 + 4 * 11

Символы + и * называются операторами. Первый – сложение, второй – умножение. Помещаем оператор между двумя величинами и получаем значение выражения.

А в примере получается «сложить 4 и 100 и затем умножить результат на 11», или умножение выполняется сначала? Как вы могли догадаться, умножение выполняется первым. Но как и в математике, это можно изменить при помощи скобок:

(100 + 4) * 11

Для вычитания используется оператор -, а для деления - /.

Когда операторы используются без скобок, порядок их выполнения определяется их приоритетом. У операторов * и / приоритет одинаковый, выше, чем у + и -, которые между собой равны по приоритету. При вычислении операторов с равным приоритетом они вычисляются слева направо:

1 - 2 + 1

вычисляется как (1 - 2) + 1.

Пока беспокоиться о приоритетах не надо. Если сомневаетесь, используйте скобки.

Есть ещё один оператор, который вы не сразу узнаете. Символ % используется для получения остатка. X % Y – остаток от деления X на Y. 314 % 100 даёт 14, и 144 % 12 даёт 0. Приоритет у оператора такой же, как у умножения и деления. Математики для операции нахождения остатка от деления % могут использовать термин сравнение по модулю.

 

Специальные числа

В JavaScript есть три специальных значения, которые считаются числами, но ведут себя не как обычные числа.

Это Infinity и -Infinity, которые представляют положительную и отрицательную бесконечности. Infinity - 1 = Infinity, и так далее. Не надейтесь сильно на вычисления с бесконечностями, они не слишком строгие.

Третье число: NaN. Обозначает «not a number» (не число), хотя это величина числового типа. Вы можете получить её после вычислений типа 0 / 0, Infinity – Infinity, или других операций, которые не ведут к точным осмысленным результатам.

 

Строки

Следующий базовый тип данных – строки. Они используются для хранения текста. Записываются они в кавычках:

"Что посеешь, то из пруда"

'Баба с возу, потехе час'

Можно использовать как двойные, так и одинарные кавычки – главное использовать их вместе. Почти всё можно заключить в кавычки и сделать из этого строку. Но некоторые символы вызывают проблемы. Например, сложно заключить кавычки в кавычки. Перевод строки тоже нельзя просто так заключить в них – строка должна идти одной строкой.

Для заключения специальных символов используется обратный слэш (\). Он обозначает, что символ, идущий за ним, имеет специальное значение – это называется «экранирование символов» (escape character). \” можно заключать в двойные кавычки. \n обозначает перевод строки, \t – табуляцию.

Строка “Между первой и второй\nсимвол будет небольшой” на самом деле будет выглядеть так:

Между первой и второй

символ будет небольшой

Если вам нужно включить в строку обратный слэш, его тоже нужно экранировать: \\. Инструкцию “Символ новой строки — это “\n”” нужно будет написать так:

"Символ новой строки – это \"\\n\""

Строки нельзя делить, умножать и складывать. Однако с ними можно использовать оператор +, который будет соединять их друг с другом. Следующее выражение выдаст слово «соединение»:

"сое" + "ди" + "н" + "ение"

Есть много способов манипуляций со строками, которые мы обсудим в главе 4.

 

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

Не все операторы записываются символами – некоторые словами. Один из таких операторов – typeof, который выдаёт название типа величины, к которой он применяется.

console.log(typeof 4.5)

// → number

console.log(typeof "x")

// → string

Будем использовать вызов console.log в примерах, когда захотим увидеть результат на экране. Как именно будет выдан результат – зависит от окружения, в котором вы запускаете скрипт.

Предыдущие операторы работали с двумя величинами, однако typeof использует только одну. Операторы, работающие с двумя величинами, называются бинарными, а с одной – унарными. Минус (вычитание) можно использовать и как унарный, и как бинарный.

console.log(- (10 - 2))

// → -8

 

Булевские величины

Часто вам нужна величина, которая просто показывает одну из двух возможностей – типа «да» и «нет», или «вкл» и «выкл». Для этого в JavaScript есть тип Boolean, у которого есть всего два значения – true и false (правда и ложь).

 

Сравнения

Один из способов получить булевские величины:

console.log(3 > 2)

// → true

console.log(3 < 2)

// → false

Знаки < и > традиционно обозначают «меньше» и «больше». Это бинарные операторы. В результате их использования мы получаем булеву величину, которая показывает, является ли неравенство верным.

Строки можно сравнивать так же:

console.log("Арбуз" < "Яблоко")

// → true

Строки сравниваются по алфавиту: буквы в верхнем регистре всегда «меньше» букв в нижнем регистре. Сравнение основано на стандарте Unicode. Этот стандарт присваивает номер практически любому символу из любого языка. Во время сравнения строк JavaScript проходит по их символам слева направо, сравнивая номерные коды этих символов.

Другие сходные операторы – это >= (больше или равно), <= (меньше или равно), == (равно), != (не равно).

console.log("Хочется" != "Колется")

// → true

В JavaScript есть только одна величина, которая не равна самой себе – NaN («не число»).

console.log(NaN == NaN)

// → false

NaN – это результат любого бессмысленного вычисления, поэтому он не равен результату какого-то другого бессмысленного вычисления.

Есть операции, которые можно совершать и с самими булевыми значениями. JavaScript поддерживает три логических оператора: и, или, нет.

Оператор && — логическое «и». Он бинарный, и его результат – правда, только если обе величины, к которым он применяется, тоже правда.

console.log(true && false)

// → false

console.log(true && true)

// → 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, второе.

 

Неопределённые значения

Существуют два специальных значения, null и undefined, которые используются для обозначения отсутствия осмысленного значения. Сами по себе они никакой информации не несут.

Много операторов, которые не выдают значения, возвращают undefined просто для того, чтобы что-то вернуть. Разница между undefined и null появилась в языке случайно, и обычно не имеет значения.

 

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

Ранее я упоминал, что JavaScript позволяет выполнять любые, подчас очень странные программы. К примеру:

console.log(8 * null)

// → 0

console.log("5" - 1)

// → 4

console.log("5" + 1)

// → 51

console.log("пять" * 2)

// → NaN

console.log(false == 0)

// → true

Когда оператор применяется «не к тому» типу величин, JavaScript втихую преобразовывает величину к нужному типу, используя набор правил, которые не всегда соответствуют вашим ожиданиям. Это называется приведением типов (coercion). В первом выражении null превращается в 0, а “5” становится 5 (из строки – в число). Однако в третьем выражении + выполняет конкатенацию (объединение) строк, из-за чего 1 преобразовывается в “1” (из числа в строку).

Когда что-то неочевидное превращается в число (к примеру, “пять” или undefined), возвращается значение NaN. Последующие арифметические операции с NaN опять получают NaN. Если вы получили такое значение, поищите, где произошло случайное преобразование типов.

При сравнении величин одного типа через ==, легко предсказать, что вы должны получить true, если они одинаковые (исключая случай с NaN). Но когда типы различаются, JavaScript использует сложный и запутанный набор правил для сравнений. Обычно он пытается преобразовать тип одной из величин в тип другой. Когда с одной из сторон оператора возникает null или undefined, он выдаёт true только если обе стороны имеют значение null или undefined.

console.log(null == undefined);

// → true

console.log(null == 0);

// → false

Последний пример демонстрирует полезный приём. Когда вам надо проверить, имеет ли величина реальное значение вместо null или undefined, вы просто сравниваете её с null при помощи == или !=.

Но что, если вам надо сравнить нечто с точной величиной? Правила преобразования типов в булевские значения говорят, что 0, NaN и пустая строка “” считаются false, а все остальные – true. Поэтому 0 == false и “” == false. В случаях, когда вам не нужно автоматическое преобразование типов, можно использовать ещё два оператора: === и !==. Первый проверяет, что две величины абсолютно идентичны, второй – наоборот. И тогда сравнение “” === false возвращает false.

Рекомендую использовать трёхсимвольные операторы сравнения для защиты от неожиданных преобразований типов, которые могут привести к непредсказуемым последствиям. Если вы уверены, что типы сравниваемых величин будут совпадать, можно спокойно использовать короткие операторы.

 

Короткое вычисление логических операторов

Логические операторы && и || работают с величинами разных типов очень странным образом. Они преобразуют величину с левой стороны оператора в булевскую, чтобы понять, что делать дальше, но в зависимости от оператора и от результата этого преобразования, возвращают оригинальное значение либо левой, либо правой части.

К примеру, || вернёт значение с левой части, когда его можно преобразовать в true – а иначе вернёт правую часть.

console.log(null || "user")

// → user

console.log("Karl" || "user")

// → Karl

Такая работа оператора || позволяет использовать его как откат к значению по умолчанию. Если вы дадите ему выражение, которое может вернуть пустое значение слева, то значение справа будет служить заменой на этот случай.

Оператор && работает сходным образом, но наоборот. Если величина слева преобразовывается в false, он возвращает эту величину, а иначе – величину справа.

Ещё одно важное их свойство – выражение в правой части вычисляется только при необходимости. В случае true || X не важно, чему равно X. Даже если это какое-то ужасное выражение. Результат всегда true и X не вычисляется. Так же работает false && X – X просто игнорируется. Это называется коротким вычислением.

Оператор условия работает так же. Первое выражение всегда вычисляется, а из второго и третьего значения – только то, которое оказывается выбранным в результате.

 

Итог

Мы рассмотрели четыре типа величин JavaScript: числа, строки, булевские и неопределённые.

Эти величины получаются, когда мы пишем их имена (true, null) или значения (13, “ёпрст”). Их можно комбинировать и изменять при помощи операторов. Для арифметики есть бинарные операторы (+, -, *, / и %), объединение строк (+), сравнение (==, !=, ===, !==, <, >, <=, >=) и логические операторы (&&, ||), а также несколько унарных операторов (- для отрицательного значения, ! для логического отрицания и typeof для определения типа величины).

Эти знания позволяют использовать JavaScript в качестве калькулятора, но и только. В следующей главе мы будем связывать эти простые значения вместе, чтобы составлять простые программы.