I. Базовый JavaScript

2. Лексическая структура

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

содержание 2.1. Набор символов содержание

При написании программ на JavaScript используется набор символов Юникода. Юникод является надмножеством кодировок ASCII и Latin-1 и поддерживает практически все письменные языки, имеющиеся на планете. Стандарт ЕСМА- Script 3 требует, чтобы реализации JavaScript обеспечивали поддержку стандарта Юникода версии 2.1 или выше, а стандарт ECMAScript 5 требует, чтобы реализации обеспечивали поддержку стандарта Юникода версии 3 или выше. Более подробно о Юникоде и JavaScript говорится во врезке в разделе 3.2.

2.1.1. Чувствительность к регистру

JavaScript – это язык, чувствительный к регистру символов. Это значит, что ключевые слова, имена переменных и функций и любые другие идентификаторы языка должны всегда содержать одинаковые наборы прописных и строчных букв. Например, ключевое слово while должно набираться как «while», а не «While» или «WHILE». Аналогично online, Online, OnLine и ONLINE – это имена четырех разных переменных.

Заметим, однако, что язык разметки HTML (в отличие от XHTML) не чувствителен к регистру. Так как HTML и клиентский JavaScript тесно связаны, это различие может привести к путанице. Многие JavaScript-объекты и их свойства имеют те же имена, что и теги и атрибуты языка HTML, которые они обозначают. Однако если в HTML эти теги и атрибуты могут набираться в любом регистре, то в JavaScript они обычно должны набираться строчными буквами. Например, атрибут onclick обработчика события чаще всего задается в HTML как onClick, однако в JavaScript-коде (или в XHTML-документе) он должен быть обозначен как onclick.

2.1.2. Пробелы, переводы строк и символы управления форматом

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

Помимо обычного символа пробела (\u0020) JavaScript дополнительно распознает как пробельные следующие символы: табуляция (\u0009), вертикальная табуляция (\u000В), перевод формата (\u000C), неразрывный пробел (\u00А0), маркер порядка следования байтов (\uFEFF), а также все символы Юникода, относящиеся к категории Zs.

Следующие символы распознаются интерпретаторами JavaScript как символы конца строки: перевод строки (\u000А), возврат каретки (\u000D), разделитель строк (\u2028) и разделитель абзацев (\u2029). Последовательность из символов возврата каретки и перевода строки интерпретируется как единственный символ завершения строки.

Символы Юникода, управляющие форматом (категория Cf), такие как RIGHT-TO-LEFT MARK (\u200F) и LEFT-TO-RIGHT MARK (\u200E), управляют визуальным представлением текста, в котором они присутствуют. Они имеют большое значение для корректного отображения текста на некоторых языках и являются допустимыми в комментариях JavaScript, строковых литералах и в литералах регулярных выражений, но не в идентификаторах (таких как имена переменных), определяемых в программах JavaScript. Исключение составляют ZERO WIDTH JOINER (\u200D) и ZERO WIDTH NON-JOINER (\u200C), которые можно использовать в идентификаторах при условии, что они не являются первыми символами идентификаторов. Как отмечалось выше, символ управления порядком следования байтов (\uFEFF) интерпретируется как пробельный символ.

2.1.3. Экранированные последовательности Юникода

Некоторые компьютеры и программное обеспечение не могут отображать или обеспечивать ввод полного набора символов Юникода. Для поддержки программистов, использующих подобную устаревшую технику, JavaScript определяет специальные последовательности, состоящие из шести символов ASCII, представляющие 16-битные кодовые пункты Юникода. Эти экранированные последовательности Юникода начинаются с символов и, за которыми следуют точно четыре шестнадцатеричные цифры (при этом символы A-F могут быть и строчными, и прописными). Экранированные последовательности Юникода могут появляться в строковых литералах JavaScript, в литералах регулярных выражений и в идентификаторах (но не в ключевых словах языка). Экранированная последовательность Юникода для символа é, например, имеет вид \u00E9, и с точки зрения JavaScript следующие две строки являются идентичными:

"café" === "caf\u00e9" // => true

Экранированные последовательности Юникода могут также появляться в комментариях, но поскольку комментарии игнорируются, в данном контексте они воспринимаются как последовательность символов ASCII и не интерпретируются как символы Юникода.

2.1.4. Нормализация

Юникод позволяет закодировать один и тот же символ несколькими способами. Строка «e», например, может быть закодирована как единственный символ Юникода u00E9 или как обычный ASCII-символ е, со следующим за ним диакритическим знаком u0301. Эти два способа представления обеспечивают одинаковое отображение в текстовом редакторе, но имеют различные двоичные коды и с точки зрения компьютера считаются различными. Стандарт Юникода определяет предпочтительные способы кодирования для всех символов и задает процедуру нормализации для приведения текста к канонической форме, пригодной для сравнения. Интерпретаторы JavaScript полагают, что интерпретируемый программный код уже был нормализован, и не предпринимают никаких попыток нормализовать идентификаторы, строки или регулярные выражения.

2.2. Комментарии

JavaScript поддерживает два способа оформления комментариев. Любой текст между символами // и концом строки рассматривается как комментарий и игнорируется JavaScript. Любой текст между символами /* и */ также рассматривается как комментарий. Эти комментарии могут состоять из нескольких строк, но не могут быть вложенными. Следующие строки представляют собой корректные JavaScript-комментарии:

// Это однострочный комментарий.
/* Это тоже комментарий */ // а это другой комментарий.
/*
 * Это еще один комментарий.
 * Он располагается в нескольких строках.
 */

содержание 2.3. Литералы содержание

Литерал – это значение, указанное непосредственно в тексте программы. Ниже приводятся примеры различных литералов:

Полное описание числовых и строковых литералов приводится в главе 03. Литералы регулярных выражений рассматриваются в главе 10. Ниже приводятся более сложные выражения (смотрите раздел 4.2), которые могут служить литералами массивов и объектов:



содержание 2.4. Идентификаторы и зарезервированные слова содержание

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

my_variable_name
v13
_dummy
$str

Для совместимости и простоты редактирования для составления идентификаторов обычно используются только символы ASCII и цифры. Однако JavaScript допускает возможность использования в идентификаторах букв и цифр из полного набора символов Юникода. (Технически стандарт ECMAScript также допускает наличие в идентификаторах символов Юникода из категорий Мп, Мс и Рс при условии, что они не являются первыми символами идентификаторов.) Это позволяет программистам давать переменным имена на своих родных языках и использовать в них математические символы:

var si = true;
var π = 3.14;

Подобно другим языкам программирования, JavaScript резервирует некоторые идентификаторы. Эти «зарезервированные слова» не могут использоваться в качестве обычных идентификаторов. Они перечислены ниже.

содержание 2.4.1. Зарезервированные слова

JavaScript резервирует ряд идентификаторов, которые играют роль ключевых слов самого языка. Эти ключевые слова не могут служить идентификаторами в программах:

break delete function return typeof
case do if switch var
catch else in this void
continue false instanceof throw while
debugger finally new true with
default for null try

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

class , const , enum , export , extends , import , super

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

implements let private public yield
interface package protected static  

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

arguments , eval

Стандарт ECMAScript 3 резервирует все ключевые слова языка Java, и, хотя это требование было ослаблено в стандарте ECMAScript 5, тем не менее следует избегать использования этих идентификаторов, если необходимо обеспечить работоспособность JavaScript-кода при использовании реализаций JavaScript, соответствующих стандарту ECMAScript 3:

abstract double goto native static
boolean enum implements package super
byte export import private synchronized
char extends int protected throws
class final interface public transient
const float long short volatile

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

arguments encodeURI Infinity Number RegExp
Array encodeURIComponent isFinite Object String
Boolean Error isNaN parseFloat SyntaxError
Date eval JSON parselnt TypeError
decodeURI EvalError Math RangeError undefined
decodeURIComponent Function NaN ReferenceError URIError

Имейте в виду, что конкретные реализации JavaScript могут содержать свои предопределенные глобальные переменные и функции. Кроме того, каждая конкретная платформа JavaScript (клиентская, серверная и прочие) может иметь свой собственный список глобальных свойств. В описании объекта Window в четвертой части книги приводится список глобальных переменных и функций, определяемых реализацией клиентского JavaScript.

содержание 2.5. Необязательные точки с запятой содержание

Как и в других языках программирования, для отделения инструкций (глава 5) друг от друга в языке JavaScript используется точка с запятой (;). Использование точек с запятой имеет важное значение для ясного выражения намерений программиста: без этого разделителя по ошибке можно принять конец одной инструкции за начало следующей и наоборот. Обычно в JavaScript точку с запятой между инструкциями можно не ставить, если они находятся в разных строках. (Точку с запятой можно также опустить в конце программы или если следующей лексемой в программе является закрывающая фигурная скобка (}.) Многие программисты на JavaScript используют точки с запятой для явного обозначения концов инструкций (этот же прием используется в примерах для этой книги), даже если в этом нет необходимости. Другие опускают точки с запятой везде, где только возможно, используя их лишь в некоторых ситуациях, где они совершенно необходимы. Прежде чем выбрать тот или иной стиль, вам необходимо познакомиться с некоторыми особенностями использования точек с запятой в JavaScript.

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

a = 3;
b = 4;

Однако если эти инструкции записать, как показано ниже, первая точка с запятой становится обязательной:

а = 3; b = 4;

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

var a
a
=
3
console.log(a)

JavaScript интерпретирует этот программный код, как показано ниже:

var a; a = 3; console.log(a);

Интерпретатор JavaScript будет интерпретировать первый разрыв строки как точку с запятой, потому что он не сможет проанализировать фрагмент var a а без точки с запятой. Второй идентификатор a можно было бы интерпретировать как инструкцию a;, но JavaScript не будет воспринимать второй разрыв строки как точку с запятой, потому что он сможет продолжить синтаксический анализ и получить более длинную инструкцию a = 3;.

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

var y = x + f
(a+b).toString()

Однако круглые скобки во второй строке могут быть интерпретированы как вызов функции f из первой строки, и JavaScript будет интерпретировать этот фрагмент, как показано ниже:

var y = x + f(a+b).toString();

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

В целом, если инструкция начинается с символа (, [, /, + или -, есть вероятность, что она будет воспринята интерпретатором как продолжение предыдущей инструкции. Инструкции, начинающиеся с символов /, + и -, редко встречаются на практике, но инструкции, начинающиеся с символов ( и [, встречаются достаточно часто, по крайней мере, при использовании некоторых стилей программирования на JavaScript. Некоторые программисты любят вставлять защитную точку с запятой в начало каждой такой инструкции, чтобы обеспечить корректную ее работу, даже если предыдущая инструкция будет изменена и ранее имевшаяся завершающая точка с запятой исчезнет:

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

return
true;

интерпретатор JavaScript предположит, что программист имеет в виду следующее:

return; true;

Хотя на самом деле программист, видимо, хотел написать:

return true;

Это означает, что вы не должны вставлять разрыв строки между ключевым словом или continue и выражением, следующим за ним. Если вставить разрыв строки в таком месте, программный код, скорее всего, будет порождать ошибку во время выполнения, которую будет сложно отыскать во время отладки. Второе исключение связано с операторами ++ и -- раздел (4.8). Эти операторы могут быть префиксными, т. е. располагаться перед выражением, и постфиксными, т. е. располагаться после выражения. Если вам потребуется использовать любой из этих операторов в постфиксной форме записи, он должен находиться в той же строке, что и выражение, к которому применяется этот оператор. В противном случае разрыв строки будет интерпретироваться как точка с запятой, а оператор ++ или -- будет интерпретироваться как префиксный оператор, применяемый к выражению, следующему далее. Например, взгляните на следующий фрагмент:

х
++
У

Он будет интерпретирован как х; ++у;, а не как х++; у.