Роботландский Университет © А.А.Дуванов

## урок 2: переменные, константы, выражения
дополнительный материал

Урок знакомит читателя с базовыми элементами языка:

- Данные
  - переменные
  - константы
- Выражения
- Команды
  - комментарий
  - команда присваивания
  - вызов функции

### переменная

Переменная — это именованная область памяти для хранения данных. Данные могут быть разных типов: целое число, десятичная дробь, логическая константа, текстовая строка. В компилируемых языках (таких, как Си, Паскаль) перед использованием переменной она обязательно описывается (либо явно программистом, либо по умолчанию транслятором). Ведь данные каждого типа по-своему кодируются двоичными нулями и единицами в памяти компьютера, и обработка каждого типа происходит по своим алгоритмам.

В интерпретируемом языке JavaScript нет описателей типов данных (таких, как `int`, `float`, `char` в Си). Переменная описывается ключевым словом `var`, которое не вносит никакой типизации. Описатель `var` просто фиксирует именованную область для хранения данных, не приписывая этой области никакого типа. Это не означает, конечно, что JavaScript одинаково работает с любыми данными. Это означает лишь, что тип переменной определяется по типу присваиваемого значения и может много раз меняться в процессе работы программы:

    var x;         // Описана переменная
                   // (фиксируется область для хранения данных).
    x = 10;        // Сейчас переменная хранит целое число 10.
    x = "10";      // Сейчас переменная хранит строку "10".
    x = 30+10;     // Сейчас переменная хранит число 40.
    x = 30+"10";   // Сейчас переменная хранит строку "3010".
    x = 30-"10";   // Сейчас переменная хранит целое число 20.
    x = "30"-10;   // Сейчас переменная хранит целое число 20.
    x = "30"-"10"; // Сейчас переменная хранит целое число 20.

Заметьте, что в выражении `30+"10"` число 30 преобразуется в строку, а операция “+” рассматривается как операция конкатенации (соединение двух строк).

В выражении `30-"10"` строка `"10"` преобразуется в число 10, после чего выполняется вычитание. Аналогично в выражении `"30"-10` строка `"30"` преобразуется в число.

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

Там, где нет полной уверенности, лучше пользоваться явным преобразованием при помощи встроенных в язык функций `parseInt` и `parseFloat` (урок 5):

    var y = "30";
    x = parseInt(y,10)-10;

### описание переменных

Несмотря на то, что JavaScript позволяет описывать переменные в любом месте, и даже не описывать их вовсе, описания нужно делать обязательно и собирать их в одном месте (в начале функции, скрипта). Если переменная не описана программистом явно, браузер описывает ее (заводит под нее область памяти) самостоятельно при первом упоминании. При этом переменная считается глобальной.

Глобальная переменная доступна в любой части кода текущего HTML-файла. А это очень плохо. Легко случайно испортить ее значение.

Примите на вооружение полезное правило: “переменные нужно описывать всегда”.

Если действительно нужна глобальная переменная, опишите ее явно в первом блоке `<SCRIPT>…</SCRIPT>`, разместив этот блок между тегами `<HEAD>…</HEAD>`. Глобальные описания должны быть самыми первыми в этом блоке и обязательно вне функций.

Цепочка из нескольких блоков `<SCRIPT>…</SCRIPT>` равнозначна одному большому скрипту (в смысле видимости переменных и функций).

Переменные, описанные вне функций, видны всюду. Такие описания считаются глобальными.

### имена переменных

Выбор имени очень важен. Вот несколько советов.

1. Не именуйте “с потолка”.
2. Не именуйте одной буквой.
3. Избегайте аббревиатур и сокращений.
4. Не именуйте зарезервированными словами языка.

Из приведенных правил 2 и 3 есть очевидные исключения:

`i, j` | — | счетчики циклов
`col` | — | индекс столбца
`row` | — | индекс строки
`cur` | — | текущее значение
`max` | — | максимум (обычно в качестве префикса или суффикса: `maxCol`)
`min` | — | минимум (обычно в качестве префикса или суффикса: `ValueMin`)
`s, str` | — | строка (если нельзя ее назвать более содержательно)
`buf` | — | буфер (временное хранилище)
`len` | — | длина (обычно как префикс или суффикс: lenStr)
`ret` | — | возвращаемое из функции значение
`sum` | — | сумма

Из правил 1 и 4 исключений быть не может!

Достаточно сложно уследить за всеми ключевыми словами языка и легко попасть в ловушку.

Однажды Сидоров назвал функцию, обрабатывающую щелчок на кнопке “Старт”, очень подходящим именем `start`, и в браузерах четвертых версий все работало прекрасно. После появления Microsoft Internet Explorer 5 на Сидорова посыпались рекламации: гипертекстовое приложение не работало. Причина оказалась именно в имени `start`, которое в IE-5 стало ключевым словом.

С тех пор Сидоров принял для себя правило: добавлять к “нормальным” именам один и тот же предохранительный суффикс. Он переименовал `start` в `startRU`, `pointer` в `pointerRU` и все стало прекрасно работать.

5. Не бойтесь длинных имен! Хотя они и увеличивают объем программы.

Мера, конечно, нужна во всем. Но я за понятные программы в ущерб некоторой эффективности. Как именовать Горбункова Семена Семеновича?

Есть два способа:

1. `ГорбунковСеменСеменыч` (`горбунковСеменСеменыч`)
2. `горбунков_семен_семеныч`

Представьте, что мы оставили бы от Семена Семеновича одну аббревиатуру `гсс` — никто бы Горбункова и не признал. С трудом бы узнали его и за шифром `ГорСеСе`. Какой способ именования из a) и b) вам больше по душе? Выбирайте! Но старайтесь придерживаться одной нотации на протяжении всей программы, а лучше — всего программирования.

6. Избегайте ненужных имен.

Это вариант уродлив и избыточен:

    var ret = "ok!";
    if(...хорошо...) return ret;

Лучше записать так:

    if(...хорошо...) return true;

6. Избегайте магических чисел.

Константы в программах нельзя оставлять без имен.

При суммировании всех элементов массива `set` лучше всего воспользоваться свойством `set.length`, указывающим длину массива:

    // Суммирование всех элементов массива.
    // set.length - свойство массива: его длина.
    var sum = 0;
    for(var i=0; i<set.length; i++) sum += set[i];

Суммирование первой сотни элементов можно задать так:

    var bonus = new Array(....); // Массив оценок.
    var important_length = 100;  // Число важных оценок (они идут первыми).
    var middle_important_bonus;  // Средняя оценка среди важных.
    // Суммирование важных оценок.
    middle_important_bonus = 0;
    for(var i=0; i<important_length; i++)
      middle_important_bonus += bonus[i];
    // Нахождение средней оценки среди важных.
    middle_important_bonus /= important_length;

Что это дает?

Если программа почему-то работает с первыми 100 числами массива, то, вероятно, в этом есть сермяжная правда. И это число “100” будет встречаться в программе много раз (у нас уже два раза).

Если через месяц важными окажутся не 100 элементов, а 200, придется кропотливо перечитывать уже забытый код, чтобы всюду заменить 100 на 200. Много шансов, что некоторые замены не будут выполнены (там, например, где стоит 99, которое означает `important_length-1`), а некоторые будут выполнены неправильно (например, число 100, означающее проценты, заменится на 200).

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

“Магических” чисел в программе быть не должно, но 0 и 1 — очевидные исключения.

В этом суммировании массива:

    var sum = 0;
    for(var i=0; i<set.length; i++) sum += set[i];

И в этом нахождении произведения:

    var num = 10;
    var factorial = 1;
    for(var i=1; i<=num; i++) factorial *= i;

константы 0 и 1 — в самом деле, числа магические! Именовать их не надо!

Существует практика именования переменных русскими словами, записанными латинскими буквами. С одной стороны, в этом нет ничего плохого:

    // Возвращает НОД чисел a и b.
    function Nod(a,b)
    {
     .....
    }
    var summa = 0;  // Сумма элементов массива.
    var dlina = 10; // Длина актуальной части массива.
    var poz;        // Текущая позиция в строке.

Однако, в таком виде удается написать гораздо меньше красивых и понятных имен, чем на английском языке. Сравните:

    var counter;   // Счетчик чего-то...
    var schetchik; // Счетчик чего-то...

    var bonus;     // Оценка за урок.
    var ozenka;    // Оценка за урок.

    function number_of_elements()   // Число элементов.
    function chislo_elementov()     // Число элементов.

    function error_message()        // Сообщение об ошибке.
    function soobchenie_ob_osibke() // Сообщение об ошибке.

Совет такой: на этапе написания кода держите под рукой маленький русско-английский словарь и пользуйтесь им при выборе подходящего имени. Заодно потихоньку пойдет обучение словам, которые все равно нужно знать по-английски тем, кто занимается программированием и компьютерами.

И еще: хорошей практикой является комментирование имен при их описании.

Конечно, не надо комментировать переменную цикла! Описание остальных имен надо помещать в одном месте (в начале) и сопровождать максимально подробными комментариями.

Описание функции нужно также предварять подробными комментариями, в которых обязательно указывать:

- назначение функции;
- аргументы и их допустимые значения;
- возвращаемые значения;
- особенности работы функции.

Пример:

    // Вычисление НОД(а,b).
    // Вход: a и b - натуральные числа.
    // Выход: НОД(а,b) или 0, если a и b не являются допустимыми.
    function Nod(a,b)
    {
      ...
    }

Но даже несмотря на комментарии, для выбора содержательных, понятных и красивых имен не надо жалеть времени. Ведь, если одна переменная называется `kfrm`, а другая `krfm`, то читать программу практически невозможно, даже если эти переменные подробным образом откомментированы в начале скрипта.

### команда присваивания

Команда присваивания имеет вид: `переменная = выражение`. В любых языках программирования эта конструкция работает так:

1. вычисляется выражение;
2. результат становится значением переменной.

Рассмотрим сложный пример из задания 2g зачетного класса урока:

    var x = 13;
    var y = 3;
    y = x++%y++;

Работает это так:

1. Вычисляется арифметическое выражение `x++%y++`
   - сначала вычисляется `x%y` (операции `++` здесь работают после использования переменных `x` и `y`): `13%3` равно 1;
   - выполняются операции `++` (ведь они в составе арифметического выражения, значит, должны быть выполнены до присваивания): значение `x` теперь равно 14, а значение `y` равно 4.
2. Выполняется присваивание `y = 1` (прежнее значение 4 пропадает).

Теперь переменная `x` имеет значение 14, а переменная `y` — значение 1.

### операция присваивания

Сюрприз для тех, кто пишет на Паскале.

Оказывается, в Си и вслед за ним в JavaScript, команда присваивания `переменная = выражение` одновременно является бинарной операцией. Результат этой операции — значение, полученное переменной.

Если мы пишем:

    x = 1;
    y = x+1;

— то можно и не думать о второй ипостаси команды. Но эти две команды можно кодировать как одну: `y = (x = 1) + 1;`

Работает это так:

1. Сначала выполняется `(x = 1)`. Переменная x получает значение 1. Но этого мало! Вместо скобки `(x = 1)` в выражение подставляется результат выполнения операции, то есть число 1.
2. Теперь вычисляется выражение `1 + 1` и переменная `y` получает значение 2.

А что, если опустить скобки:

    y = x = 1 + 1;

Что произойдет теперь?

1. Браузер выдаст сообщение о синтаксической ошибке?
2. Переменные `x` и `y` получат значение 2?
3. Переменная `x` получит значение 1, а `y` — значение 2?

Правильный ответ дает второй вариант, то есть `x` и `y` получат значения 2.

Из таблицы приоритетов операций JavaScript (смотрите справочный раздел в конце книги) следует:

- приоритет операции “`=`” — самый низкий;
- операции “`=`” выполняются справа налево.

Это означает, что в последнем примере:

1. сначала выполнится операция сложения;
2. результат 2 присвоится переменной `x`;
3. переменная `y` получит значение результата предыдущей операции присваивания, то есть число 2.

Команды:

    переменная += выражение;
    переменная -= выражение;
    переменная *= выражение;
    переменная /= выражение;
    переменная %= выражение;

тоже имеют статус операции. Значение этой операции совпадает со значением, которое получает переменная.

В следующем примере:

    var x=1;
    var y;
    y = (x+=2)+1;

переменная `y` получит значение 4, а переменная `x` — значение 3.

Наиболее популярно использования операции “`=`” в цепочке присваиваний:

    x = y = z = t = выражение;

Здесь нескольким переменным присваивается одно и тоже значение.

Можно увидеть и такие записи:

1. `y = f(x=1);`
2. `z = k(x+=1);`
3. `x=1; y=2; y += x+=1+y;`
4. `y = !(x=2);`

Подумайте, что они означают!

А вот настоящее “безумие”:

    y = ++(x=1);

К счастью, оно не работает (браузер выдает сообщение об ошибке). Операции “`++`” и “`--`” применимы только к переменным, но не к выражениям.