4. Основы JavaScript


4.8. Основы языка

Ядром любого языка является спецификация его работы на самом базовом уровне. Как правило, она определяет синтаксис, операторы, типы данных и встроенный функционал, на основе которых можно создавать сложные решения. В стандарте ЕСМА-262 все эти элементы определены для JavaScript в форме псевдоязыка, который называется ECMAScript.

В большинстве веб-браузеров реализована версия ECMAScript из третьей редакции ЕСМА-262. На очереди пятая редакция, которая на конец 2011 года ни в одном браузере не реализована полностью. Сведения в этой главе основаны преимущественно на третьей редакции ECMAScript, а отличия пятой редакции описаны в выносках.

4.8.1. Синтаксис

Синтаксис ECMAScript во многом похож на С (Си) и другие С-подобные языки, такие как Java и Perl. Если вы знакомы с ними, вам будет легко привыкнуть к более свободному синтаксису ECMAScript.

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

В ECMAScript все элементы, включая имена переменных, функций и операторов, чувствительны к регистру. Например, переменные test и Test различны, а ключевое слово typeof не может быть именем функции, тогда как typeOf – нормальное имя.

Идентификаторы.

Идентификатор (identifier) – это имя переменной, функции, свойства или аргумента функции. Идентификаторы могут состоять из одного или нескольких знаков, удовлетворяющих двум условиям:

  • первый знак должен быть буквой, знаком подчеркивания (_) или знаком доллара ($);
  • все остальные знаки могут быть буквами, знаками подчеркивания, знаками доллара или цифрами.

В ECMAScript-идентификаторах применяется "верблюжья" нотация. Это означает, что первая буква является строчной, а первые буквы всех последующих слов – прописными, например:

firstSecond
myCar
doSomethinglmportant

Хотя это не является требованием, рекомендуется следовать этому правилу, чтобы не отступать от формата встроенных функций и объектов ECMAScript.

Ключевые слова, зарезервированные слова и значения true, false и null не могут быть идентификаторами.

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

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

// однострочный комментарий

Блочный комментарий начинается с косой черты и звездочки (/*), а заканчивается ими же в обратном порядке (*/):

/* Это многострочный
комментарий */
Строгий режим.

В ECMAScript 5 представлена концепция строгого режима (strict mode) – особой модели синтаксического анализа и выполнения JavaScript-кода, в которой исправлены некоторые неправильные аспекты работы ECMAScript и генерируются ошибки при небезопасных действиях. Чтобы включить строгий режим для всего сценария, добавьте в начало файла следующую команду:

"use strict"

Хотя она выглядит как строка, которую забыли присвоить переменной, на самом деле это директива, переводящая JavaScript в строгий режим. Такой синтаксис был выбран специально, чтобы исключить конфликты с ECMAScript 3.

Строгий режим можно включить и для отдельной функции, добавив эту директиву в начало тела функции:

function doSomething() {
"use strict";
// тело функции

}

В строгом режиме выполнение JavaScript-кода заметно меняется, и мы не раз с этим столкнемся. Строгий режим поддерживается в Internet Explorer 10+, Firefox 4+, Safari 5.1+, Opera 12+ и Chrome.

Инструкции.

Инструкции в ECMAScript завершаются точками с запятой, хотя синтаксический анализатор сам способен определить конец инструкции, например:

var sum = а + b // правильно даже без точки с запятой, но не рекомендуется
var diff = а - b; // правильно и рекомендуется

Хотя точки с запятой в конце инструкций необязательны, рекомендуется всегда добавлять их. Это предотвращает некоторые ошибки, например незавершенный ввод, и позволяет сжимать ECMAScript-код за счет удаления пустых мест (без точек с запятой это приводит к синтаксическим ошибкам). Кроме того, это препятствует снижению быстродействия, потому что синтаксические анализаторы пытаются исправлять предполагаемые ошибки, добавляя недостающие точки с запятой.

Как и в С, при помощи фигурных скобок ({ }) несколько инструкций можно объединить в блок кода:

if (test) {
test = false;
alert (test);

}

В управляющих инструкциях вроде if блоки требуются, только если инструкций несколько, но на практике рекомендуется создавать блок даже для одной инструкции:

if (test)
alert (test); // допустимо, но чревато ошибками и не рекомендуется
if (test) {
alert (test); // предпочтительный способ
}

Использование блоков кода с управляющими инструкциями проясняет намерения программиста и предотвращает ошибки при изменении кода.

Ключевые и зарезервированные слова.

Стандарт ЕСМА-262 определяет набор ключевых слов (keywords), служащих для решения специализированных задач, таких как указание начала или конца управляющей инструкции или выполнение специфической операции. Ключевые слова нельзя использовать как идентификаторы или имена свойств. Вот их полный список (ключевое слово со звездочкой было добавлено в пятой редакции):

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

Кроме того, ЕСМА-262 содержит набор зарезервированных слов (reserved words), которые также нельзя использовать как идентификаторы или имена свойств. Хотя эти слова не имеют специфического применения в языке, они зарезервированы на будущее как потенциальные ключевые слова. Вот полный список зарезервированных слов из третьей редакции ЕСМА-262:

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

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

class const enum export extends import super

В строгом режиме в пятой редакции в этот список добавляются следующие слова:

implements let private protected public static yield
interface package

Обратите внимание, что слова let и yield были зарезервированы в пятой редакции, а все остальные – в третьей. Ради совместимости рекомендуется брать за ориентир список из третьей редакции, добавляя в него слова let и yield.

Попытка использовать ключевое слово как имя идентификатора в реализациях ECMAScript 3 приводит к ошибке "Identifier Expected" (ожидается идентификатор). Применение зарезервированного слова с этой же целью в одних реализациях допускается, а в других приводит к ошибке.

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

Кроме ключевых и зарезервированных слов в пятой редакции ЕСМА-262 налагаются ограничения на имена eval и arguments. В строгом режиме они не могут быть идентификаторами и именами свойств, иначе возникнет ошибка.

Переменные.

ECMAScript-переменные типизированы слабо, то есть могут содержать данные любого типа. Каждая переменная – это просто именованный заполнитель для значения. Для определения переменной используется оператор var (заметьте, что это одно из ключевых слов), после которого указывается имя (идентификатор) переменной, например:

var message;

Здесь определяется переменная с именем message, которая может содержать любое значение (без инициализации она содержит специальное значение undefined, описанное в следующем разделе). ECMAScript поддерживает инициализацию переменных, то есть можно одновременно определить переменную и присвоить ей значение, например:

var message = "hi";

Здесь определяется переменная message для хранения строки "hi". Инициализация не превращает переменную в строковую, она просто присваивает ей значение. После инициализации можно не только изменить хранящееся в переменной значение, но и тип этого значения, например:

var message = "hi";
message = 100;// допустимо, но не рекомендуется

В этом примере переменная message сначала определяется как строковое значение "hi", а затем перезаписывается числовым значением 100. Хотя изменять тип данных, содержащихся в переменной, не рекомендуется, в ECMAScript это возможно.

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

function test () {
var message ="hi"; // локальная переменная
}
test ();
alert (message); // ошибка!

Здесь переменная message определяется с помощью оператора var в функции test (). При создании переменной ей присваивается значение, но сразу же после этого она уничтожается, из-за чего в последней строке возникает ошибка. Однако переменную можно определить глобально, просто опустив оператор var:

function test () {
message = "hi”; // глобальная переменная
}
test ();
alert (message); // "hi"

Теперь переменная message определена как глобальная. При вызове функции test () она инициализируется и становится доступна вне функции.

Определять глобальные переменные, опуская оператор var, не рекомендуется. Код с глобальными переменными, определенными локально, трудно разбирать и сопровождать, потому что непонятно, пропущен оператор var специально или случайно. В строгом режиме при попытке присвоить значение необъявленной переменной возникает ошибка ReferenceError.

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

var message = "hi",
found = false,
age = 29;

Здесь объявляются и инициализируются три переменные. Поскольку ECMAScript типизирован слабо, в одной инструкции переменные можно инициализировать значениями разных типов. Чтобы облегчить чтение кода, можно разделить строку на несколько и добавить отступы, но это не требуется.

В строгом режиме определить переменную с именем eval или arguments нельзя. Попытка сделать это приведет к синтаксической ошибке.

4.8.2. Типы данных

В ECMAScript есть пять простых типов данных, также называемых примитивными типами (primitive types): неопределенный (undefined), нулевой (null), логический (boolean), числовой (number) и строковый (string). Есть также один сложный тип данных (object), который представляет собой неупорядоченный список пар имен и значений. Поскольку определить собственные типы данных в ECMAScript нельзя, все значения представляются с помощью одного из этих шести типов. Может показаться, что этого недостаточно, но у типов данных в ECMAScript есть динамические аспекты, благодаря которым каждый из них работает сразу за нескольких.

Оператор typeof.

Поскольку ECMAScript типизирован слабо, для работы с ним необходим какой-то способ определения типа данных переменной. Для получения этой информации можно применить к значению оператор typeof, который возвращает одну из следующих строк:

  • undefined – значение не определено;
  • boolean – значение имеет логический тип;
  • string – значение является строкой;
  • number – значение является числом;
  • object – значение является объектом (отличным от функции) или значением null;
  • function – значение является функцией.

Оператор typeof вызывается следующим образом:

var message = "some string";
alert (typeof message); // "string"
alert (typeof (message)); // "string"
alert (typeof 95); // "number"

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

Иногда typeof возвращает странные, но технически правильные значения. Так, typeof null возвращает строку "object", потому что специальное значение null считается ссылкой на пустой объект. В Safari до версии 5 (включительно) и Chrome до версии 7 (включительно) оператор typeof возвращает для регулярного выражения значение "function", а во всех остальных браузерах – "object".

Тип undefined.

Неопределенный тип (undefined) содержит единственное специальное значение undefined. Такое значение имеет переменная, объявленная с помощью оператора var, но не инициализированная:

var message;
alert (message == undefined); // true

Здесь переменная message объявляется без инициализации. Сравнение с литеральным значением undefined показывает, что они равны. Этот пример идентичен следующему:

var message = undefined;
alert (message = undefined); // true

Теперь переменная message явно инициализируется значением undefined, но это не требуется, потому что по умолчанию любая переменная без инициализации получает значение undefined.

Тип null.

Нулевой тип (null) также содержит единственный элемент – специальное значение null. Логически null – это указатель на пустой объект, поэтому оператор typeof возвращает для него строку "object":

var car = null;
alert (typeof car); // "object"

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

if (car != null) {
// какие-то действия с car
}

Значение undefined является производным от null, так что в ЕСМА-262 они определены как нестрого равные:

alert (null == undefined); // true

При сравнении значений null и undefined с помощью оператора = = всегда возвращается true, но помните, что этот оператор преобразует свои операнды.

Несмотря на то что значения null и undefined связаны, используются они поразному. Как уже отмечалось, никогда не следует явно присваивать переменной значение undefined, но к null это не относится. Каждый раз, когда нужный объект недоступен, вместо него следует использовать null. Это отражает тот факт, что значение null было введено как указатель на пустой объект, и подчеркивает его отличие от undefined.

Тип boolean.

Логический тип (boolean) – один из наиболее часто используемых в ECMAScript типов данных и имеет только два литеральных значения: true и false. Они отличаются от числовых значений: true не равно 1, a false не равно 0. Присвоить логические значения переменным можно следующим образом:

var found = true;
var lost = false;

Имейте в виду, что литералы true и false чувствительны к регистру, так что True и False (и эти же слова с другими сочетаниями прописных и строчных букв) являются допустимыми идентификаторами, но не логическими значениями.

Хотя литеральных логических значений всего два, в ECMAScript логические эквиваленты есть у всех значений. Для преобразования значения в его логический эквивалент используется специальная функция приведения типов bооlean ():

var message = "Hello world!";
var messageAsBoolean = вoolean (message);

В этом примере строка message преобразуется в логическое значение и сохраняется в переменной messageAsBoolean. Функция bоо1ean () может принимать и данные других типов, но всегда возвращает логическое значение. Правила преобразования значения в true или false зависят как от самого значения, так и от его типа (табл. 5).

5. Правила преобразования логического значения
Тип данныхЗначения, преобразуемые в trueЗначения, преобразуемые в false
booleantruefalse
stringЛюбая непустая строка"" (пустая строка)
numberЛюбое ненулевое число (включая бесконечность0, NaN
objectЛюбой объектnull
undefinedundefined

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

var message = "Hello world!";
if (message) {
alert ("Истинное значение");
}

В этом примере оповещение выводится на экран, потому что строка message автоматически преобразуется в логический эквивалент (true). Внимательно следите за тем, какие переменные используются в управляющих инструкциях. Ошибочное указание объекта вместо логического значения может радикально изменить логику приложения.

Тип number.

Пожалуй, наиболее интересным типом данных в ECMAScript является числовой (number). Он служит для представления целых чисел и чисел с плавающей точкой (которые в ряде языков называются числами с двойной точностью) в формате IEEE-754. Для поддержки чисел разных типов предусмотрено несколько разных форматов числовых литералов.

Самый простой из них – формат десятичного числа, которое можно ввести непосредственно:

var intNum = 55; // целое число

Целые числа также можно представлять как восьмеричные или шестнадцатеричные литералы. В восьмеричном литерале первой цифрой является нуль (0), за которым следует последовательность восьмеричных цифр (от 0 до 7). Если в литерале обнаруживается цифра не из этого диапазона, начальный нуль игнорируется и число интерпретируется как десятичное, например:

var octalNuml = 070; // 56 в восьмеричном формате
var octalNum2 = 079; // недопустимое восьмеричное значение – интерпретируется как 79
var octalNum3 = 08; // недопустимое восьмеричное значение – интерпретируется как 8

Числа, созданные в восьмеричном или шестнадцатеричном формате, во всех арифметических операциях используются как десятичные.

Способ хранения чисел в JavaScript позволяет представить положительный нуль (+0) и отрицательный нуль (-0). Они всегда эквивалентны, но помечаются знаками для ясности.

Значения с плавающей точкой.

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

var floatNuml = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1;// допустимо, но не рекомендуется

Из-за того что для хранения значений с плавающей точкой требуется вдвое больше памяти, чем для целых чисел, ECMAScript по возможности преобразует значения в целые числа. Если после десятичной точки нет разрядов, число становится целым. Если значение не имеет дробной части (например, 1.0), оно также преобразуется в целое число.

Тип string.

Строковый тип (string) – это последовательности 16-разрядных знаков Юникода (в том числе пустые). Строки могут быть заключены в двойные (") или одинарные (') кавычки:

var firstName = "Nicholas";
var lastName = 'Zakas';

В отличие от языка РНР, в котором интерпретация строки зависит от типа кавычек, в ECMAScript эти два варианта синтаксиса одинаковы, но кавычки в начале и конце строки не должны различаться. Например, такое выражение вызовет синтаксическую ошибку:

var firstName = ’Nicholas"; // синтаксическая ошибка – разные кавычки
Тип Object.

В ЕСМAScript объекты создаются как неспецифические сочетания данных и функциональности. Чтобы добавить в программу объект, нужно ввести оператор new и указать тип объекта. Для создания собственных объектов разработчики обычно создают экземпляры типа Object, а затем добавляют к ним свойства и (или) методы, например:

var о = new Object ();

Этот синтаксис похож на Java, хотя в ECMAScript скобки нужны только при передаче аргументов в конструктор. Если аргументов нет, скобки можно опускать (однако это не рекомендуется):

var о = new Object; // допустимо, но не рекомендуется

Сами по себе экземпляры типа Object не очень полезны, но важно понимать основы их работы, потому что подобно типу java.lang. Object в ECMAScript является родительским для всех остальных объектов. Все его свойства и методы есть у других, более специфичных объектов.

Каждый экземпляр Object имеет свойства и методы из приведенного списка:

  • constructor – функция, которая была использована для создания объекта. В предыдущем примере это функция Object();
  • hasOwnProperty (имяСвойства) – указывает, есть ли у объекта (не у прототипа) данное свойство. Имя свойства должно быть указано как строка (например, о.hasOwnProperty ("name"));
  • isPrototypeOf (объект) – определяет, является ли объект прототипом другого объекта;
  • propertyIsEnumerable (имяСвойства) – указывает, можно ли перебирать данное свойство в инструкции for-in. Как и в случае метода hasOwnProperty (), имя свойства должно быть строкой;
  • toLocaleString () – возвращает строковое представление объекта в соответствии с региональными настройками среды выполнения;
  • toString () – возвращает строковое представление объекта;
  • valueOf () – возвращает строковый, численный или логический эквивалент объекта, часто совпадающий с результатом вызова toString ().

Поскольку тип Object является родительским для всех объектов в ECMAScript, эти базовые свойства и методы есть у каждого объекта.

Технически принципы работы объектов в ЕСМА-262 относятся не ко всем объектам в JavaScript. Объекты в среде браузера, например ВОМ- и DOM-объекты, предоставляются и определяются средой. На них не распространяются требования ЕСМА-262, а потому они могут не наследоваться от типа Object.