400 lines
17 KiB
Markdown
400 lines
17 KiB
Markdown
# Всё вместе: особенности JavaScript
|
||
|
||
В этой главе приводятся основные особенности JavaScript, на уровне базовых конструкций, типов, синтаксиса.
|
||
|
||
Она будет особенно полезна, если ранее вы программировали на другом языке, ну или как повторение важных моментов раздела.
|
||
|
||
Все очень компактно, со ссылками на развёрнутые описания.
|
||
|
||
[cut]
|
||
|
||
## Структура кода
|
||
|
||
Операторы разделяются точкой с запятой:
|
||
|
||
```js
|
||
//+ run no-beautify
|
||
alert('Привет'); alert('Мир');
|
||
```
|
||
|
||
Как правило, перевод строки тоже подразумевает точку с запятой. Так тоже будет работать:
|
||
|
||
```js
|
||
//+ run no-beautify
|
||
alert('Привет')
|
||
alert('Мир')
|
||
```
|
||
|
||
...Однако, иногда JavaScript не вставляет точку с запятой. Например:
|
||
|
||
```js
|
||
//+ run no-beautify
|
||
var a = 2
|
||
+3
|
||
|
||
alert(a); // 5
|
||
```
|
||
|
||
Бывают случаи, когда это ведёт к ошибкам, которые достаточно трудно найти и исправить, например:
|
||
|
||
```js
|
||
//+ run
|
||
alert("После этого сообщения будет ошибка")
|
||
|
||
[1, 2].forEach(alert)
|
||
```
|
||
|
||
Детали того, как работает код выше (массивы `[...]` и `forEach`) мы скоро изучим, здесь важно то, что при установке точки с запятой после `alert` он будет работать корректно.
|
||
|
||
**Поэтому в JavaScript рекомендуется точки с запятой ставить. Сейчас это, фактически, общепринятый стандарт.**
|
||
|
||
Поддерживаются однострочные комментарии `// ...` и многострочные `/* ... */`:
|
||
|
||
Подробнее: [](/structure).
|
||
|
||
## Переменные и типы
|
||
|
||
<ul>
|
||
<li>Объявляются директивой `var`. Могут хранить любое значение:
|
||
|
||
```js
|
||
var x = 5;
|
||
x = "Петя";
|
||
```
|
||
|
||
</li>
|
||
<li>Есть 5 "примитивных" типов и объекты:
|
||
|
||
```js
|
||
//+ no-beautify
|
||
x = 1; // число
|
||
x = "Тест"; // строка, кавычки могут быть одинарные или двойные
|
||
x = true; // булево значение true/false
|
||
x = null; // спец. значение (само себе тип)
|
||
x = undefined; // спец. значение (само себе тип)
|
||
```
|
||
|
||
Также есть специальные числовые значения `Infinity` (бесконечность) и `NaN`.
|
||
|
||
Значение `NaN` обозначает ошибку и является результатом числовой операции, если она некорректна.
|
||
</li>
|
||
<li>**Значение `null` не является "ссылкой на нулевой адрес/объект" или чем-то подобным. Это просто специальное значение.**
|
||
|
||
Оно присваивается, если мы хотим указать, что значение переменной неизвестно.
|
||
|
||
Например:
|
||
|
||
```js
|
||
var age = null; // возраст неизвестен
|
||
```
|
||
|
||
</li>
|
||
<li>**Значение `undefined` означает "переменная не присвоена".**
|
||
|
||
Например:
|
||
|
||
```js
|
||
var x;
|
||
alert( x ); // undefined
|
||
```
|
||
|
||
Можно присвоить его и явным образом: `x = undefined`, но так делать не рекомендуется.
|
||
|
||
Про объекты мы поговорим в главе [](/object), они в JavaScript сильно отличаются от большинства других языков.
|
||
</li>
|
||
<li>В имени переменной могут быть использованы любые буквы или цифры, но цифра не может быть первой. Символы доллар `$` и подчёркивание `_` допускаются наравне с буквами.</li>
|
||
</ul>
|
||
|
||
Подробнее: [](/variables), [](/types-intro).
|
||
|
||
## Строгий режим
|
||
|
||
Для того, чтобы интерпретатор работал в режиме максимального соответствия современному стандарту, нужно начинать скрипт директивой `'use strict';`
|
||
|
||
```js
|
||
'use strict';
|
||
|
||
...
|
||
```
|
||
|
||
Эта директива может также указываться в начале функций. При этом функция будет выполняться в режиме соответствия, а на внешний код такая директива не повлияет.
|
||
|
||
Одно из важных изменений в современном стандарте -- все переменные нужно объявлять через `var`. Есть и другие, которые мы изучим позже, вместе с соответствующими возможностями языка.
|
||
|
||
|
||
|
||
## Взаимодействие с посетителем
|
||
|
||
Простейшие функции для взаимодействия с посетителем в браузере:
|
||
|
||
<dl>
|
||
<dt>["prompt(вопрос[, по_умолчанию])"](https://developer.mozilla.org/en/DOM/window.prompt)</dt>
|
||
<dd>Задать `вопрос` и возвратить введённую строку, либо `null`, если посетитель нажал "Отмена".</dd>
|
||
<dt>["confirm(вопрос)"](https://developer.mozilla.org/en/DOM/window.confirm)</dt>
|
||
<dd>Задать `вопрос` и предложить кнопки "Ок", "Отмена". Возвращает, соответственно, `true/false`.</dd>
|
||
<dt>["alert(сообщение)"](https://developer.mozilla.org/en/DOM/window.alert)</dt>
|
||
<dd>Вывести сообщение на экран.</dd>
|
||
</dl>
|
||
|
||
Все эти функции являются *модальными*, т.е. не позволяют посетителю взаимодействовать со страницей до ответа.
|
||
|
||
Например:
|
||
|
||
```js
|
||
//+ run
|
||
var userName = prompt("Введите имя?", "Василий");
|
||
var isTeaWanted = confirm("Вы хотите чаю?");
|
||
|
||
alert( "Посетитель: " + userName );
|
||
alert( "Чай: " + isTeaWanted );
|
||
```
|
||
|
||
Подробнее: [](/uibasic).
|
||
|
||
## Особенности операторов
|
||
|
||
<ul>
|
||
<li>**Для сложения строк используется оператор `+`.**
|
||
|
||
Если хоть один аргумент -- строка, то другой тоже приводится к строке:
|
||
|
||
```js
|
||
//+ run
|
||
alert( 1 + 2 ); // 3, число
|
||
alert( '1' + 2 ); // '12', строка
|
||
alert( 1 + '2' ); // '12', строка
|
||
```
|
||
|
||
</li>
|
||
<li>**Сравнение `===` проверяет точное равенство, включая одинаковый тип.** Это самый очевидный и надёжный способ сравнения.
|
||
|
||
**Остальные сравнения `== < <= > >=` осуществляют числовое приведение типа:**
|
||
|
||
```js
|
||
//+ run
|
||
alert( 0 == false ); // true
|
||
alert( true > 0 ); // true
|
||
```
|
||
|
||
Исключение -- сравнение двух строк (см. далее).
|
||
|
||
**Исключение: значения `null` и `undefined` ведут себя в сравнениях не как ноль.**
|
||
<ul>
|
||
<li>Они равны `null == undefined` друг другу и не равны ничему ещё. В частности, не равны нулю.</li>
|
||
<li>В других сравнениях (кроме `===`) значение `null` преобразуется к нулю, а `undefined` -- становится `NaN` ("ошибка").</li>
|
||
</ul>
|
||
|
||
Такое поведение может привести к неочевидным результатам, поэтому лучше всего использовать для сравнения с ними `===`. Оператор `==` тоже можно, если не хотите отличать `null` от `undefined`.
|
||
|
||
Например, забавное следствие этих правил для `null`:
|
||
|
||
```js
|
||
//+ run no-beautify
|
||
alert( null > 0 ); // false, т.к. null преобразовано к 0
|
||
alert( null >= 0 ); // true, т.к. null преобразовано к 0
|
||
alert( null == 0 ); // false, в стандарте явно указано, что null равен лишь undefined
|
||
```
|
||
|
||
С точки зрения здравого смысла такое невозможно. Значение `null` не равно нулю и не больше, но при этом `null >= 0` возвращает `true`!
|
||
</li>
|
||
</ol>
|
||
|
||
|
||
<li>**Сравнение строк -- лексикографическое, символы сравниваются по своим unicode-кодам.**
|
||
|
||
Поэтому получается, что строчные буквы всегда больше, чем прописные:
|
||
|
||
```js
|
||
//+ run
|
||
alert( 'а' > 'Я' ); // true
|
||
```
|
||
|
||
</li>
|
||
</ul>
|
||
|
||
Подробнее: [](/operators), [](/comparison).
|
||
|
||
## Логические операторы
|
||
|
||
В JavaScript есть логические операторы: И (обозначается `&&`), ИЛИ (обозначается `||`) и НЕ (обозначается `!`). Они интерпретируют любое значение как логическое.
|
||
|
||
Не стоит путать их с [побитовыми операторами](/bitwise-operators) И, ИЛИ, НЕ, которые тоже есть в JavaScript и работают с числами на уровне битов.
|
||
|
||
Как и в большинстве других языков, в логических операторах используется "короткий цикл" вычислений. Например, вычисление выражения `1 && 0 && 2` остановится после первого И `&&`, т.к. понятно что результат будет ложным (ноль интерпретируется как `false`).
|
||
|
||
**Результатом логического оператора служит последнее значение в коротком цикле вычислений.**
|
||
|
||
Можно сказать и по-другому: значения хоть и интерпретируются как логические, но то, которое в итоге определяет результат, возвращается без преобразования.
|
||
|
||
Например:
|
||
|
||
```js
|
||
//+ run
|
||
alert( 0 && 1 ); // 0
|
||
alert( 1 && 2 && 3 ); // 3
|
||
alert( null || 1 || 2 ); // 1
|
||
```
|
||
|
||
Подробнее: [](/logical-ops).
|
||
|
||
## Циклы
|
||
|
||
<ul>
|
||
<li>Поддерживаются три вида циклов:
|
||
|
||
```js
|
||
// 1
|
||
while (условие) {
|
||
...
|
||
}
|
||
|
||
// 2
|
||
do {
|
||
...
|
||
} while (условие);
|
||
|
||
// 3
|
||
for (var i = 0; i < 10; i++) {
|
||
...
|
||
}
|
||
```
|
||
|
||
</li>
|
||
<li>Переменную можно объявлять прямо в цикле, но видна она будет и за его пределами.</li>
|
||
<li>Поддерживаются директивы `break/continue` для выхода из цикла/перехода на следующую итерацию.
|
||
|
||
Для выхода одновременно из нескольких уровней цикла можно задать метку.
|
||
|
||
Синтаксис: "`имя_метки:`", ставится она только перед циклами и блоками, например:
|
||
|
||
```js
|
||
*!*outer:*/!*
|
||
for(;;) {
|
||
...
|
||
for(;;) {
|
||
...
|
||
*!*break outer;*/!*
|
||
}
|
||
}
|
||
```
|
||
|
||
Переход на метку возможен только изнутри цикла, и только на внешний блок по отношению к данному циклу. В произвольное место программы перейти нельзя.
|
||
</li>
|
||
</ul>
|
||
|
||
Подробнее: [](/while-for).
|
||
|
||
## Конструкция switch
|
||
|
||
При сравнениях в конструкции `switch` используется оператор `===`.
|
||
|
||
Например:
|
||
|
||
```js
|
||
//+ run
|
||
var age = prompt('Ваш возраст', 18);
|
||
|
||
switch (age) {
|
||
case 18:
|
||
alert( 'Никогда не сработает' ); // результат prompt - строка, а не число
|
||
|
||
case "18": // вот так - сработает!
|
||
alert( 'Вам 18 лет!' );
|
||
break;
|
||
|
||
default:
|
||
alert( 'Любое значение, не совпавшее с case' );
|
||
}
|
||
```
|
||
|
||
Подробнее: [](/switch).
|
||
|
||
## Функции
|
||
|
||
Синтаксис функций в JavaScript:
|
||
|
||
```js
|
||
//+ run
|
||
// function имя(список параметров) { тело }
|
||
function sum(a, b) {
|
||
var result = a + b;
|
||
|
||
return result;
|
||
}
|
||
|
||
// использование:
|
||
alert( sum(1, 2) ); // 3
|
||
```
|
||
|
||
<ul>
|
||
<li>`sum` -- имя функции, ограничения на имя функции -- те же, что и на имя переменной.</li>
|
||
<li>Переменные, объявленные через `var` внутри функции, видны везде внутри этой функции, блоки `if`, `for` и т.п. на видимость не влияют.</li>
|
||
<li>Параметры копируются в локальные переменные `a`, `b`.
|
||
</li>
|
||
<li>Функция без `return` считается возвращающей `undefined`. Вызов `return` без значения также возвращает `undefined`:
|
||
|
||
```js
|
||
//+ run no-beautify
|
||
function f() { }
|
||
alert( f() ); // undefined
|
||
```
|
||
|
||
</li>
|
||
</ul>
|
||
|
||
Подробнее: [](/function-basics).
|
||
|
||
## Function Declaration и Expression
|
||
|
||
Функция в JavaScript является обычным значением.
|
||
|
||
Её можно создать в любом месте кода и присвоить в переменную, вот так:
|
||
|
||
```js
|
||
//+ run
|
||
var sum = function(a, b) {
|
||
var result = a + b;
|
||
|
||
return result;
|
||
}
|
||
|
||
alert( sum(1, 2) ); // 3
|
||
```
|
||
|
||
Такой синтаксис, при котором функция объявляется в контексте выражения (в данном случае, выражения присваивания), называется Function Expression, а обычный синтаксис, при котором функция объявляется в основном потоке кода -- Function Declaration.
|
||
|
||
Функции, объявленные через Function Declaration, отличаются от Function Expression тем, что интерпретатор создаёт их при входе в область видимости (в начале выполнения скрипта), так что они работают до объявления.
|
||
|
||
Обычно это удобно, но может быть проблемой, если нужно объявить функцию в зависимости от условия. В этом случае, а также в других ситуациях, когда хочется создать функцию "здесь и сейчас", используют Function Expression.
|
||
|
||
Детали: [](/function-declaration-expression).
|
||
|
||
## Named Function Expression
|
||
|
||
Если объявление функции является частью какого-либо выражения, например `var = function...` или любого другого, то это Function Expression.
|
||
|
||
В этом случае функции можно присвоить "внутреннее" имя, указав его после `function`. Оно будет видно только внутри этой функции и позволяет обратиться к функции изнутри себя. Обычно это используется для рекурсивных вызовов.
|
||
|
||
Например, создадим функцию для вычисления факториала как Function Expression и дадим ей имя `me`:
|
||
|
||
```js
|
||
//+ run
|
||
var factorial = function me(n) {
|
||
return (n == 1) ? n : n * me(n - 1);
|
||
}
|
||
|
||
alert( factorial(5) ); // 120
|
||
*!*
|
||
alert( me ); // ошибка, нет такой переменной
|
||
*/!*
|
||
```
|
||
|
||
Ограничение видимости для имени не работает в IE8-, но вызов с его помощью работает во всех браузерах.
|
||
|
||
Более развёрнуто: [](/named-function-expression).
|
||
|
||
## Итого
|
||
|
||
В этой главе мы повторили основные особенности JavaScript, знание которых необходимо для обхода большинства "граблей", да и просто для написания хорошего кода.
|
||
|
||
Это, конечно, лишь основы. Дальше вы узнаете много других особенностей и приёмов программирования на этом языке.
|