Добро пожаловать в серию книг Вы не знаете JS (You don't know JS - YDKJS).
Книга Начните и Совершенствуйтесь является введением в некоторые базовые концепции программирования, конечно, с намеренным уклоном в JavaScript (часто сокращаемый до JS), также она о том, как подступиться к оставшимся книгам серии и понять их. Эта книга кратко описывает все необходимое, чтобы начать программировать, в том числе на JavaScript.
Книга начинается с объяснения базовых принципов программирования на самом высоком уровне. Она в основном предназначена для тех из вас, кто начинает YDKJS, имея малый или не имея вовсе опыта в программировании, а также тех, кто рассчитывает, что эти книги помогут встать на путь понимания программирования сквозь призму JavaScript.
Главу 1 можно представить как быстрый обзор того, что вам следует изучить поподробнее и с чем попрактиковаться, дабы быстрее войти в программирование. Кроме того, есть много других фантастических ресурсов по основам программирования, которые помогут вам изучить во всех подробностях затрагиваемые темы, и я призываю вас изучить их в дополнение к этой главе.
Если вы хорошо знаете общие основы программирования, глава 2 подтолкнет вас к более близкому знакомству с духом программирования на JavaScript. Глава 2 знакомит с тем, что такое JavaScript, но, обращаю ваше внимание еще раз: это не подробное руководство — за это выступают остальные книги YDKJS!
Если вы уже довольно комфортно чувствуете себя с JavaScript, сначала ознакомьтесь с главой 3 для беглого знакомства с тем, чего ожидать от YDKJS, а затем приступайте!
Начнем с начала.
Программа, часто упоминаемая как исходный код или просто код — это набор особых инструкций, сообщающих компьютеру, какие задачи нужно сделать. Обычно код сохраняют в текстовый файл, хотя в случае c JavaScript можно писать код прямо в консоли разработчика в браузере, чего мы кратко коснемся далее.
Правила допустимого формата и комбинаций операторов, называемые языком программирования, иногда соотносят с его синтаксисом, аналогично английскому языку, где правила говорят вам, как произносить слова и как составлять правильные предложения, используя слова и знаки препинания.
В языке программирования группа слов, чисел и операций, которые выполняют определенную задачу, называются оператором. В JavaScript, оператор может выглядеть так:
a = b * 2;
Символы a
и b
называются переменными (см. «Переменные»), которые примерно как обычные коробки, в которых вы можете хранить что угодно. В программах переменные хранят значения (например, число 42
), используемые программой. Представляйте их как символьную подмену для самих значений.
В противоположность им, 2
— это само значение, называемое литеральным значением, поскольку оно само по себе не хранится в переменной.
Символы =
и *
— это операции (см. «Операции»): они выполняют действия, такие как присваивание и математическое умножение, со значениями и переменными.
Большинство операторов в JavaScript заканчиваются точкой с запятой (;
).
Оператор a = b * 2;
сообщает компьютеру, грубо говоря, взять текущее значение из переменной b
, умножить это значение на 2
, а затем сохранить результат в другую переменную, которую мы назвали a
.
Программы — это всего лишь набор стольких операторов, сколько необходимо для того, чтобы описать все шаги для достижения цели вашей программы.
Операторы состоят из одного или более выражений. Выражение — это любая ссылка на переменную или значение, или набор переменных и значений, объединенных операциями.
Например:
a = b * 2;
У этого оператора 4 выражения:
2
— это выражение литерального значенияb
— это выражение переменной, которое тут означает извлечение его текущего значенияb * 2
— это арифметическое выражение, в данном случае выполнение умноженияa = b * 2
— это выражение присваивания, в данном случае это присвоить результат выраженияb * 2
переменнойa
(подробнее о выражениях далее)
Типичное выражение, которое является законченным, называется оператор-выражение, например, такое как это:
b * 2;
Этот пример оператора-выражения не является типовым или полезным, и в целом не оказывает никакого эффекта на выполнение программы — он всего лишь извлекает значение b
и умножает его на 2
, но затем ничего не делает с результатом.
Более распространенный оператор-выражение — это оператор-выражение вызова (см. «Функции»), поскольку весь оператор — это выражение вызова функции:
alert( a );
Так как же эти наборы программных операторов сообщают компьютеру, что нужно делать? Программу нужно выполнить, также говорят запуск программы.
Операторы, подобные a = b * 2
, понятны для разработчиков как при чтении, так и записи, но фактически в такой форме они не понятны напрямую компьютеру. Поэтому используется специальная утилита в компьютере (либо интерпретатор, либо компилятор) для перевода кода, который вы пишете, в команды, понятные компьютеру.
В некоторых языках программирования перевод команд обычно выполняется сверху вниз, строка за строкой, каждый раз когда программа запускается, что обычно называется интерпретацией кода.
В других языках перевод, выполняемый заранее, называется компиляцией кода, поэтому, когда позднее программа запускается, то что запускается — это по факту уже скомпилированные инструкции компьютера, готовые к выполнению.
Обычно утверждают, что JavaScript — интерпретируемый, так как ваш исходный код на JavaScript обрабатывается каждый раз, когда запускается. Но это не совсем точно. Движок JavaScript на самом деле компилирует программу на лету и затем сразу же запускает скомпилированный код.
Примечание: Подробнее о компиляции JavaScript смотрите в первых двух главах книги Область видимости и замыкания этой серии.
Эта глава проиллюстрирует каждое понятие из программирования простыми примерами кода, полностью написанными на JavaScript (очевидно!).
Нельзя не отметить, что пока вы продвигаетесь по этой главе, вам может понадобиться перечитать её несколько раз, и вам следует практиковаться в каждом из понятий, набирая код самостоятельно. Простейший способ сделать это - открыть консоль в средствах разработчика в ближайшем браузере (Firefox, Chrome, IE и т.п.).
Подсказка: Обычно вы можете запустить консоль разработчика с помощью горячих клавиш или из меню. Подробнее о запуске и использовании консоли в вашем любимом браузере см. “Mastering The Developer Tools Console” (http://blog.teamtreehouse.com/mastering-developer-tools-console). Чтобы ввести несколько строк в консоли за раз, используйте <shift> + <enter>
, чтобы переместиться на новую строку. Как только вы просто нажмете <enter>
, консоль выполнит всё, что вы написали.
Давайте познакомимся с процессом запуска кода в консоли. Сперва я предлагаю открыть пустую вкладку в браузере. Я предпочитаю делать это, набирая about:blank
в адресной строке. Затем убедитесь, что ваша консоль разработчика, о которой мы только что упоминали, открылась.
Теперь наберите этот код и посмотрите, как он выполняется:
a = 21;
b = a * 2;
console.log( b );
Набрав в консоли вышеуказанный код в браузере Chrome, мы увидим что-то вроде этого:
Вперед, попробуйте! Наилучший путь обучения программированию — это начать писать код!
В предыдущем кусочке кода мы использовалиconsole.log(..)
. Давайте взглянем вкратце о чем же эта строка кода.
Возможно вы это предполагали, но это и в самом деле то, как мы печатаем текст (т.е. вывод для пользователя) в консоли разработчика. Есть две характеристики этого оператора, которые нам следует пояснить.
Первая часть, log( b )
, указывает на вызов функции (см. «Функции»). Здесь получается, что мы передаем переменную b
в эту функцию, которая берет значение b
и печатает его в консоли.
Вторая часть, console.
, является ссылкой на объект, где расположена функция log(..)
. Мы рассмотрим объекты и их свойства более детально в главе 2.
Еще один путь вывести информацию — запустить оператор alert(..)
. Например:
alert( b );
Если вы запустите этот оператор, то заметите, что вместо вывода значения в консоль он показывает всплывающее окно с кнопкой «OK» и содержимым переменной b
. Однако использование console.log(..)
обычно лучше помогает кодировать и запускать программы в консоли, чем использование alert(..)
, потому что вы можете вывести несколько значений за раз без остановки в интерфейсе браузера.
В этой книге мы будем использовать для вывода console.log(..)
.
Пока мы обсуждаем вывод, вы попутно могли задаться вопросом о вводе (т.е. о получении информации от пользователя).
Самый распространенный путь — показать элементы формы на HTML-странице (например, строки ввода) для пользователя, чтобы он мог вводить туда данные, а затем, используя JS, считать эти значения в переменные программы.
Но есть более простой путь получать входные данные в целях обучения и демонстрации, который вы будете использовать на протяжении всей этой книги. Используйте функцию prompt(..)
:
age = prompt( "Please tell me your age:" );
console.log( age );
Как вы уже могли догадаться, сообщение, которое вы передаете в prompt(..)
, в данном случае "Please tell me your age:"
('"Пожалуйста сообщите мне свой возраст:"'), выводится во всплывающем окне.
Это может выглядеть примерно так:
Как только вы подтвердите ввод текста, щелкнув по «OK», вы заметите, что введенное значение теперь хранится в переменной age
, которую мы затем выводим с помощью console.log(..)
:
Для упрощения, пока мы изучаем основные понятия программирования, примеры в этой книге не потребуют ввода. Зато теперь вы увидели как пользоваться prompt(..)
. Если вы хотите проверить себя, то можете попробовать использовать ввод в порядке экспериментов с примерами.
Операции — это действия, которые мы выполняем над переменными и значениями. Мы уже видели две операции в JavaScript: =
и *
.
Операция *
выполняет математическое умножение. Достаточно просто, не так ли?
Операция =
используется для присваивания — сначала мы вычисляем значение с правой стороны (исходное значение) от =
, а затем записываем его в переменную, которую мы указываем с левой стороны (переменная назначения).
Предупреждение: Такой обратный порядок для присваивания может выглядеть немного странно. Вместо a = 42
кто-то может предпочесть поменять порядок, чтобы исходное значение было слева, а переменная назначения — справа, например 42 -> a
(это неправильный JavaScript!). К сожалению, форма a = 42
и похожие на нее практически полностью превалируют в современных языках программирования. Если вам такой порядок присваивания кажется неестественным, потратьте некоторое время на привыкание к нему.
Пример:
a = 2;
b = a + 1;
Тут мы присваиваем значение 2
переменной a
. Затем мы получаем значение переменной a
(пока еще 2
), прибавляем к нему 1
получая в результате 3
, потом сохраняем это значение в переменной b
.
Хотя оно технически не является операцией, вам необходимо ключевое слово var
в любой программе, поскольку это основной способ, с помощью которого вы объявляете (т.е. создаете) переменные (сокращение от variables) (см. «Переменные»).
Вы всегда должны объявить переменную с именем до того, как начнете её использовать. Но вам достаточно объявить переменную всего раз для каждой области видимости (см. «Область видимости»), а затем пользоваться ею столько раз, сколько нужно. Например:
var a = 20;
a = a + 1;
a = a * 2;
console.log( a ); // 42
Вот несколько самых базовых операций в JavaScript:
-
Присваивание:
=
как вa = 2
. -
Математические:
+
(сложение),-
(вычитание),*
(умножение) и/
(деление), как вa * 3
. -
Составное присваивание:
+=
,-=
,*=
и/=
— это составные операции, которые объединяют математическую операцию с присваиванием, как вa += 2
(эквивалентноa = a + 2
). -
Инкремент/Декремент:
++
(инкремент),--
(декремент), как вa++
(эквивалентноa = a + 1
). -
Доступ к свойству объекта:
.
как вconsole.log()
.Объекты — это значения, которые хранят другие значения под своими именами, называемые свойства.
obj.a
означает значение из объектаobj
из его свойстваa
. Еще один способ доступа к свойствам —obj["a"]
. См. главу 2. -
Равенство:
==
(нестрогое),===
(строгое),!=
(нестрогое неравенство),!==
(строгое неравенство), как вa == b
.См. «Значения и типы» и главу 2.
-
Сравнение:
<
(меньше чем),>
(больше чем),<=
(меньше или нестрого равно),>=
(больше или нестрого равно), как вa <= b
.См. «Значения и типы» и главу 2.
-
Логические:
&&
(и),||
(или), как вa || b
, которое выбирает илиa
, или (or)b
.Эти операции используются для создания составных условных конструкций (см. «Условные конструкции»), например: если либо
a
либо (or)b
— истина.
Примечание: Для более детального рассмотрения и охвата операций, не рассмотренных здесь, см. the Mozilla Developer Network (MDN)'s “Expressions and Operators“ (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators).
Если вы спросите сотрудника в магазине сотовой связи, сколько стоит определенный телефон, и он ответит: «девяносто девять, девяносто девять» (т.е. 99.99), таким образом он дает вам точную информацию о сумме денег, которую вам необходимо заплатить, чтобы купить телефон. Если вы хотите купить два таких телефона, вы легко сможете в уме удвоить стоимость, получив 199.98 в качестве общей стоимости.
Если тот же сотрудник возьмет другой аналогичный телефон и скажет, что он «бесплатный» (конечно, в кавычках), он не скажет вам сумму, но взамен предоставит другую форму представления ожидаемой стоимости (0.00) — слово «бесплатный».
Если затем вы спросите, включено ли в комплект поставки телефона зарядное устройство, то ответ может быть только «да» или «нет».
Весьма схожим образом, когда вы указываете значения в программе, вы выбираете разные представления для этих значений в зависимости от того, что вы планируете делать с ними.
Эти разнообразные представления для значений называются типами в терминологии программирования. В JavaScript есть встроенные типы для каждого из этих так называемых примитивных значений:
- когда вам нужно работать с математикой, вам нужно
число
. - когда вам нужно вывести значение на экран, вам нужна
строка
(один или несколько символов, слов, предложений). - когда вам нужно принять решение в своей программе, вам нужно
логическое значение
(true
(истина
) илиfalse
(ложь
)).
Значения, непосредственно включаемые в исходный код, называются литералами. строковые
литералы заключаются в двойные "..."
или одинарные '...'
кавычки — единственная разница в них — это ваши стилистические предпочтения. Литералы числа
и логического значения
пишутся как есть (т.е., 42
, true
и т.д.).
Пример:
"Я - строка";
'Я - тоже строка';
42;
true;
false;
Кроме типов значений строка
/число
/логическое значение
для языков программирования привычно предоставлять такие типы, как массивы, объекты, функции и многое другое. Мы рассмотрим детально значения и типы на протяжении этой и следующей глав.
Если у вас есть число
и вам нужно вывести его на экран, то необходимо преобразовать его значение в строку
. В JavaScript такая конвертация называется «приведением типов (coercion)». Аналогично, если кто-то вводит серию цифр в форму на веб-странице, это строка
, но если нужно потом использовать это значение для выполнения математических операций, то вам понадобится приведение его к числу
.
JavaScript предоставляет несколько различных возможностей принудительного приведения между типами. Например:
var a = "42";
var b = Number( a );
console.log( a ); // "42"
console.log( b ); // 42
Использование Number(..)
(встроенная функция), как было показано выше, это явное приведение из любого другого типа в тип число
. Это выглядит достаточно очевидно.
Но каверзный вопрос заключается в том, что произойдет, если вы попытаетесь сравнить два значения разных типов, которые могут потребовать неявного приведения.
При сравнении строки "99.99"
с числом 99.99
многие согласятся, что они равны. Но они ведь не совсем одно и то же, не так ли? Это одно и то же значение в двух разных представлениях двух разных типов. Вы могли бы сказать, что они «нестрого равны», разве нет?
Чтобы помочь вам в таких стандартных ситуациях, JavaScript иногда вмешивается и неявно приводит значения к подходящим типам.
Поэтому если вы используете операцию нестрогого равенства ==
для сравнения "99.99" == 99.99
, JavaScript преобразует с левой стороны "99.99"
в его числовой
эквивалент 99.99
. После этого сравнение превращается в 99.99 == 99.99
, которое, конечно, является истинным
.
Несмотря на то, что неявное приведение было задумано, чтобы помочь вам, оно может привести в замешательство, если вы не уделили достаточно времени изучению правил, которые определяют его поведение. У большинства JS-разработчиков никогда его нет, поэтому общее отношение выражается в том, что неявное приведение сбивает с толку и вредит программам, внося непредвиденные ошибки, и поэтому следует его избегать. Иногда его даже называют изъяном дизайна языка.
Однако, неявное приведение — это механизм, который может и, даже более того, должен быть изучен каждым, кто хочет серьезно заниматься программированием на JavaScript. Не только потому, что после изучения его правил оно не будет смущать вас, оно может в самом деле улучшить ваши программы! Усилия того стоят!
Примечание: Для получения более подробной информации о приведении типов см. главу 2 этой книги и главу 4 книги «Типы и синтаксис» этой серии.
Сотрудник салона сотовой связи может набросать некоторые заметки о возможностях только что выпущенных телефонов или о новых тарифных планах, которые предлагает его компания. Эти заметки только для самого сотрудника — они не предназначены для чтения покупателями. Тем не менее эти заметки помогают сотруднику улучшить свою работу, документируя все "как" и "почему" того, что ему следует рассказать покупателям.
Один из самых важных уроков, который вы можете освоить при изучении программирования - код не только для компьютера. Каждый бит кода также, если не больше, важен для программиста, как и для компилятора.
Ваш компьютер заботится только о машинном коде, последовательности бинарных 0 и 1, которые появляются после компиляции. Почти бесконечное количество программ можно написать таким образом, что они скомпилируются в одинаковую последовательность 0 и 1. Выбор, который вы делаете о том, как написать программу, имеет значение не только для вас, но и для других членов вашей команды и даже для будущего вас.
Нужно стремиться писать программы, не только правильно работающие, но и понятные при чтении. Вы можете пройти долгий путь в этом направлении, к примеру, выбирая понятные имена для своих переменных (см. «Переменные») и функций (см. «Функции»).
Но еще одна важная часть этого процесса — это комментарии в коде. Это кусочки текста в вашей программе, которые вставляются именно для того, чтобы пояснить какие-то вещи для человека. Интерпретатор/компилятор всегда игнорирует эти комментарии.
Есть масса мнений о том, что делает код хорошо документируемым; тяжело определить на самом деле абсолютно универсальные правила. Но некоторые соображения и рекомендации будут весьма полезны:
- Код без комментариев не оптимален.
- Слишком много комментариев (по одному на каждую строку кода, например), возможно, является признаком плохо написанного кода.
- Комментарии должны объяснять почему, а не что. Они могут дополнительно объяснять как, когда код особенно сложен.
В JavaScript есть два типа комментариев: однострочный и многострочный.
Пример:
// Это - однострочный комментарий
/* А это
многострочный
комментарий.
*/
Однострочный комментарий //
подходит, если вы собираетесь разместить комментарий прямо над одиночным оператором или даже в конце строки. Всё что написано в строке после //
интерпретируется как комментарий (и потому игнорируется компилятором) до самого конца строки. Нет никаких ограничений на то, что может находиться внутри однострочного комментария.
Пример:
var a = 42; // 42 - смысл жизни
Многострочный комментарий /* .. */
подходит в случае, если у вас есть несколько строк пояснений для вашего кода.
Вот типичный пример использования многострочного комментария:
/* Нижеприведенное значение используется, поскольку
выяснилось, что оно отвечает
на любой вопрос во вселенной. */
var a = 42;
Он может появляться в любом месте строки, даже в середине, поскольку есть */
, обозначающий его окончание. Например:
var a = /* произвольное значение */ 42;
console.log( a ); // 42
Единственное, что не может появляться в многострочном комментарии — это */
, так как это будет означать конец комментария.
Вам, несомненно, следует изучать программирование, параллельно вырабатывая привычку комментировать код. На всем протяжении оставшейся части этой главы вы увидите, что я использую комментарии для пояснения кода, поэтому и вы делайте также в вашей собственной практике написания кода. Поверьте, все, кто будут читать ваш код, скажут вам спасибо!
Большинству программ нужно отслеживать то, как меняется то или иное значение на протяжении выполнения программы, проходя через различные операции, вызываемые для соответствующих задач вашей программы.
Самый простой путь сделать это в программе — это присвоить значение символьному контейнеру, называемому переменной, называющейся так потому, что значение в этом контейнере может меняться с течением времени при необходимости.
В некоторых языках программирования вы определяете переменную (контейнер), чтобы хранить определенный тип значения, такой как число
или строка
. Статическая типизация, также известная как контроль типов, обычно упоминается как преимущество в корректности программы, предотвращая непредусмотренные преобразования значений.
Для других языков больше важны типы для значений нежели переменных. Слабая типизация, также известная как динамическая типизация, позволяет переменной хранить значения любого типа в любое время. Это обычно упоминается как преимущество в гибкости программы, позволяющее одной переменной представлять значения разного типа в разные моменты выполнения программы.
JavaScript использует второй подход, динамическую типизацию. Это означает, что переменные могут хранить значения любого типа без какого-либо контроля над ними.
Как уже упоминалось ранее, мы объявляем переменную, используя оператор var
-- заметьте, при этом нет никакой другой информации о типе в объявлении. Обратите внимание на эту простую программу:
var amount = 99.99;
amount = amount * 2;
console.log( amount ); // 199.98
// преобразует `amount` в строку и
// добавляет "$" в начало
amount = "$" + String( amount );
console.log( amount ); // "$199.98"
Переменная amount
начинает свой жизненный цикл с хранения числа 99.99
, а затем хранит числовой
результат amount * 2
, который равен 199.98
.
Первая команда console.log(..)
должна неявно привести это числовое
значение к строке
, чтобы вывести его в консоль.
Затем оператор amount = "$" + String(amount)
явно приводит значение 199.98
к строке
и добавляет символ "$"
в начало. С этого момента, amount
хранит строковое
значение "$199.98"
, поэтому второму оператору console.log(..)
не нужно выполнять никакого приведения, чтобы вывести его в консоль.
Разработчики на JavaScript отметят гибкость использования переменной amount
для каждого из значений 99.99
, 199.98
и "$199.98"
. Энтузиасты статической типизации предпочтут отдельную переменную, например amountStr
, чтобы хранить окончательное представление значения "$199.98"
, поскольку оно уже будет другого типа.
В любом случае, вы заметите, что amount
хранит текущее значение, которое меняется по ходу выполнения программы, иллюстрируя первичную цель переменных: управление состоянием программы.
Другими словами, состояние отслеживает изменения значений при выполнении программы.
Еще одно распространенное использование переменных — централизация установки значений. Обычно это называется константами, когда вы объявляете переменную со значением и предполагаете, что это значение не будет меняться в течение работы программы.
Вы объявляете эти константы чаще всего в начале программы таким образом, чтобы иметь всего одно место для изменения значений при необходимости. По общепринятым соглашениям переменные в JavaScript, являющиеся константами, обычно пишутся большими буквами, с подчеркиваниями _
между словами.
Вот глупый пример:
var TAX_RATE = 0.08; // 8% налог с продаж
var amount = 99.99;
amount = amount * 2;
amount = amount + (amount * TAX_RATE);
console.log( amount ); // 215.9784
console.log( amount.toFixed( 2 ) ); // "215.98"
Примечание: Также как в выражении console.log(...)
функция log(..)
доступна как свойство объекта console
, в этом примере toFixed(..)
— это функция, которая может быть доступна у числовых
значений. Число
в JavaScript не форматируется автоматически со знаком валюты — среда выполнения не знает ваших намерений, плюс к этому не существует типа для валюты. toFixed(..)
позволяет нам указать, до скольких знаков после запятой мы хотим округлить число
, и при необходимости возвращает строку
.
Переменная TAX_RATE
является константой лишь по соглашению - в этой программе нет ничего, что могло бы предотвратить ее изменение. Но если ставка налога повысится до 9%, мы все еще можем легко обновить нашу программу, присвоив TAX_RATE
значение равное 0.09
всего в одном месте, вместо поиска и изменения всех вхождений значения 0.08
,разбросанных по программе.
Новейшая версия JavaScript на момент написания этих строк (обычно называемая "ES6") включает в себя новый способ объявления констант, использующий const
вместо var
:
// согласно ES6:
const TAX_RATE = 0.08;
var amount = 99.99;
// ..
Константы полезны также как и переменные с неизменяемыми значениями, за исключением того, что константы также предотвращают случайное изменение где-либо после начальной установки значения. Если вы попытаетесь присвоить любое значение в TAX_RATE
после её объявления, ваша программа отвергнет это изменение (а в строгом (strict) режиме, прервется с ошибкой, см. "Строгий режим" в главе 2).
Кстати, такой тип «защиты» от ошибок похож на контроль типов статической типизации, так что вы в какой-то степени поймете, почему статические типы в других языках могут быть привлекательными!
Примечание: Для получения более подробной информации о том, как различные значения в переменных могут использоваться в программах, см. книгу Типы и синтаксис этой серии.
Когда вы покупаете новый телефон, сотрудник салона сотовой связи должен пройти последовательность шагов для завершения оформления покупки.
Примерно также в коде нам часто нужно сгруппировать последовательности операторов вместе, которые мы часто называем блоками. В JavaScript блок определяется обрамлением одного или более операторов парой фигурных скобок { .. }
. Пример:
var amount = 99.99;
// отдельный блок
{
amount = amount * 2;
console.log( amount ); // 199.98
}
Такой вид отдельного блока { .. }
вполне допустим, но не часто встречается в JS-программах. Обычно блоки присоединяются к другим управляющим операторам, таким как оператор if
(см. «Условные конструкции») или цикл (см. «Циклы»). Например:
var amount = 99.99;
// сумма достаточно велика?
if (amount > 10) { // <-- блок прикрепляется к `if`
amount = amount * 2;
console.log( amount ); // 199.98
}
Мы расскажем об операторе if
в следующем разделе, но как вы видите блок { .. }
с двумя операторами присоединен к if (amount > 10)
. Операторы внутри этого блока будут выполнены только при истинности выражения в условной конструкции.
Примечание: В отличие от многих других операторов, таких как console.log(amount);
, блоковый оператор не требует точки с запятой (;
) в конце.
«Хотите ли вы добавить дополнительную защитную пленку за $9.99 в вашу покупку?». Любезный сотрудник магазина попросил вас принять решение. И вам может сначала понадобиться проинспектировать текущее состояние вашего кошелька или банковского счета, чтобы ответить на этот вопрос. Но, очевидно, что это всего лишь простой вопрос из разряда «да или нет».
Есть несколько способов выражения условных конструкций (т.е. выбора) в наших программах.
Самый распространенный из них — это оператор if
. По сути, вы говорите, «Если (if) это условие истинно, сделать следующее...». Например:
var bank_balance = 302.13;
var amount = 99.99;
if (amount < bank_balance) {
console.log( "Я хочу купить этот телефон" );
}
Оператор if
требует выражение между скобками ( )
, которое может быть интерпретировано либо как истина
(true
), либо как ложь
(false
). В этой программе мы написали выражение amount < bank_balance
, которое конечно же будет вычислено как true
или false
в зависимости от значения переменной bank_balance
.
Вы даже можете предоставить альтернативу программе, в случае если условие не будет истинным, описываемую оператором else
. Пример:
const ACCESSORY_PRICE = 9.99;
var bank_balance = 302.13;
var amount = 99.99;
amount = amount * 2;
// может ли мы позволить себе дополнительную покупку?
if ( amount < bank_balance ) {
console.log( "Я возьму этот аксессуар!" );
amount = amount + ACCESSORY_PRICE;
}
// иначе:
else {
console.log( "Нет, спасибо." );
}
Если amount < bank_balance
- истинно
, выведем "Я возьму этот аксессуар!"
и добавим 9.99
в нашу переменную amount
. В противном случае, оператор else
говорит, что мы вежливо ответим "Нет, спасибо."
и оставим переменную amount
без изменений.
Как мы уже обсуждали ранее в главе «Значения и типы», значения, которые не совпадают с ожидаемым типом, часто приводятся к этому типу. Оператор if
ожидает логическое значение
, но если вы передадите что-либо отличное от логического значения
, произойдет неявное приведение типов.
JavaScript определяет список особых значений, которые считаются «как бы ложными», так как при приведении к логическому значению
они примут значение false
; такой список включает в себя 0
и ""
. Любое другое значение, не входящее в список «как бы ложных», автоматически считается «как бы истинным»: при приведении к логическому значению
оно становится равным true
. Истинными, например, являются значения 99.99
и "free"
. См. «Истинный и ложный» в главе 2 для получения более детальной информации.
Условные конструкции существуют и в других формах, отличных от if
. Например, оператор switch
может использоваться в качестве сокращения для последовательности операторов if..else
(см. главу 2). Циклы (см. «Циклы») используют условную конструкцию, чтобы определить завершать выполнение или нет.
Примечание: Детальную информацию о приведениях типов, которые происходят неявно в проверочных выражениях условных конструкций, см. главу 4 книги Типы и синтаксис этой серии.
При большой занятости магазина образуется очередь из покупателей, которым нужно поговорить с сотрудником магазина. Пока в этой очереди есть люди, сотруднику нужно продолжать обслуживать очередного покупателя.
Повторение набора действий пока не нарушится определенное условие, или другими словами, повторение только пока соблюдается условие — это как раз работа для циклов. Циклы могут принимать различные формы, но все они удовлетворяют этому базовому поведению.
Цикл включает в себя проверяемое условие и блок (обычно в виде { .. }
). Процесс каждого выполнения блока в цикле называется итерацией.
Например, цикл while
и цикл do..while
иллюстрируют принцип повторения блока выражений до тех пор, пока условие не перестанет быть равным true
:
while (numOfCustomers > 0) {
console.log( "Чем я могу вам помочь?" );
// помощь покупателю...
numOfCustomers = numOfCustomers - 1;
}
// против:
do {
console.log( "Чем я могу вам помочь?" );
// помощь покупателю...
numOfCustomers = numOfCustomers - 1;
} while (numOfCustomers > 0);
Единственной разницей между этими циклами является проверка условия до первой итерации (while
) или после первой итерации (do..while
).
Если в любом из этих циклов условная конструкция возвратит false
, то следующая итерация не будет выполнена. Это означает, что, если условие изначально будет false
, цикл while
никогда не будет выполнен, а цикл do..while
выполнится только один раз.
Иногда вы используете цикл для подсчета определенного набора чисел, например, от 0
до 9
(десять чисел). Это можно сделать присвоением переменной итерации, например, i
значения 0
, а затем увеличением ее на 1
в каждой итерации.
Предупреждение: По множеству исторических причин языки программирования почти всегда ведут подсчет, начиная с 0
вместо 1
. Если вы не знакомы с таким типом подсчета, поначалу это может сбивать с толку. Уделите некоторое время тому, чтобы попрактиковаться в подсчете, начинающимся с 0
, чтобы освоиться в нем!
Условная конструкция проверяется на каждой итерации, как если бы был неявный оператор if
внутри цикла.
Для выхода из цикла можно использовать оператор break
. К тому же, можно обнаружить, что ужасно легко можно создать цикл, который без механизма break
будет работать вечно.
Проиллюстрируем:
var i = 0;
// цикл `while..true` будет выполняться вечно, не так ли?
while (true) {
// прервать цикл?
if ((i <= 9) === false) {
break;
}
console.log( i );
i = i + 1;
}
// 0 1 2 3 4 5 6 7 8 9
Предупреждение: Показанное выше не является практикой, которой вам необходимо придерживаться при реализации ваших циклов. Это представлено только в иллюстративных целях.
Если while
(или do..while
) может достичь цели вручную, есть еще одна синтаксическая форма, называемая циклом for
, подходящая именно для такой цели:
for (var i = 0; i <= 9; i = i + 1) {
console.log( i );
}
// 0 1 2 3 4 5 6 7 8 9
Как видите, в обоих случаях условная конструкция i <= 9
равна true
для первых 10 итераций (i
принимает значения от 0
до 9
) для любой из форм цикла, но становится равной false
, как только переменная i
становится равной 10
.
У цикла for
есть три составных части: инициализация (var i=0
), проверка условия (i <= 9
) и обновление значения (i = i + 1
). Поэтому, если вы собираетесь заниматься выполнением конкретного количества итераций, for
будет более компактной и зачастую более легкой формой цикла для понимания и записи.
Есть и другие особые формы циклов, которые предназначены для итерирования по особым значениям, таким как свойства объекта (см. главу 2), где неявная проверка условной конструкции — это все ли свойства уже обработаны. Принцип «цикл работает пока не нарушится условие» соблюдается независимо от формы цикла.
Сотрудник магазина, возможно, не носит постоянно с собой калькулятор, чтобы учесть налоги и рассчитать окончательную стоимость покупки. Это задача, которую ему нужно определить один раз и использовать раз за разом. Преимущество в том, что у компании есть контрольно-кассовый аппарат (компьютер, планшет и т.п.), в который эти «функции» уже встроены.
Похожим образом и в вашей программе вам определенно захочется разбить задачи в коде на повторно используемые части, вместо того, чтобы снова и снова однообразно повторять себя. Для реализации этого необходимо определить функцию
.
Обычно функция — это именованная секция кода, которая может быть «вызвана» по имени, и код внутри нее будет при этом запускаться при каждом вызове. Пример:
function printAmount() {
console.log( amount.toFixed( 2 ) );
}
var amount = 99.99;
printAmount(); // "99.99"
amount = amount * 2;
printAmount(); // "199.98"
У функций могут быть аргументы (т.е. параметры) — это значения, которые вы ей передаете. А также функции могут возвращать значение.
function printAmount(amt) {
console.log( amt.toFixed( 2 ) );
}
function formatAmount() {
return "$" + amount.toFixed( 2 );
}
var amount = 99.99;
printAmount( amount * 2 ); // "199.98"
amount = formatAmount();
console.log( amount ); // "$99.99"
Функция printAmount(..)
принимает параметр, который мы назвали amt
. Функция formatAmount()
возвращает значение. Конечно, вы можете комбинировать параметры и возвращаемое значение в одной и той же функции.
Функции часто используются для кода, который вы планируете вызывать несколько раз, но они также полезны для организации связанных частей кода в именованные наборы, даже если вы вызовете их всего лишь раз.
Пример:
const TAX_RATE = 0.08;
function calculateFinalPurchaseAmount(amt) {
// вычисляем новую сумму с налогом
amt = amt + (amt * TAX_RATE);
// возвращаем новую сумму
return amt;
}
var amount = 99.99;
amount = calculateFinalPurchaseAmount( amount );
console.log( amount.toFixed( 2 ) ); // "107.99"
Хотя calculateFinalPurchaseAmount(..)
вызывается только один раз, выделение её поведения в отдельную именованную функцию делает код, использующий её логику (оператор amount = calculateFinal...
), яснее. Если в функции есть несколько операторов, то её преимущества будут более очевидны.
Если вы попросите у продавца модель телефона, которой у магазина нет в продаже, то он не сможет продать вам телефон, который вы хотите. У него есть доступ только к телефонам, которые есть в наличии в магазине. Вам нужно найти другой магазин, чтобы посмотреть, есть ли в нем нужный вам телефон.
В программировании есть термин для этого принципа: область видимости (технически называемая лексическая область видимости). В JavaScript каждая функция получает свою собственную область видимости. Областью видимости является коллекция переменных и правила доступа к этим переменным по имени. Только код внутри функции имеет доступ к переменным, определенным в ее области видимости.
Имя переменной должно быть уникальным в рамках одной и той же области видимости: не может быть двух различных переменных a
, расположенных рядом друг с другом. Но одно и тоже имя переменной a
может появляться в разных областях видимости.
function one() {
// эта `a` принадлежит только функции `one()`
var a = 1;
console.log( a );
}
function two() {
// эта `a` принадлежит только функции `two()`
var a = 2;
console.log( a );
}
one(); // 1
two(); // 2
Также, область видимости может быть вложена внутрь другой области видимости, прямо как клоун на дне рождения надувает один шарик внутри другого. Если одна область вложена в другую, для кода внутри самой внутренней области доступны переменные из окружающей области.
Пример:
function outer() {
var a = 1;
function inner() {
var b = 2;
// здесь у нас есть доступ и к `a`, и к `b`
console.log( a + b ); // 3
}
inner();
// здесь у нас есть доступ только к `a`
console.log( a ); // 1
}
outer();
Правила лексической области видимости говорят, что код в одной области может иметь доступ к переменным как её самой, так и к переменным любой области снаружи этой области.
Таким образом, код внутри функции inner()
имеет доступ к обеим переменным a
и b
, но у кода в outer()
есть доступ только к a
— у него нет доступа к b
, потому что эта переменная внутри inner()
.
Вспомните этот код, который появлялся выше:
const TAX_RATE = 0.08;
function calculateFinalPurchaseAmount(amt) {
// вычисляем новую сумму с налогом
amt = amt + (amt * TAX_RATE);
// возвращаем новую сумму
return amt;
}
Константа (переменная) TAX_RATE
доступна внутри функции calculateFinalPurchaseAmount(..)
, даже несмотря на то, что мы не передавали её внутрь, из-за лексической области видимости.
Примечание: Подробная информация о лексической области видимости есть в первых трех главах книги Область видимости и замыкания этой серии.
Нет абсолютно никакой равноценной замены практике при обучении программированию. Никакое, даже самое ясное, описание с моей стороны само по себе не сделает из вас программиста.
Держа это в уме, давайте попробуем попрактиковаться в некоторых принципах, которые мы изучили в этой главе. Я дам вам «требования», а вы попробуете их реализовать. Затем сверьтесь с кодом, приведенным ниже, чтобы увидеть, как реализовал их я.
- Напишите программу для вычисления общей стоимости покупки телефона. Вы будете продолжать покупать телефоны (подсказка: циклы!), пока у вас не закончатся деньги на банковском счете. Вы также будете покупать аксессуары для каждого из телефонов до тех пор, пока сумма покупки не превысит ваш мысленный предел трат.
- После того, как вы посчитаете сумму покупки, прибавьте налог, затем выведите на экран вычисленную сумму покупки, правильно отформатировав её.
- Наконец, сверьте сумму с балансом вашего банковского счета, чтобы понять можете вы себе это позволить или нет.
- Вы должны настроить некоторые константы для «ставки налога», «цены телефона», «цены аксессуара» и «предела трат», также как и переменную для вашего «баланса банковского счета».
- Вам следует определить функции для вычисления налога и для форматирования цены со знаком валюты и округлением до двух знаков после запятой.
- Бонусная задача: Попробуйте включить ввод данных в вашу программу, например с помощью функции
prompt(..)
, рассмотренной ранее в разделе «Ввод». Вы можете, например, запросить у пользователя баланс банковского счета. Развлекайтесь и будьте изобретательны!
Хорошо, вперед. Попробуйте. Не подсматривайте в мой код, пока сами не попробуете!
Примечание: Так как это книга о JavaScript, очевидно, что я буду решать практические упражнения на JavaScript. Но вы можете сделать это на другом языке, если чувствуете себя в нем более уверенно.
Вот мое решение для этого упражнения, написанное на JavaScript:
const SPENDING_THRESHOLD = 200;
const TAX_RATE = 0.08;
const PHONE_PRICE = 99.99;
const ACCESSORY_PRICE = 9.99;
var bank_balance = 303.91;
var amount = 0;
function calculateTax(amount) {
return amount * TAX_RATE;
}
function formatAmount(amount) {
return "$" + amount.toFixed( 2 );
}
// продолжаем покупать телефоны пока у нас остаются деньги
while (amount < bank_balance) {
// покупаем новый телефон!
amount = amount + PHONE_PRICE;
// можем ли мы позволить себе аксессуар?
if (amount < SPENDING_THRESHOLD) {
amount = amount + ACCESSORY_PRICE;
}
}
// не забудьте заплатить налог
amount = amount + calculateTax( amount );
console.log(
"Ваша покупка: " + formatAmount( amount )
);
// Ваша покупка: $334.76
// можете ли вы в самом деле позволить себе эту покупку?
if (amount > bank_balance) {
console.log(
"Вы не можете позволить себе эту покупку. :("
);
}
// Вы не можете позволить себе эту покупку. :(
Примечание: Простейший способ запустить эту JavaScript программу — набрать её в консоли разработчика в вашем браузере.
Как ваши успехи? Не помешало бы попробовать еще раз после того, как вы увидели мой код. Также попробуйте поиграть с изменением констант, чтобы увидеть работу программы с разными значениями.
Обучение программированию не такой уж сложный и непреодолимый процесс. Есть всего несколько базовых принципов, которые вам нужно уложить у себя в голове.
Они действуют подобно строительным блокам. Чтобы построить высокую башню, вы начинаете класть блок на блок, блок на блок. То же самое и в программировании. Вот несколько необходимых строительных блоков в программировании:
- Вам нужны операции для выполнения действий над значениями.
- Вам нужны значения и типы для выполнения различного рода действий, например, математических с
числом
или вывод сострокой
. - Вам нужны переменные для хранения данных (т.е. состояния) в процессе выполнения программы.
- Вам нужны условные конструкции, такие как оператор
if
, чтобы делать выбор. - Вам нужны циклы, чтобы повторять действия, пока заданное условие не прекратит быть истинным.
- Вам нужны функции для организации вашего кода в логические и повторно используемые части программы.
Комментарии к коду — это весьма эффективный путь к написанию более читаемого кода; они сделают вашу программу понятнее, легче для разработки и поддержания в будущем.
Наконец, не пренебрегайте мощью практики. Лучший способ научиться как писать код — это писать код.
Я рад, что вы теперь на верном пути к изучению написания кода! Так держать! Не забудьте ознакомиться с другими ресурсами по программированию для начинающих (книги, блоги, онлайн-тренировки и т.д.). Эта глава и эта книга являются хорошим стартом, но они — всего лишь краткое введение.
Следующая глава рассмотрит многие принципы из этой главы, но с более специфичной для JavaScript перспективы. Она осветит многие основные темы, которые будут рассматриваться более детально на протяжении оставшихся книг серии.