В главе 4 выражения были названы фразами языка JavaScript. По аналогии инструкции можно считать предложениями на языке JavaScript, или командами. Как в обычном языке предложения завершаются и отделяются друг от друга точками, так же и инструкции JavaScript завершаются точками с запятой (раздел 2.5). Выражения вычисляются и возвращают значение, а инструкции выполняются, чтобы что-то происходило.
Чтобы «что-то происходило», можно вычислить выражение, имеющее побочные эффекты. Выражения с побочными эффектами, такие как присваивание и вызовы функций, могут играть роль самостоятельных инструкций – при таком использовании их обычно называют инструкциями-выражениями. Похожую категорию инструкций образуют инструкции-объявления, которые объявляют новые переменные и определяют новые функции.
Программы на языке JavaScript представляют собой не более чем последовательности выполняемых инструкций. По умолчанию интерпретатор JavaScript выполняет эти инструкции одну за другой в порядке их следования. Другой способ сделать так, чтобы «что-то происходило», заключается в том, чтобы влиять на этот порядок выполнения по умолчанию, для чего в языке JavaScript имеется несколько инструкций, или управляющих конструкций, специально предназначенных для этого:
• Условные инструкции, такие как if
и switch
, которые заставляют
интерпретатор JavaScript выполнять или пропускать другие инструкции в зависимости
от значения выражения.
• Инструкции циклов, такие как while
и for
, которые многократно выполняют
другие инструкции.
• Инструкции переходов, такие как break, return
и throw
, которые заставляют
интерпретатор выполнить переход в другую часть программы.
В разделах, следующих далее, описываются различные инструкции языка JavaScript и их синтаксис. Краткая сводка синтаксиса инструкций приводится в табл. 5.1, в конце главы. Программа на языке JavaScript – это просто последовательность инструкций, разделенных точками с запятой, поэтому, познакомившись с инструкциями JavaScript, вы сможете писать программы на этом языке.
Простейший вид инструкций в JavaScript – это выражения, имеющие побочные эффекты. (Загляните в раздел 5.7.3, где описывается инструкция–выражение, не имеющая побочных эффектов.) Инструкции такого рода мы рассматривали в главе 4. Основной категорией инструкций–выражений являются инструкции присваивания. Например:
greeting = "Hello " + name;
i *= 3;
Операторы инкремента и декремента ++ и -- схожи с инструкциями присваивания. Их побочным эффектом является изменение значения переменной, как при выполнении присваивания:
counter++;
Оператор delete
имеет важный побочный эффект – он удаляет свойство объекта.
Поэтому он почти всегда применяется как инструкция, а не как часть более
сложного выражения:
delete o.x;
Вызовы функций – еще одна большая категория инструкций–выражений. Например:
alert(greeting);
window.close();
Эти вызовы клиентских функций являются выражениями, однако они имеют побочный эффект, заключающийся в воздействии на веб-браузер, поэтому также могут использоваться в качестве инструкций. Если функция не имеет каких-либо побочных эффектов, нет смысла вызывать ее, если только она не является частью более сложного выражения или инструкции присваивания. Например, никто не станет просто вычислять косинус и отбрасывать результат:
Math.cos(x);
Наоборот, надо вычислить значение и присвоить его переменной для дальнейшего использования:
cx = Math.cos(x);
Обратите внимание, что каждая строка в этих примерах завершается точкой с запятой.
Подобно оператору запятой (раздел 4.13.5), объединяющему несколько выражений в одно выражение, блок инструкций позволяет объединить несколько инструкций в одну составную инструкцию. Блок инструкций – это просто последовательность инструкций, заключенная в фигурные скобки. Таким образом, следующие строки рассматриваются как одна инструкция и могут использоваться везде, где интерпретатор JavaScript требует наличия единственной инструкции:
{
x = Math.PI;
cx = Math.cos(x);
console.log("cos(π) = " + cx);
}
Здесь есть несколько аспектов, на которые следует обратить внимание. Во-первых, составная инструкция не завершается точкой с запятой. Отдельные инструкции внутри блока завершаются точками с запятой, однако сам блок – нет. Во-вторых, строки внутри блока оформлены с отступами относительно фигурных скобок, окружающих их. Это не является обязательным требованием, но подобное оформление программного кода упрощает его чтение и понимание. Наконец, напомню, что в языке JavaScript не поддерживается область видимости блока, поэтому переменные, объявленные внутри блока, не являются частными по отношению к этому блоку (подробности смотрите в разделе 3.10.1).
Объединение инструкций в более крупные блоки инструкций используется в языке JavaScript повсеместно. Подобно тому как выражения часто включают другие подвыражения, многие инструкции JavaScript могут содержать другие инструкции. Формальный синтаксис языка JavaScript обычно позволяет использовать не более одной подынструкции. Например, синтаксис инструкции цикла while включает единственную подынструкцию, которая служит телом цикла. Блоки инструкций позволяют помещать любое количество инструкций там, где требуется наличие единственной подынструкции.
Составные инструкции позволяют использовать множество инструкций там, где синтаксис JavaScript допускает только одну инструкцию. Пустая инструкция действует противоположным образом: она позволяет не вставлять инструкции там, где они необходимы. Пустая инструкция имеет следующий вид:
;
Встретив пустую инструкцию, интерпретатор JavaScript не выполняет никаких
действий. Пустая инструкция может оказаться полезной, когда требуется
создать цикл с пустым телом. Взгляните на следующий цикл for
(циклы for
будут
рассматриваться в разделе 5.5.3):
// Инициализировать массив a
for(i = 0; i < a.length; a[i++] = 0);
В этом цикле вся работа выполняется выражением a[i++] = 0
, и тело цикла здесь
не требуется. Однако синтаксис JavaScript требует, чтобы цикл имел тело,
поэтому здесь использована пустая инструкция – просто точка с запятой.
Обратите внимание, что ошибочное добавление точки с запятой после
закрывающей круглой скобки в инструкции for, while
или if
может вызывать появление
досадных ошибок, которые сложно обнаружить. Например, следующий
фрагмент наверняка будет делать не совсем то, что предполагал автор:
Если вы собираетесь намеренно использовать пустую инструкцию, нелишним будет добавить комментарий, поясняющий ваши намерения. Например:
for(i = 0; i < a.length; a[i++] = 0) /* пустое тело цикла */ ;
Инструкции var
и function
являются инструкциями-объявлениями – они
объявляют, или определяют, переменные и функции. Эти инструкции определяют
идентификаторы (имена переменных и функций), которые могут использоваться
повсюду в программе, и присваивают значения этим идентификаторам.
Инструкции-объявления сами ничего особенного не делают, но, создавая переменные
и функции, они в значительной степени определяют значение других инструкций
в программе.
В подразделах, следующих ниже, описываются инструкции var
и function
, но они
не дают исчерпывающего описания переменных и функций. Более подробная
информация о переменных приводится в разделах 3.9 и
3.10, а полное описание
функций – в главе 8.
Инструкция var
позволяет явно объявить одну или несколько переменных.
Инструкция имеет следующий синтаксис:
var имя_1 [ = значение_1] [ имя_n [= значение_n]]
За ключевым словом var
следует список объявляемых переменных через
запятую; каждая переменная в списке может иметь специальное
выражение-инициализатор, определяющее ее начальное значение. Например:
Если инструкция var
находится внутри тела функции, она определяет локальные
переменные, видимые только внутри этой функции. При использовании в
программном коде верхнего уровня инструкция var
определяет глобальные
переменные, видимые из любого места в программе. Как отмечалось в разделе 3.10.2,
глобальные переменные являются свойствами глобального объекта. Однако, в
отличие от других глобальных свойств, свойства, созданные с помощью инструкции
var
, нельзя удалить.
Если в инструкции var
начальное значение переменной не указано, то
переменная определяется, однако ее начальное значение остается неопределенным
(undefined
). Как описывалось в разделе 3.10.1, переменные определены во всем
сценарии или в функции, где они были объявлены, – их объявления «поднимаются»
в начало сценария или функции. Однако инициализация переменной
производится в той точке программы, где находится инструкция var
, а до этого
переменная имеет значение undefined
.
Обратите внимание, что инструкция var
может также являться частью циклов
for
и for/in
. (Объявления этих переменных так же поднимаются в начало
сценария или функции, как и объявления других переменных вне цикла.) Ниже
повторно приводятся примеры из раздела 3.9:
var o = {l1:2,l2:",",l3:7,l4:1,l5:8,l6:2,l7:8,l8:1,l9:8,l10:2,l11:8};
for(var i = 0; i < 10; i++) console.log(i);
for(var i = 0, j=10; i < 10; i++,j--) console.log(i*j);
for(var i in o) console.log(i);
for(var i in o) console.log(o[i]);
Отметьте также, что допускается несколько раз объявлять одну и ту же переменную.
Ключевое слово function
в языке JavaScript используется для определения
функций. В разделе 4.3 мы уже встречались с выражением определения функции. Но
функции можно также определять в форме инструкций. Взгляните на
следующие две функции:
Объявление функции в форме инструкции имеет следующий синтаксис:
Здесь имя_функции
– это идентификатор, определяющий имя объявляемой
функции. За именем функции следует заключенный в скобки список имен
аргументов, разделенных запятыми. Эти идентификаторы могут использоваться в теле
функции для ссылки на значения аргументов, переданных при вызове функции.
Тело функции состоит из произвольного числа JavaScript-инструкций,
заключенных в фигурные скобки. Эти инструкции не выполняются при определении
функции. Они просто связываются с новым объектом функции для выполнения
при ее вызове. Обратите внимание, что фигурные скобки являются обязательной
частью инструкции function
. В отличие от блоков инструкций в циклах while
и в других конструкциях, тело функции требует наличия фигурных скобок,
даже если оно состоит только из одной инструкции.
Ниже приводится несколько примеров определений функций:
Инструкции объявления функций могут находиться в JavaScript-коде верхнего
уровня или быть вложенными в определения других функций, но только на
«верхнем уровне», т. е. объявления функций не могут находиться внутри
инструкций if
, циклов while
или любых других конструкций. Из-за такого
ограничения, накладываемого на объявления функций, спецификация ECMAScript не
относит объявления функций к истинным инструкциям. Некоторые реализации
JavaScript позволяют вставлять объявления функций в любые инструкции, но
разные реализации по-разному обрабатывают эти случаи, поэтому включение
объявлений функций в другие инструкции снижает переносимость программ.
Инструкция объявления функции (function f(x) {})
отличается от выражения определения функции (var f = function(x) {})
тем, что она
включает имя функции. Обе формы создают новый объект function
, но инструкция
объявления функции при этом объявляет имя функции – переменную, которой
присваивается объект function
. Подобно переменным, объявляемым с помощью
инструкции var
, объявления функций, созданные с помощью инструкции
function
, неявно «поднимаются» в начало содержащего их сценария или функции,
поэтому они видимы из любого места в сценарии или функции. Однако при
использовании инструкции var
поднимается только объявление переменной, а
инициализация остается там, куда ее поместил программист. В случае же с инструкцией
function
поднимается не только имя функции, но и ее тело: все функции в
сценарии или все функции, вложенные в функцию, будут объявлены до того, как
начнется выполнение программного кода. Это означает, что функцию можно вызвать
еще до того, как она будет объявлена.
Подобно инструкции var
, инструкции объявления функций создают переменные,
которые невозможно удалить. Однако эти переменные доступны не только для
чтения – им можно присвоить другие значения.
Условные инструкции позволяют пропустить или выполнить другие инструкции в зависимости от значения указанного выражения. Эти инструкции являются точками принятия решений в программе, и иногда их также называют инструкциями «ветвления». Если представить, что программа – это дорога, а интерпретатор JavaScript – путешественник, идущий по ней, то условные инструкции можно представить как перекрестки, где программный код разветвляется на две или более дорог, и на таких перекрестках интерпретатор должен выбирать, по какой дороге двигаться дальше.
В подразделах ниже описывается основная условная инструкция языка
JavaScript – инструкция if/else
, а также более сложная инструкция switch
,
позволяющая создавать множество ответвлений.
Инструкция if
– это базовая управляющая инструкция, позволяющая
интерпретатору JavaScript принимать решения или, точнее, выполнять инструкции в
зависимости от условий. Инструкция имеет две формы. Первая:
if (выражение)
инструкция
В этой форме сначала вычисляется выражение. Если полученный результат является истинным, то инструкция выполняется. Если выражение возвращает ложное значение, то инструкция не выполняется. (Определения истинных и ложных значений приводятся в разделе 3.3). Например:
Аналогично:
// Если переменная username равна null, undefined, 0, "" или NaN,
// присвоить ей новое значение.
if (!username) username = "John Doe";
Обратите внимание, что скобки вокруг условного выражения являются
обязательной частью синтаксиса инструкции if
.
Синтаксис языка JavaScript позволяет вставить только одну инструкцию после
инструкции if
и выражения в круглых скобках, однако одиночную инструкцию
всегда можно заменить блоком инструкций. Поэтому инструкция if
может
выглядеть так:
if (!address) {
address = "";
message = "Пожалуйста, укажите почтовый адрес";
}
Вторая форма инструкции if
вводит конструкцию else
, выполняемую в тех
случаях, когда выражение возвращает ложное значение. Ее синтаксис:
if (выражение)
инструкция 1
else
инструкция2
Эта форма инструкции выполняет инструкцию1
если выражение
возвращает истинное
значение, и инструкцию2
, если выражение
возвращает ложное значение. Например:
if (n == 1)
console.log("Получено 1 новое сообщение.");
else
console.log("Получено " + n + " новых сообщений.");
При наличии вложенных инструкций if
с блоками else
требуется некоторая
осторожность – необходимо гарантировать, что else
относится к соответствующей
ей инструкции if
. Взгляните на следующие строки:
i = j = 1;
k = 2;
if (i == j)
if (j == k)
console.log("i равно k");
else
console.log("i не равно j"); // НЕПРАВИЛЬНО!!
В этом примере внутренняя инструкция if
является единственной инструкцией,
вложенной во внешнюю инструкцию if
. К сожалению, неясно (если исключить
подсказку, которую дают отступы), к какой инструкции if
относится блок else
.
А отступы в этом примере выставлены неправильно, потому что в действительности интерпретатор
JavaScript интерпретирует предыдущий пример так:
if (i == j) {
if (j == k)
console.log("i равно k");
else
console.log("i не равно j"); // Вот как!
}
Согласно правилам JavaScript (и большинства других языков программирования), конструкция else является частью ближайшей к ней инструкции if. Чтобы сделать этот пример менее двусмысленным и более легким для чтения, понимания, сопровождения и отладки, надо поставить фигурные скобки:
if (i == j) {
if (j == k) {
console.log("i равно k");
}
}
else { // Вот какая разница возникает из-за добавления фигурных скобок!
console.log("i не равно j");
}
Хотя этот стиль и не используется в данной книге, тем не менее многие
программисты заключают тела инструкций if
и else
(а также других составных
инструкций, таких как циклы while
) в фигурные скобки, даже когда тело состоит только
из одной инструкции. Последовательное применение этого правила поможет
избежать неприятностей, подобных только что описанной.
Инструкция if/else
вычисляет значение выражения и выполняет один из двух
фрагментов программного кода в зависимости от результата. Но что если требуется
выполнить один из многих фрагментов? Возможный способ сделать это состоит
в применении инструкции else if
. Формально она не является самостоятельной
инструкцией JavaScript; это лишь распространенный стиль программирования,
заключающийся в применении повторяющихся инструкций if/else
:
if (n == 1) {
// Выполнить блок #1
}
else if (n == 2) {
// Выполнить блок #2
}
else if (n == 3) {
// Выполнить блок #3
}
else {
// Если ни одна из предыдущих инструкций else не была выполнена, выполнить блок #4
}
В этом фрагменте нет ничего особенного. Это просто последовательность
инструкций if
, где каждая инструкция if
является частью конструкции else
предыдущей инструкции. Стиль else if
предпочтительнее и понятнее записи в
следующей синтаксически эквивалентной форме, полностью показывающей вложенность
инструкций:
if (n == 1) {
// Выполнить блок 1
}
else {
if (n == 2) {
// Выполнить блок 2
}
else {
if (n == 3) {
// Выполнить блок 3
}
else {
// Если ни одна из предыдущих инструкций else
// не была выполнена, выполнить блок 4
}
}
}
Инструкция if
создает ветвление в потоке выполнения программы, а
многопозиционное ветвление можно реализовать посредством нескольких инструкций
else if
. Однако это не всегда наилучшее решение, особенно если все ветви зависят
от значения одного и того же выражения. В этом случае расточительно повторно
вычислять значение одного и того же выражения в нескольких инструкциях if
.
Инструкция switch
предназначена именно для таких ситуаций. За ключевым
словом switch
следует выражение в скобках и блок кода в фигурных скобках:
switch(выражение) { инструкции }Однако полный синтаксис инструкции
switch
более сложен, чем показано здесь.
Различные места в блоке помечены ключевым словом case
, за которым следует
выражение и символ двоеточия. Ключевое слово case
напоминает инструкцию
с меткой за исключением того, что оно связывает инструкцию с выражением, а не
с именем. Когда выполняется инструкция switch
, она вычисляет значение
выражения, а затем ищет метку case
, соответствующую этому значению (соответствие
определяется с помощью оператора идентичности ===). Если метка найдена,
выполняется блок кода начиная с первой инструкции, следующей за меткой case
. Если
метка case
с соответствующим значением не найдена, выполнение начинается
с первой инструкции, следующей за специальной меткой default:
. Если метка
default:
отсутствует, блок инструкции switch
пропускается целиком.
Работу инструкции switch
сложно объяснить на словах, гораздо понятнее
выглядит объяснение на примере. Следующая инструкция switch
эквивалентна
повторяющимся инструкциям if/else
, показанным в предыдущем разделе:
switch(n) case 1: // Выполняется, если n === 1 // Выполнить блок 1. break; // Здесь остановиться case 2: // Выполняется, если n === 2 // Выполнить блок 2. break; // Здесь остановиться case 3: // Выполняется, если n === 3 // Выполнить блок 3. break; // Здесь остановиться default: // Если все остальное не подходит... // Выполнить блок 4. break; // Здесь остановиться }Обратите внимание на ключевое слово
break
в конце каждого блока case
.
Инструкция break
, описываемая далее в этой главе, приводит к передаче управления в
конец инструкции switch
и продолжению выполнения инструкций, следующих
далее. Конструкции case
в инструкции switch
задают только начальную точку
выполняемого программного кода, но не задают никаких конечных точек. В случае
отсутствия инструкций break
инструкция switch
начнет выполнение блока кода
с меткой case
, соответствующей значению выражения, и продолжит выполнение
инструкций до тех пор, пока не дойдет до конца блока. В редких случаях это
полезно для написания программного кода, который переходит от одной метки case
к следующей, но в 99% случаев следует аккуратно завершать каждый блок case
инструкцией break
. (При использовании switch
внутри функции вместо break
можно использовать инструкцию return
. Обе эти инструкции служат для завершения
работы инструкции switch
и предотвращения перехода к следующей метке case
.)
Ниже приводится более практичный пример использования инструкции switch
;
он преобразует значение в строку способом, зависящим от типа значения:
function convert(x) { switch(typeof x) { case 'number': // Преобразовать число в шестнадцатеричное целое // (integer ???) return x.toString(16); case 'string': // Вернуть строку, заключенную в кавычки return '"' + x + '"'; default: // Любой другой тип преобразуется обычным способом return x.toString() } } console.log(convert(Math.PI + 'h')); console.log(convert("Math.PI"))Обратите внимание, что в двух предыдущих примерах за ключевыми словами
case
следовали числа или строковые литералы. Именно так инструкция switch
чаще всего используется на практике, но стандарт ECMAScript позволяет
указывать после case
произвольные выражения.
Инструкция switch
сначала вычисляет выражение после ключевого слова switch
,
а затем выражения case
в том порядке, в котором они указаны, пока не будет
найдено совпадающее значение.
(Тот факт, что выражения в метках case
вычисляются во время выполнения
программы, существенно отличает инструкцию switch
в языке JavaScript (и делает ее менее
эффективной) от инструкции switch
в С, C++ и Java. В этих языках выражения case
должны быть константами, вычисляемыми на этапе компиляции, и иметь один тот же тип.
Кроме того, инструкция switch
в этих языках часто может быть реализована с
использованием высокоэффективной таблицы переходов.)
Факт совпадения определяется с помощью оператора
идентичности ===, а не с помощью оператора равенства ==, поэтому выражения
должны совпадать без какого-либо преобразования типов.
Поскольку при каждом выполнении инструкции switch
вычисляются не все
выражения case
, следует избегать использования выражений case
, имеющих
побочные эффекты, такие как вызовы функций и присваивания. Безопаснее всего
ограничиваться в выражениях case
константными выражениями.
Как объяснялось ранее, если ни одно из выражений case
не соответствует
выражению switch
, инструкция switch
начинает выполнение с инструкции с меткой
default:
. Если метка default:
отсутствует, тело инструкции switch
полностью
пропускается. Обратите внимание, что в предыдущих примерах метка default:
указана в конце тела инструкции switch
после всех меток case
. Это логичное и
обычное место для нее, но на самом деле она может располагаться в любом месте
внутри инструкции switch
.
Чтобы понять действие условных инструкций, мы предлагали представить их
в виде разветвлений на дороге, по которой двигается интерпретатор JavaScript.
Инструкции циклов можно представить как разворот на дороге, возвращающий
обратно, который заставляет интерпретатор многократно проходить через один
и тот же участок программного кода. В языке JavaScript имеется четыре
инструкции циклов: while, do/while
, for
и
for/in
. Каждому из них посвящен один из
следующих подразделов. Одно из обычных применений инструкций циклов –
обход элементов массива. Эта разновидность циклов подробно обсуждается в
разделе 7.6, где также рассматриваются специальные методы итераций класса Array.
Инструкция if
является базовой условной инструкцией в языке JavaScript, а
базовой инструкцией циклов для JavaScript можно считать инструкцию while
. Она
имеет следующий синтаксис:
while (выражение)
инструкция
Инструкция while
начинает работу с вычисления выражения. Если это выражение
имеет ложное значение, интерпретатор пропускает инструкцию, составляющую
тело цикла, и переходит к следующей инструкции в программе. Если выражение
имеет истинное значение, то выполняется инструкция, образующая тело цикла,
затем управление передается в начало цикла и выражение вычисляется снова.
Иными словами, интерпретатор снова и снова выполняет инструкцию тела цикла, пока
(while
) значение выражения остается истинным. Обратите внимание, что имеется
возможность организовать бесконечный цикл с помощью синтаксиса while(true)
.
Обычно не требуется, чтобы интерпретатор JavaScript снова и снова выполнял
одну и ту же операцию. Почти в каждом цикле с каждой итерацией цикла одна или
несколько переменных изменяют свои значения. Поскольку переменная
меняется, действия, которые выполняет инструкция, при каждом проходе тела цикла
могут отличаться. Кроме того, если изменяемая переменная (или переменные)
присутствует в выражении, значение выражения может меняться при каждом проходе
цикла. Это важно, т. к. в противном случае выражение, значение которого было
истинным, никогда не изменится и цикл никогда не завершится! Ниже
приводится пример цикла while
, который выводит числа от 0 до 9:
var count = 0;
while (count < 10) {
console.log(count);
count++;
}
Как видите, в начале переменной count
присваивается значение 0, а затем ее
значение увеличивается каждый раз, когда выполняется тело цикла. После того как
цикл будет выполнен 10 раз, выражение вернет false
(т. е. переменная count
уже
не меньше 10), инструкция while
завершится и интерпретатор перейдет к
следующей инструкции в программе. Большинство циклов имеют
переменные-счетчики, аналогичные count
. Чаще всего в качестве счетчиков цикла выступают
переменные с именами i, j и к, хотя для того чтобы сделать программный код более
понятным, следует давать счетчикам более наглядные имена.
Цикл do/while
во многом похож на цикл while
, за исключением того, что
выражение цикла проверяется в конце, а не в начале. Это значит, что тело цикла всегда
выполняется как минимум один раз. Эта инструкция имеет следующий синтаксис:
do
инструкция
while (выражение):
Цикл do/while
используется реже, чем родственный ему цикл while
. Дело в том,
что на практике ситуация, когда вы заранее уверены, что потребуется хотя бы
один раз выполнить тело цикла, несколько необычна. Ниже приводится пример
использования цикла do/while
:
function printArray(a) {
var len = a.length, i = 0;
if (len == 0)
console.log("Пустой массив");
else {
do {
console.log(a[i]);
} while (++i < len);
console.log("len = " + len);
}
}
var a = ["p","r","o","g","r","a","m"];
console.log(printArray(a));
Между циклом do/while
и обычным циклом while
имеется два отличия.
Во-первых, цикл do
требует как ключевого слова do
(для отметки начала цикла), так
и ключевого слова while
(для отметки конца цикла и указания условия).
Во-вторых, в отличие от цикла while
, цикл do
завершается точкой с запятой. Цикл while
необязательно завершать точкой с запятой, если тело цикла заключено в
фигурные скобки.
Инструкция for
представляет собой конструкцию цикла, которая часто
оказывается более удобной, чем инструкция while
. Инструкция for
упрощает
конструирование циклов, следующих шаблону, общему для большинства циклов.
Большинство циклов имеют некоторую переменную-счетчик. Эта переменная
инициализируется перед началом цикла и проверяется перед каждой итерацией. Наконец,
переменная-счетчик инкрементируется или изменяется каким-либо другим
образом в конце тела цикла, непосредственно перед повторной проверкой
переменной. Инициализация, проверка и обновление – это три ключевых операции,
выполняемые с переменной цикла. Инструкция for
делает эти три шага явной
частью синтаксиса цикла:
for(инициализация; проверка; инкремент)
инструкция
Инициализация, проверка и инкремент – это три выражения (разделенные точкой с
запятой), которые ответственны за инициализацию, проверку и увеличение
переменной цикла. Расположение их в первой строке цикла упрощает понимание
того, что делает цикл for
, и не позволяет забыть инициализировать или увеличить
переменную цикла.
Проще всего объяснить работу цикла for
, показав эквивалентный ему цикл while
(как мы увидим, когда будем знакомиться с инструкцией continue в разделе 5.6.3, этот
цикл while
не является точным эквивалентом цикла for
):
инициализация;
while(проверка) {
инструкция
инкремент;
}
Другими словами, выражение инициализации вычисляется один раз перед началом цикла. Это выражение, как правило, является выражением с побочными эффектами (обычно присваиванием). В JavaScript также допускается, чтобы выражение инициализации было инструкцией объявления переменной var, поэтому можно одновременно объявить и инициализировать счетчик цикла. Выражение проверки вычисляется перед каждой итерацией и определяет, будет ли выполняться тело цикла. Если результатом проверки является истинное значение, выполняется инструкция, являющаяся телом цикла. В конце цикла вычисляется выражение инкремент. Чтобы использование этого выражения имело смысл, оно должно быть выражением с побочными эффектами. Обычно это либо выражение присваивания, либо выражение, использующее оператор ++ или --.
Вывести числа от 0 до 9 можно с помощью цикла for
, как показано ниже.
В противовес эквивалентному циклу while
, показанному в предыдущем разделе:
for(var count = 0; count < 10; count++)
console.log(count);
Конечно, циклы могут быть значительно более сложными, чем в этих простых
примерах, и иногда в каждой итерации цикла изменяется несколько
переменных. Эта ситуация – единственное место, где в JavaScript
обычно применяется оператор «запятая»; он обеспечивает способ объединить
множественные выражения инициализации и инкрементации в единое выражение,
удобное для использования в цикле for
:
var i, j
for(i = 0, j = 10; i < 10; i++, j --)
sum += i * j;
Во всех наших примерах циклов, представленных до сих пор, переменная цикла
содержала число. Это достаточно распространенная, но не обязательная
практика. В следующем примере цикл for
используется для обхода связанного списка
структур данных и получения последнего объекта в списке (например, первого
объекта, который не имеет свойства next
:
Обратите внимание на отсутствие в приведенном примере выражения инициализации.
Любое из трех выражений цикла for
может быть опущено, но две точки с запятой
являются обязательными. Если опустить выражение проверки, цикл будет
повторяться вечно, и форма записи for
(;;) является еще одним способом написать
бесконечный цикл, подобно while(true).
Инструкция цикла for/in
использует ключевое слово for
, но она в корне
отличается от инструкции обычного цикла for
. Цикл for/in
имеет следующий синтаксис:
for (переменная in объект)
инструкция
В качестве переменной здесь обычно используется имя переменной, но точно так
же можно использовать любое выражение, возвращающее lvalue (левостороннее
выражение, раздел 4.7.3), или
инструкцию var
, объявляющую единственную
переменную, – практически все, что может находиться слева от оператора присваивания.
Параметр объект – это выражение, возвращающее объект. И как обычно
инструкция – это инструкция или блок инструкций, образующих тело цикла.
Для обхода элементов массива естественно использовать обычный цикл for
:
for(var i = 0; i < a.length; i++) // Присваивать индексы в массиве переменной i
console.log(a[i]); // Вывести значение каждого элемента массива
Инструкция for/in
так же естественно позволяет выполнить обход свойств объекта.
for(var p in o) // Присваивать имена свойств объекта o переменной р
console.log(o[p]); // Вывести значение каждого свойства
Чтобы выполнить инструкцию for/in
, интерпретатор JavaScript сначала
вычисляет выражение объект. Если оно возвращает значение null
или undefined
,
интерпретатор пропускает цикл и переходит к следующей инструкции¹.
TypeError
.
for/in
может быть любым
выражением, возвращающим значение, которое можно использовать слева от оператора
присваивания. Это выражение вычисляется в каждой итерации цикла, т. е.
каждый раз оно может возвращать разные значения. Например, чтобы скопировать
имена всех свойств объекта в массив, можно использовать следующий цикл:
var o = {x:1, y:2, z:3};
var a = []; var i = 0;
for(a[i++] in o) /* пустое тело цикла */;
Массивы в JavaScript – это просто специальный тип объектов, а индексы в
массиве – свойства объекта, обход которых можно выполнить с помощью цикла for/in
.
Например, следующая инструкция перечислит индексы 0, 1 и 2 массива,
объявленного выше:
for(i in a) console.log(i);
В действительности цикл for/in
может совершать обход не по всем свойствам
объекта, а только по перечислимым свойствам (раздел 6.7). Многочисленные
встроенные методы, определяемые в базовом языке JavaScript, не являются
перечислимыми. Например, все объекты имеют метод toString()
, но цикл for/in
не
перечислит свойство toString. Кроме встроенных методов также не являются
перечислимыми многие другие свойства встроенных объектов. При этом все свойства
и методы, определяемые пользователем, являются перечислимыми. (Но в
реализации, следующей стандарту ECMAScript 5, имеется возможность сделать их
неперечислимыми, использовав прием, описанный в разделе 6.7) Унаследованные
свойства, которые были определены пользователем (раздел 6.2.2), также
перечисляются циклом for/in
.
Если в теле цикла for/in
удалить свойство, которое еще не было перечислено, это
свойство перечислено не будет. Если в теле цикла создать новые свойства, то
обычно такие свойстве не будут перечислены. (Однако некоторые реализации могут
перечислять унаследованные свойства, добавленные в ходе выполнения цикла.)
Спецификация ECMAScript не определяет порядок, в каком цикл for/in
должен
перечислять свойства объекта. Однако на практике реализации JavaScript во всех
основных браузерах перечисляют свойства простых объектов в порядке, в каком
они были определены, – когда ранее объявленные свойства перечисляются
первыми. Если объект был создан с помощью литерала объекта, свойства
перечисляются в том же порядке, в каком они следуют в литерале. В Интернете существуют
сайты и библиотеки, которые опираются на такой порядок перечисления,
поэтому маловероятно, что производители браузеров изменят его.
В абзаце выше описывается порядок перечисления свойств «простых» объектов. Однако в разных реализациях порядок перечисления может отличаться, если:
• объект наследует перечислимые свойства;
• объект имеет свойства, которые являются целочисленными индексами массива;
• использовалась инструкция delete для удаления существующих свойств объекта или
• использовался метод Object.def ineProperty() (раздел 6.7) или аналогичный ему для изменения атрибутов свойства объекта.
Обычно (но не во всех реализациях) унаследованные свойства (раздел 6.2.2) перечисляются после всех неунаследованных, «собственных» свойств объекта, но они также перечисляются в порядке их определения. Если объект наследует свойства более чем от одного «прототипа» (раздел 6.1.3) - например, когда в его «цепочке прототипов» имеется более одного объекта, - свойства каждого объекта-прототипа в цепочке перечисляются в порядке их создания перед перечислением свойств следующего объекта. Некоторые (но не все) реализации перечисляют свойства массива в порядке возрастания чисел, а не в порядке их создания, но при наличии в массиве свойств с нечисловыми именами происходит возврат к перечислению в порядке создания свойств, то же самое происходит и в случае разреженных массивов (т. е. когда в массиве отсутствуют некоторые элементы).
Еще одной категорией инструкций языка JavaScript являются инструкции
перехода. Как следует из названия, эти инструкции заставляют интерпретатор
JavaScript переходить в другое место в программном коде. Инструкция break
заставляет интерпретатор перейти в конец цикла или другой инструкции. Инструкция
continue
заставляет интерпретатор пропустить оставшуюся часть тела цикла,
перейти обратно в начало цикла и приступить к выполнению новой итерации. В
языке JavaScript имеется возможность помечать инструкции именами, благодаря
чему в инструкциях break
и continue
можно явно указывать, к какому циклу или
к какой другой инструкции они относятся.
Инструкция return
заставляет интерпретатор перейти из вызванной функции
обратно в точку ее вызова и вернуть значение вызова. Инструкция throw
возбуждает
исключение и предназначена для работы в сочетании с инструкцией try/catch/finally
,
которая определяет блок программного кода для обработки исключения.
Это достаточно сложная разновидность инструкции перехода: при появлении
исключения интерпретатор переходит к ближайшему объемлющему обработчику
исключений, который может находиться в той же функции или выше, в стеке
возвратов вызванной функции.
Подробнее все эти инструкции перехода описываются в следующих подразделах.
Любая инструкция может быть помечена указанным перед ней идентификатором и двоеточием:
идентификатор: инструкция
Помечая инструкцию, вы тем самым даете ей имя, которое затем можно будет
использовать в качестве ссылки в любом месте в программе. Пометить можно
любую инструкцию, однако помечать имеет смысл только инструкции, имеющие
тело, такие как циклы и условные инструкции. Присвоив имя циклу, его затем
можно использовать в инструкциях break
и continue
,
внутри цикла для выхода из
него или для перехода в начало цикла, к следующей итерации. В языке JavaScript
метки инструкций используют только инструкции break
и continue
;
о них подробнее рассказывается далее в этой главе. Ниже приводится
пример инструкции while
с меткой и инструкции continue
,
использующей эту метку:
mainloop: while(token != null) {
// Программный код опущен...
continue mainloop; // Переход к следующей итерации именованного цикла
// Программный код опущен...
}
Идентификатор, используемый в качестве метки инструкции, может быть любым допустимым идентификатором JavaScript, кроме зарезервированного слова. Пространства имен меток и переменных или функций различны, поэтому в качестве меток допускается использовать идентификаторы, совпадающие с именами переменных или функций. Метки инструкций определены только внутри инструкций, к которым они применяются (и, конечно же, внутри вложенных в них инструкций). Вложенные инструкции не могут помечаться теми же идентификаторами, что и вмещающие их инструкции, но две независимые инструкции могут помечаться одинаковыми метками. Помеченные инструкции могут помечаться повторно. То есть любая инструкция может иметь множество меток.
Инструкция break приводит к немедленному выходу из самого внутреннего
цикла или инструкции switch
. Синтаксис ее прост:
break;
Поскольку инструкция break
приводит к выходу из цикла или инструкции switch
,
такая форма break
допустима только внутри этих инструкций.
Выше мы уже видели примеры использования инструкции break
внутри
инструкции switch
. В циклах она обычно используется для немедленного выхода из
цикла, когда по каким-либо причинам требуется завершить выполнение цикла.
Когда цикл имеет очень сложное условие завершения, зачастую проще бывает
реализовать эти условия с помощью инструкций break
, чем пытаться выразить их
в одном условном выражении цикла. Следующий пример пытается отыскать
элемент массива с определенным значением. Цикл завершается обычным образом
по достижении конца массива или с помощью инструкции break
, как только будет
найдено искомое значение:
for(var i = 0; i < a.length; i++) {
if (a[i] == target) break;
}
В языке JavaScript допускается указывать имя метки за ключевым словом break
(идентификатор без двоеточия):
break имя_метки;
Когда инструкция break
используется с меткой, она выполняет переход в конец
именованной инструкции, или прекращение ее выполнения. В случае отсутствия
инструкции с указанной меткой попытка использовать такую форму инструкций
break
порождает синтаксическую ошибку. Именованная инструкция не обязана
быть циклом или инструкцией switch
: инструкция break
с меткой может
выполнять «выход» из любой вмещающей ее инструкции. Объемлющая инструкция
может даже быть простым блоком инструкций, заключенным в фигурные скобки
исключительно с целью пометить его.
Между ключевым словом break
и именем метки не допускается вставлять символ
перевода строки. Дело в том, что интерпретатор JavaScript автоматически
вставляет пропущенные точки с запятой: если разбить строку программного кода
между ключевым словом break
и следующей за ним меткой, интерпретатор
предположит, что имелась в виду простая форма этой инструкции без метки, и добавит
точку с запятой (раздел 2.5).
Инструкция break
с меткой необходима, только когда требуется прервать
выполнение инструкции, не являющейся ближайшим объемлющим циклом или
инструкцией . Следующий фрагмент демонстрирует это:
var matrix = getData(); // Получить 2-мерный массив чисел откуда-нибудь
// Найти сумму всех чисел в матрице.
var sum = 0, success = false;
// Пометить инструкцию, выполнение которой требуется прервать в случае ошибки
compute_sum: if (matrix) {
for(var x = 0; x < matrix.length; x++) {
var row = matrix[x];
if (!row) break compute_sum;
for(var y = 0; y < row.length; y++) {
var cell = row[y];
if (isNaN(cell)) break compute_sum;
um += cell;
}
}
success = true;
}
// Здесь инструкция break выполняет переход. Если будет выполнено условие
// success == false, значит, что-то не так в полученной матрице.
// В противном случае переменная sum будет содержать сумму всех элементов матрицы.
Наконец, обратите внимание, что инструкция break
, с меткой или без нее, не может
передавать управление через границы функций. Например, нельзя пометить
инструкцию объявления функции и затем использовать эту метку внутри функции.
Инструкция continue
схожа с инструкцией break
. Однако вместо выхода из цикла
инструкция continue
запускает новую итерацию цикла. Синтаксис инструкции
continue
столь же прост, как и синтаксис инструкции break
:
continue;
Инструкция continue
может также использоваться с меткой:
continue имя_метки;
Инструкция continue
как в форме без метки, так и с меткой может
использоваться только в теле цикла. Использование ее в любых других местах приводит к
синтаксической ошибке.
Когда выполняется инструкция continue
, текущая итерация цикла прерывается
и начинается следующая. Для разных типов циклов это означает разное:
• В цикле while
указанное в начале цикла выражение проверяется снова, и если
оно равно true
, тело цикла выполняется с начала.
• В цикле do/while
происходит переход в конец цикла, где перед повторным
выполнением цикла снова проверяется условие.
• В цикле for
вычисляется выражение инкремента и снова вычисляется
выражение проверки, чтобы определить, следует ли выполнять следующую итерацию.
• В цикле for/in
цикл начинается заново с присвоением указанной переменной
имени следующего свойства.
Обратите внимание на различия в поведении инструкции continue
в циклах while
и for
: цикл while
возвращается непосредственно к своему условию, а цикл for
сначала вычисляет выражение инкремента, а затем возвращается к условию. Ранее
при обсуждении цикла for
объяснялось поведение цикла for
в терминах
«эквивалентного» цикла while
. Поскольку инструкция continue
ведет себя в этих двух
циклах по-разному, точно имитировать цикл for
с помощью одного цикла while
невозможно.
В следующем примере показано использование инструкции continue
без метки
для выхода из текущей итерации цикла в случае ошибки:
Инструкция continue
, как и break
, может применяться во вложенных циклах
в форме, включающей метку, и тогда заново запускаемым циклом необязательно
будет цикл, непосредственно содержащий инструкцию continue
. Кроме того, как
и для инструкции break
, переводы строк между ключевым словом continue
и
именем метки не допускаются.
Как вы помните, вызов функции является выражением и подобно всем
выражениям имеет значение. Инструкция return
внутри функций служит для
определения значения, возвращаемого функцией. Инструкция return имеет следующий
синтаксис:
return выражение;
Инструкция return
может располагаться только в теле функции. Присутствие ее
в любом другом месте является синтаксической ошибкой. Когда выполняется
инструкция return
, функция возвращает значение выражения вызывающей
программе. Например:
Если функция не имеет инструкции return
, при ее вызове интерпретатор будет
выполнять инструкции в теле функции одну за другой, пока не достигнет конца
функции, и затем вернет управление вызвавшей ее программе. В этом случае выражение
вызова вернет значение undefined
. Инструкция return
часто является
последней инструкцией в функции, но это совершенно необязательно: функция
вернет управление вызывающей программе, как только будет достигнута
инструкция return
, даже если за ней следуют другие инструкции в теле функции.
Инструкция return
может также использоваться без выражения, тогда она просто
прерывает выполнение функции и возвращает значение undefined
вызывающей
программе. Например:
Из-за того что интерпретатор JavaScript автоматически вставляет точки с
запятой, нельзя разделять переводом строки инструкцию return
и следующее за ней
выражение.
Исключение – это сигнал, указывающий на возникновение какой-либо
исключительной ситуации или ошибки. Возбуждение исключения (throw
) – это способ
просигнализировать о такой ошибке или исключительной ситуации.
Перехватить исключение (catch
) – значит обработать его, т. е. предпринять действия,
необходимые или подходящие для восстановления после исключения. В JavaScript
исключения возбуждаются в тех случаях, когда возникает ошибка времени
выполнения и когда программа явно возбуждает его с помощью инструкции throw
.
Исключения перехватываются с помощью инструкции try/catch/finally
, которая
описана в следующем разделе.
Инструкция throw
имеет следующий синтаксис:
throw
выражение;
Результатом выражения может быть значение любого типа. Инструкции throw
можно
передать число, представляющее код ошибки, или строку, содержащую текст
сообщения об ошибке. Интерпретатор JavaScript возбуждает исключения, используя
экземпляр класса Error
одного из его подклассов, и вы также можете использовать
подобный подход. Объект Error
имеет свойство name
, определяющее тип ошибки,
и свойство message
, содержащее строку, переданную функции-конструктору
(смотрите описание класса Error
в справочном разделе). Ниже приводится пример
функции, которая возбуждает объект Error
при вызове с недопустимым аргументом:
Когда возбуждается исключение, интерпретатор JavaScript немедленно прерывает нормальное выполнение программы и переходит к ближайшему обработчику исключений.
(К самому внутреннему по вложенности охватывающему обработчику исключений. – Прим. науч. ред.)
В обработчиках исключений используется конструкция catch
инструкции try/catch/finally
, описание которой приведено в следующем разделе.
Если блок программного кода, в котором возникло исключение, не имеет
соответствующей конструкции catch
, интерпретатор анализирует следующий внешний
блок программного кода и проверяет, связан ли с ним обработчик исключений.
Это продолжается до тех пор, пока обработчик не будет найден. Если исключение
генерируется в функции, не содержащей инструкции try/catch/finally
,
предназначенной для его обработки, то исключение распространяется выше, в
программный код, вызвавший функцию. Таким образом исключения
распространяются по лексической структуре методов JavaScript вверх по стеку вызовов. Если
обработчик исключения так и не будет найден, исключение рассматривается как
ошибка и о ней сообщается пользователю.
Инструкция try/catch/finally
реализует механизм обработки исключений в
JavaScript. Конструкция try
в этой инструкции просто определяет блок кода, в
котором обрабатываются исключения. За блоком try
следует конструкция catch
с
блоком инструкций, вызываемых, если где-либо в блоке try
возникает исключение.
За конструкцией catch
следует блок finally
, содержащий программный код,
выполняющий заключительные операции, который гарантированно выполняется
независимо от того, что происходит в блоке try
. И блок catch
, и блок finally
не
являются обязательными, однако после блока try
должен обязательно
присутствовать хотя бы один из них. Блоки try
, catch
и finally
начинаются и заканчиваются
фигурными скобками. Это обязательная часть синтаксиса, и она не может быть
опущена, даже если между ними содержится только одна инструкция.
Следующий фрагмент иллюстрирует синтаксис и назначение инструкции try/catch/finally
:
try { // Обычно этот код без сбоев работает от начала до конца. // Но в какой-то момент в нем может быть сгенерировано исключение // либо непосредственно с помощью инструкции throw, либо косвенно - // вызовом метода, генерирующего исключение. } catch (e) { // Инструкции в этом блоке выполняются тогда и только тогда, когда // в блоке try возникает исключение. Эти инструкции могут использовать // локальную переменную e, ссылающуюся на объект Error или на другое // значение, указанное в инструкции throw. Этот блок может либо некоторым // образом обработать исключение, либо проигнорировать его, делая что-то // другое, либо заново сгенерировать исключение с помощью инструкции throw. } finally { // Этот блок содержит инструкции, которые выполняются всегда, независимо // от того, что произошло в блоке try. Они выполняются, если блок try // завершился: // 1) как обычно, достигнув конца блока // 2) из-за инструкции break, continue или return // 3) с исключением, обработанным приведенным в блоке catch выше // 4) с неперехваченным исключением, которое продолжает свое // распространение на более высокие уровни }
Обратите внимание, что за ключевым словом catch
следует идентификатор в
скобках. Этот идентификатор похож на параметр функции. Когда будет перехвачено
исключение, этому параметру будет присвоено исключение (например, объект
Error
). В отличие от обычной переменной идентификатор, ассоциированный с
конструкцией catch
, существует только в теле блока catch
.
Далее приводится более реалистичный пример инструкции try/catch
. В нем
вызываются метод factorial(), определенный в предыдущем разделе, и методы
prompt()
и alert()
клиентского JavaScript для организации ввода и вывода:
try { // Запросить число у пользователя var n = Number(prompt("Введите положительное число", "")); // Вычислить факториал числа, предполагая, что входные данные корректны var f = factorial(n); //var f = factorial(n); // Вывести результат alert(n + "! = " + f); //alert(n + "! = " + f); } catch (ex) { // Если данные некорректны, управление будет передано сюда alert(ex); // Сообщить пользователю об ошибке }
Запросить число у пользователя можно в окне, вызываемом двойным щелчком на этом абзаце.
Это пример инструкции try/catch
без конструкции finally
. Хотя finally
используется не так часто, как catch
, тем не менее иногда эта конструкция оказывается
полезной. Однако ее поведение требует дополнительных объяснений. Блок finally
гарантированно исполняется, если исполнялась хотя бы какая-то часть блока try
,
независимо от того, каким образом завершилось выполнение программного кода
в блоке try
. Эта возможность обычно используется для выполнения
заключительных операций после выполнения программного кода в предложении try
.
В обычной ситуации управление доходит до конца блока try
, а затем переходит
к блоку finally
, который выполняет необходимые заключительные операции.
Если управление вышло из блока try
как результат выполнения инструкций return
,
continue
или break
, перед передачей управления в другое место выполняется блок
finally
.
Если в блоке try
возникает исключение и имеется соответствующий блок catch
для его обработки, управление сначала передается в блок catch
, а затем – в блок
finally
. Если отсутствует локальный блок catch
, то управление сначала
передается в блок finally
, а затем переходит на ближайший внешний блок catch
, который
может обработать исключение.
Если сам блок finally
передает управление с помощью инструкции return
,
continue, break
или throw
или путем вызова метода, генерирующего исключение,
незаконченная команда на передачу управления отменяется и выполняется новая.
Например, если блок finally
сгенерирует исключение, это исключение заменит
любое ранее сгенерированное исключение. Если в блоке finally
имеется инструкция
return
, произойдет нормальный выход из метода, даже если генерировалось
исключение, которое не было обработано.
Конструкции try
и finally
могут использоваться вместе без конструкции catch
.
В этом случае блок finally
– это просто набор инструкций, выполняющих
заключительные операции, который будет гарантированно выполнен независимо от
наличия в блоке try
инструкции break, continue
или return
. Напомню, из-за
различий в работе инструкции continue
в разных циклах невозможно написать цикл
while
, полностью имитирующий работу цикла for
. Однако если добавить
инструкцию try/finally
, можно написать цикл while
, который будет действовать точно так
же, как цикл for
, и корректно обрабатывать инструкцию continue
:
// Имитация цикла for( инициализация ; проверка ; инкремент ) тело цикла; инициализация ; while( проверка ) { try { тело цикла ; } finally { инкремент ; } }
Обратите однако внимание, что тело цикла while
, содержащее инструкцию break
,
будет вести себя несколько иначе (из-за выполнения лишней операции
инкремента перед выходом), чем тело цикла for
, поэтому даже используя конструкцию
finally
, невозможно точно сымитировать цикл for
с помощью цикла while
.
В этом разделе описываются три остальные инструкции языка JavaScript – with
,
debugger
и use strict
.
В разделе 3.10.3 мы обсуждали область видимости переменных и цепочки
областей видимости – список объектов, в которых выполняется поиск при разрешении
имен переменных. Инструкция with
используется для временного изменения
цепочки областей видимости. Она имеет следующий синтаксис:
with (объект)
инструкция
Эта инструкция добавляет объект в начало цепочки областей видимости,
выполняет инструкцию, а затем восстанавливает первоначальное состояние цепочки.
Инструкция with
не может использоваться в строгом режиме (раздел 5.7.3) и не
рекомендуется к использованию в нестрогом режиме: избегайте ее
использования по мере возможности. Программный код JavaScript, в котором используется
инструкция with
, сложнее поддается оптимизации и наверняка будет работать
медленнее, чем эквивалентный программный код без инструкции with
.
На практике инструкция with
упрощает работу с глубоко вложенными
иерархиями объектов. В клиентском JavaScript вам наверняка придется вводить
выражения, как показано ниже, чтобы обратиться к элементам HTML-формы:
document.forms[0].address.value
Если подобные выражения потребуется записать много раз, можно
воспользоваться инструкцией with
, чтобы добавить объект формы в цепочку областей
видимости:
Этот прием сокращает объем текста программы – больше не надо указывать
фрагмент document.forms[0]
перед каждым именем свойства. Этот объект
представляет собой временную часть цепочки областей видимости и автоматически
участвует в поиске, когда JavaScript требуется разрешить идентификаторы, такие как
address
. Избежать применения инструкции with
достаточно просто, если записать
предыдущий пример, как показано ниже:
Имейте в виду, что цепочка областей видимости используется только для поиска идентификаторов и не используется при их создании. Взгляните на следующий пример:
with(o) x = 1;
Если объект o
имеет свойство x
, то данный программный код присвоит значение 1
этому свойству. Но если x
не является свойством объекта o
, данный программный
код выполнит то же действие, что и инструкция x = 1
без инструкции with
. Он
присвоит значение локальной или глобальной переменной с именем x
или создаст
новое свойство глобального объекта. Инструкция with
обеспечивает более
короткую форму записи операций чтения свойств объекта o
, но не создания новых
свойств этого объекта.
Инструкция debugger
обычно ничего не делает. Однако если имеется и запущена
программа-отладчик, реализация JavaScript может (но не обязана) выполнять
некоторые отладочные операции. Обычно эта инструкция действует подобно
точке останова: интерпретатор JavaScript приостанавливает выполнение
программного кода, и вы можете с помощью отладчика вывести значения переменных,
ознакомиться с содержимым стека вызовов и т. д. Допустим, к примеру, что ваша
функция f()
порождает исключение, потому что она вызывается с
неопределенным аргументом, а вы никак не можете определить, из какой точки программы
производится этот вызов. Чтобы решить эту проблему, можно было бы изменить
определение функции f()
, чтобы она начиналась строкой, как показано ниже:
Теперь, когда f()
будет вызвана без аргумента, ее выполнение будет
приостановлено, и вы сможете воспользоваться отладчиком и просмотреть стек вызовов,
чтобы отыскать место, откуда был выполнен некорректный вызов.
Официально инструкция debugger
была добавлена в язык стандартом
ЕСМАScript 5, но производители основных браузеров реализовали ее уже достаточно
давно. Обратите внимание, что недостаточно иметь отладчик: инструкция debugger
не запускает отладчик автоматически. Однако, если отладчик уже запущен, эта
инструкция будет действовать как точка останова. Если, к примеру,
воспользоваться расширением Firebug для Firefox, это расширение должно быть
активировано для веб-страницы, которую требуется отладить, и только в этом случае
инструкция debugger
будет работать.
"use strict"
– это директива, введенная стандартом ECMAScript 5. Директивы не
являются инструкциями (но достаточно близки, чтобы включить описание
"use strict"
в эту главу). Между обычными инструкциями и директивой "use strict"
существует два важных отличия:
• Она не включает никаких зарезервированных слов языка: директива – это лишь выражение, содержащее специальный строковый литерал (в одиночных или двойных кавычках). Интерпретаторы JavaScript, не соответствующие стандарту ECMAScript 5, будут интерпретировать ее как простое выражение без побочных эффектов и ничего не будут делать. В будущих версиях стандарта ECMAScript, как ожидается, слово use будет переведено в разряд ключевых слов, что позволит опустить кавычки.
• Она может появляться только в начале сценария или в начале тела функции,
перед любыми другими инструкциями. Однако она не обязательно должна
находиться в самой первой строке сценария или функции: директиве "use strict"
могут предшествовать или следовать за ней другие строковые
выражения-литералы, а различные реализации JavaScript могут интерпретировать эти
строковые литералы как директивы, определяемые этими реализациями.
Строковые литералы, следующие за первой обычной инструкцией в сценарии или
функции, интерпретируются как обычные выражения – они могут не
восприниматься как директивы и не оказывать никакого эффекта.
Назначение директивы "use strict"
состоит в том, чтобы показать, что
следующий за ней программный код (в сценарии или функции) является строгим
кодом. Строгим считается программный код верхнего уровня (не внутри функций),
если в сценарии имеется директива "use strict"
. Строгим считается тело
функции, если она определяется внутри строгого программного кода или если она
содержит директиву "use strict"
. Строгим считается программный код,
передаваемый методу eval()
, если вызов eval()
выполняется из строгого программного кода
или если строка с кодом содержит директиву "use strict"
.
Строгий программный код выполняется в строгом режиме. Согласно стандарту ECMAScript 5, строгий режим определяет ограниченное подмножество языка, благодаря чему исправляет некоторые недостатки языка, а также обеспечивает более строгую проверку на наличие ошибок и повышенный уровень безопасности. Ниже перечислены различия между строгим и нестрогим режимами (первые три имеют особенно большое значение):
• В строгом режиме не допускается использование инструкции with
.
• В строгом режиме все переменные должны объявляться: если попытаться
присвоить значение идентификатору, который не является объявленной переменной,
функцией, параметром функции, параметром конструкции catch
или
свойством глобального объекта, возбуждается исключение ReferenceError
. (В
нестрогом режиме такая попытка просто создаст новую глобальную переменную
и добавит ее в виде свойства в глобальный объект.)
• В строгом режиме функции, которые вызываются как функции (а не как
методы), получают в ссылке this
значение undefined
. (В нестрогом режиме
функции, которые вызываются как функции, всегда получают в ссылке this
глобальный объект.) Это отличие можно использовать, чтобы определить,
поддерживает ли та или иная реализация строгий режим:
var hasStrictMode = (function() { "use strict"; return this===undefined}());
Кроме того, когда функция вызывается в строгом режиме с помощью call()
или apply()
, значение ссылки this
в точности соответствует значению,
переданному в первом аргументе функции call()
или apply()
. (В нестрогом режиме
значения null
и undefined
замещаются ссылкой на глобальный объект, а простые
значения преобразуются в объекты.)
• В строгом режиме попытки присвоить значения свойствам, недоступным для
записи, или создать новые свойства в нерасширяемых объектах порождают
исключение TypeError
. (В нестрогом режиме эти попытки просто игнорируются.)
• В строгом режиме программный код, передаваемый функции eval()
, не может
объявлять переменные или функции в области видимости вызывающего
программного кода, как это возможно в нестрогом режиме. Вместо этого
переменные и функции помещаются в новую область видимости, создаваемую для
функции eval()
. Эта область видимости исчезает, как только eval()
вернет
управление.
• В строгом режиме объект arguments
(раздел 8.3.2) в функции хранит
статическую копию значений, переданных функции. В нестрогом режиме объект
arguments
ведет себя иначе – элементы массива arguments
и именованные
параметры функции ссылаются на одни и те же значения.
• В строгом режиме возбуждается исключение SyntaxError
, если оператору delete
передать неквалифицированный идентификатор, такой как имя переменной,
функции или параметра функции. (В нестрогом режиме такое выражение
delete
не выполнит никаких действий и вернет false
.)
• В строгом режиме попытка удалить ненастраиваемое свойство приведет к
исключению TypeError
. (В нестрогом режиме эта попытка просто завершится
неудачей и выражение delete
вернет false
.)
• В строгом режиме попытка определить в литерале объекта два или более свойств с одинаковыми именами считается синтаксической ошибкой. (В нестрогом режиме ошибка не возникает.)
• В строгом режиме определение двух или более параметров с одинаковыми именами в объявлении функции считается синтаксической ошибкой. (В нестрогом режиме ошибка не возникает.)
• В строгом режиме не допускается использовать литералы восьмеричных
целых чисел (начинающихся с 0, за которым не следует символ x
). (В нестрогом
режиме некоторые реализации позволяют использовать восьмеричные
литералы.)
• В строгом режиме идентификаторы eval()
и arguments
интерпретируются как
ключевые слова, и вы не можете изменить их значения. Вы сможете
присвоить значения этим идентификаторам, объявив их как переменные,
использовав их в качестве имен функций, имен параметров функций или
идентификаторов блока catch
.
• В строгом режиме ограничивается возможность просмотра стека вызовов.
Попытки обратиться к свойствам arguments.caller
и arguments.callee
в строгом
режиме возбуждают исключение TypeError
. Попытки прочитать свойства caller
и arguments
функций в строгом режиме также возбуждают исключение TypeError
.
(Некоторые реализации определяют эти свойства в нестрогих функциях.)
В этой главе были представлены все инструкции языка JavaScript. В табл. 5.1 содержится перечень этих инструкций с указанием синтаксиса и назначения каждой из них.
Таблица 5.1. Синтаксис инструкций JavaScript
Инструкция | Синтаксис | Назначение |
---|---|---|
break | break [имя_метки]; | Выход из самого внутреннего цикла, инструкции switch или инструкции с именем имя_метки |
case | case выражение: | Метка для инструкции внутри конструкции switch |
continue | continue [имя_метки]; | Переход к следующей итерации самого внутреннего цикла или цикла, помеченного меткой имя_метки |
debugger | debugger; | Точка останова отладчика |
default | default: | Метка инструкции по умолчанию, внутри инструкции switch |
do/while | do инструкция while (выражение); | Альтернатива циклу while |
пустая инструкция | ; | Ничего не делает |
for | for (инициализация; проверка; инкремент) инструкция | Простой в использовании цикл |
for/in | for (переменная in объект) инструкция | Цикл по свойствам объекта |
function | function имя_функции([парам[, ...]]) { тело } | Объявление функции с именем имя_функции |
if/else | if {выражение) инструкция1 [else инструкция2] | Выполняет инструкцию1 или инструкцию2 |
метка | идентификатор: инструкция | Дает инструкции имя идентификатор |
return | return [выражение]; | Возвращает значение из функции |
switch | switch (выражение) { инструкции } | Многопозиционное ветвление для инструкций с метками case и default |
throw | throw выражение: | Генерирует исключения |
try | try{ инструкции } [catch{ обработчик исключений }] [finally { заключит, операции }] | Обработка исключений |
use strict | "use strict" | Применение строгого режима, накладывающего ограничения на сценарии или функции |
var | var имя [ = выражение ] [ ,... ]; | Объявление и инициализация одной или более переменных |
while | while (выражение) инструкция | Базовая конструкция цикла |
with | with (объект) инструкция | Расширение цепочки областей видимости. (Не рекомендуется к использованию.) |