Опубликован: 10.12.2007 | Доступ: свободный | Студентов: 822 / 20 | Оценка: 5.00 / 5.00 | Длительность: 58:33:00
Лекция 5:

Скрипты

5.3.1.4. Переменные

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

my_variable x counter5 interCapName not$common _secret

Соглашения об именах рекомендуют использовать для имен констант только заглавные буквы, разделяя слова подчеркиванием (как в Java); имена с единственной заглавной буквой в начале следует использовать для конструкторов объектов; подчеркивание в начале имени должно сигнализировать о том, что переменная не предназначена для свободного применения.

Переменные не определены, если они не объявлены с ключевым словом var. Если они используются, не будучи объявленными, возникает синтаксическая ошибка. Если они объявлены, но им ничего не присваивается, они остаются неопределенными. Таким образом, переменные определяются при объявлении или при первом присваивании - это зависит от того, что случится раньше. В JavaScript переменные необязательно инициализировать константными выражениями, как в C. Для первого присваивания может использоваться любое выражение, а переменная может быть объявлена в любом месте скрипта.

var x;
var y = 2, z = 3.45;
var product = x * y * z;

Каждая переменная JavaScript хранит один элемент данных. Этот элемент - или простое значение или ссылка на объект. В разделе "Объекты" объясняется также, что переменные являются и свойствами. В JavaScript нет указателей и синтаксиса для явного использования ссылок. Имена переменных не могут совпадать с зарезервированными словами вроде if. Переменная с именем this - особая и всегда указывает на текущий объект.

5.3.1.5. Массивы

В JavaScript, как и в C, поддерживаются одномерные массивы, но их размер может задаваться и неконстантным выражением. Массивы создаются с помощью ключевого слова new, которое также используется при создании самых разных объектов. Вот несколько вариантов создания массивов:

var arr1 = new Array();             // массив нулевой длины
var arr2 = new Array(5);            // массив из 5 элементов
var arr3 = new Array(11,12,13);     // массив из 3 элементов
var arr4 = new Array(2,"red",true); // массив из 3 элементов

Все элементы массива не определены, если только их содержимое не задано при создании массива. Каждый элемент массива может хранить данные любого типа. Массив также можно создать с помощью литерала, заключенного в квадратные скобки, [ ]. Следующие примеры совпадают с предыдущими и часто предпочтительны, так как метод Array() выглядит не очень красиво и часто вносит путаницу:

var arr1 = [];              // массив нулевой длины
var arr2 = [, , , , ,];     // массив из 5 элементов
var arr3 = [11,12,13];      // массив из 3 элементов
var arr4 = [2,"red",true];  // массив из 3 элементов

Литералы массивов могут быть вложенными, так что элементы массивов могут сами быть массивами:

var arr5 = [ 6, ["red","blue"], 8, [], 10];

На элементы массивов можно ссылаться по их индексам, которые начинаются с 0. Свойство массива length - целое число, большее индекса самого последнего элемента в массиве на один. Оно не равно числу элементов массива и обновляется автоматически:

a[0];           // первый элемент массива a
b[2];           // третий элемент массива b
c.length;       // на один больше самого большого индекса в c
c[c.length-1];  // последний элемент массива c
d[1][4];        // см. ниже

В последней строке предыдущего примера используется массив d, чей второй элемент, d[1] сам является массивом. Следовательно, d[1][4] - пятый элемент массива d[1].

Размер массивов не фиксирован. Свойство length можно менять, или присваивая ему какое-то значение, или определяя элемент с большим индексом.

arr1 = new Array(3);    // length - 3
arr1.length = 5;        // теперь length - 5;
arr1[8] = 42;           // теперь length - 9;

Массивы разреженные. В предыдущем примере при определении элемента с индексом 8 элементы между 5 и 8 не создавались, если, конечно, они не использовались в последующих инструкциях. Так как доступны индексы из всего диапазона 32-битных беззнаковых целых, зазоры между элементами могут быть очень большими.

Немного забегая вперед, скажем, что массивы также являются объектами (типа Array ). Все объекты поддерживают синтаксис, позволяющий им вести себя как массивам. При этом объект представляется как массив без свойства length ; просто разрешается использовать нотацию квадратных скобок. Такой синтаксис может быть полезен при поиске свойств во всех объектных данных, но эта гибкость не дает преимуществ массивам, только другим объектам, как видно из примера:

obj.prop_name == obj["prop_name"]   // корректно и всегда истина
obj[1] != obj.1                     // некорректный синтаксис

Синтаксис в первой строке справа от знака равенства полезен, если нужно создать свойство объекта, чье имя не может быть корректным именем переменной. Например:

obj["A % overhead"] = 20;

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

obj[12.35] == obj["12.35"];

Результатом этого примера будет задание свойства объекта, а не элемента массива, так как индексов с плавающей запятой не существует. Обычно индексы массивов хранятся в переменных. Если в результате каких-то вычислений индекс преобразован из целого в число с плавающей запятой, это преобразование типов может пройти неявно. Обнаружить его будет сложно, потому что заданное таким образом свойство будет, вероятно, использоваться осмысленно до тех пор, пока нецелое не будет округлено или из-за погрешностей в вычислениях не изменится дробная часть. В этом случае произойдет преобразование в другую строку, которая будет указывать на другое свойство. При этом присвоенное свойству значение также нельзя будет узнать, обратившись к нему обычным способом, так как имя 12.35 не является корректным именем переменной. Мораль такова: при работе с индексами пользуйтесь простыми вычислениями.

5.3.1.6. Выражения

Выражения JavaScript довольно точно соответствуют правилам выражений в C, C++ и Java и предоставляют возможности выполнять арифметические и логические вычисления, битовые операции и некоторые операции над объектами. Выражения составляются из переменных, литералов и операторов, перечисленных в таблице 5.1.

Таблица 5.1. Операторы JavaScript
Имя Бинарный Приоритет Символ
Принудительно задать высший приоритет Унарный 0 ()
Литерал массива Унарный 0 []
Литерал объекта Унарный 0 {}
Вызов функции Унарный 0 ()
Свойство объекта 1 .
Элемент массива Бинарный 1 []
Литерал объекта Унарный 1 {}
Создание объекта Унарный 2 new
Удалить ссылку на свойство Унарный 3 delete
Преобразовать в неопределенное Унарный 3 void
Получить строку, соответствующую типу Унарный 3 typeof
Пре- и постинкремент Унарный 3 ++
Пре- и постдекремент Унарный 3 --
Тот же знак Унарный 3 +
Противоположный знак Унарный 3 -
32-битное побитовое отрицание Унарный 3 ~
Логическое отрицание Унарный 3 !
Умножение 4 *
Деление 4 /
Остаток от деления 4 %
Сложение, конкатенация 5 +
Вычитание 5 -
32-битный сдвиг влево 6 <<
32-битный знаковый сдвиг вправо 6 >>
32-битный беззнаковый сдвиг вправо 6 >>>
Совпадает с данным типом 7 instanceof
Совпадает со свойством объекта 7 in
Порядковые сравнения 7 < > <= >=
Равенство 7 == !=
Строгое равенство 7 === !==
32-битное побитовое И 8 &
32-битное побитовое исключающее ИЛИ 9 ^
32-битное побитовое ИЛИ 10 |
Логическое И 11 &&
Логическое ИЛИ 12 ||
Условное выражение Тернарный 13 ?:
Простое присваивание 14 =
Составное присваивание 14 *= /= %= += -= <<= >>= >>>= &= ^= |=
Разделитель элементов списка 15 ,

Приоритет 0 - самый высокий. Для операторов с одинаковым приоритетом JavaScript грубо следует принятым в C соглашениям о вычислениях слева направо и справа налево. Он также поддерживает упрощенное вычисление булевых выражений, то есть если выражение состоит из нескольких операций && и ||, они вычисляются слева направо только до тех пор, пока не будет понятно, что окончательный результат уже не изменится, а не до тех пор, пока не будет вычислено последнее условие.

Одна область, в которой логика работы с булевыми выражениями JavaScript ближе к Perl, чем к C - многозначная семантика. По этому соглашению выражения с && и || играют роль управляющих конструкций наподобие ?:, а не обычных булевых выражений. Поэтому в инструкции

var x = flag && y;

переменная x принимает значение y, если значение flag - true, иначе она принимает значение false. В C переменная x как значение принимала бы результат вычисления булевого выражения " flag и y ".

В математических выражениях смесь операндов типа Number, хранящихся как целых и как нецелых, приведет к тому, что результат будет нецелым. Попытка применить побитовые операции к нецелым числам приведет к тому, что эти числа будут обрезаны до 32 бит в общем бесполезным способом. Убедитесь, что побитовые операции выполняются только для тех операндов типа Number, которые хранятся как целые.

5.3.1.7. Управление

В JavaScript поддерживаются управляющие конструкции в стиле C. Ниже приведены стандартные формы их использования, в которых место как составных, так их простых инструкций отмечено как инструкция.

if (выражение) инструкция
if (выражение) инструкция else инструкция
while (выражение) инструкция
do инструкция while (выражение)
for (выражение; выражение; выражение) инструкция
switch (выражение) {
case выражение: инструкция; break;
case выражение: инструкция; break; // столько раз, сколько нужно
default: инструкция; break;
}

Аргументом switch() может быть что угодно, не только переменная. Селекторами case также могут быть не только литералы. Следующие две if -конструкции эквивалентны:

if (a) инструкция else if (b) инструкция else инструкция
if (a) инструкция else {if (b) инструкция else инструкция}

Как и во многих языках с C-подобным синтаксисом, стоит опасаться ловушки с if -конструкциями, когда else -часть относится к последнему if независимо от расстановки отступов; эта ловушка во втором случае исчезает благодаря использованию явного синтаксиса.

Для инструкции for есть вариация, которая позволяет проходить по свойствам объекта JavaScript. При этом просматриваются все свойства, не являющиеся DontEnum (см. раздел 8.6.1 стандарта ECMA-262):

for ( имя-переменной in объект ) инструкция

В JavaScript нет инструкции goto. В нем есть метки, чьи имена соответствуют именам переменных, но они находятся в отдельном пространстве имен. continue завершает текущую итерацию цикла; break прерывает выполнение цикла или выходит из инструкции switch. Метки можно использовать для прерывания нескольких вложенных друг в друга циклов:

mylabel: инструкция;
break;
break label;
continue;
continue label;

В JavaScript есть также система исключений. Это не необязательный довесок, а часть ядра языка. Она перехватывает ошибки и исключения при исполнении.

try { инструкция; }
catch (переменная) { инструкция; }
finally { инструкция; }

Блоков catch может быть несколько. Блок finally необязателен. Для генерации исключения используется throw - или внутри блока try или где угодно:

throw выражение;

Созданное исключение может сравниваться с данными любого типа, начиная от обычного числа и заканчивая сложным специально созданным объектом. Чтобы имитировать исключения, генерируемые XPConnect-частью платформы, нужно всегда генерировать 32-битное целое, желательно одно из значений объекта Components.results.

Скрипты часто пишутся на скорую руку с целью быстро добиться какого-то результата, да и обработка исключений обычно не самая понятная часть в языках третьего поколения. На практике же в надежном скрипте большая часть обработки должна находиться в блоках try, так как исключения никогда не должны появляться перед пользователем. Самый простой и эффективный способ гарантировать это - поместить все в один большой блок try на самом верхнем уровне.

Инструкция with рассматривается в разделе "Область видимости".

5.3.1.8. Функции

JavaScript поддерживает функции. Функции нетипизированы и могут работать с переменным числом аргументов, как, например, printf() в C. Функции могут не иметь имени, тогда они анонимны, см. ниже. В листинге 5.1 показана типичная функция.

function sum(x, y) {
  if (arguments.length != 2) {
    return void 0;
  }
  return x + y;
}
var a = sum(2,3);		// a = 5
var b = sum(1,2,3); 		// b = undefined
var c = sum("red","blue"); 	// c = "redblue"
var d = sum(5, d); 		// d = 5 + undefined = NaN
var e = sum; 			// e теперь функция
var f = e(3,4); 		// f = 7
Листинг 5.1. Синтаксис обычной функции JavaScript

Объект arguments аналогичен объекту Array за исключением того, что он статичен: при добавлении к нему элементов свойство length не изменится. Этот объект содержит все аргументы, переданные функции. Функции также могут быть анонимными:

var plus = function (x,y) { return x + y; }
var a = plus(2,3);

Достоинство анонимных функций заключается в том, что они не создают автоматически дополнительную переменную с именем функции. Следовательно, можно создавать методы для объектов без "шлейфа" глобально определенных имен функций. Глобально определенных имен функций также можно избежать, поместив определение именованной функции в выражение:

var five = (function sum(a,b){return a+b;})(2,3);

Если функция вызывается сама по себе, а не как метод объекта, тогда смысл ключевого слова this выясняется в соответствии с правилами определения области видимости.

5.3.1.9. Регулярные выражения

JavaScript поддерживает регулярные выражения Perl5 с некоторыми мало заметными отличиями. Эти неявные отличия существуют, так как синтаксис регулярных выражений очень чувствителен к малейшим неточностям, находится в постоянном развитии и еще до конца не устоялся. В UNIX-системах есть такие варианты регулярных выражений: для работы с файлами, обычный и расширенный. Perl и JavaScript поддерживают расширенные регулярные выражения, которые очень грубо соответствуют egrep(1) или параметру "Wildcards" окна поиска в Microsoft Word. Написанное в справочной странице ( man(1) ) Perl, perlre, понять легче, чем текст определения ECMAScript, но ненамного. Поищите какое-нибудь руководство в Internet.

Все операции над регулярными выражениями в JavaScript - методы объекта String или RegExp; они не существуют самостоятельно, как оператор m// в Perl:

match(re)               // "red".match(/e/) == ["e"];
replace(re,string)      // "red".replace(/e/,"o") == "rod";
replace(re,function)    // "red".replace(/e/,myfn);
search(re)              // "red".search(/e/) == 1;
split(re)               // "red".split(/e/) == ["r","d"];

replace() возвращает строку; search() - число; split() и match() возвращают массивы строк.

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

/выражение/флаги

Здесь выражение - любое запутанное регулярное выражение; флаги - ноль или более число g (заменять везде), i (не обращать внимания на регистр) и m (в многострочных целях рассматривать каждую строку отдельно).