This commit is contained in:
Ilya Kantor 2014-11-16 01:40:20 +03:00
parent 962caebbb7
commit 87bf53d076
1825 changed files with 94929 additions and 0 deletions

View file

@ -0,0 +1,143 @@
# Введение в JavaScript
Давайте посмотрим, что такого особенного в JavaScript, почему именно он, и какие еще технологии существуют, кроме JavaScript.
[cut]
## Что такое JavaScript?
*JavaScript* изначально создавался для того, чтобы сделать web-странички "живыми".
Программы на этом языке называются *скриптами*. Они подключаются напрямую к HTML и, как только загружается страничка -- тут же выполняются.
**Программы на JavaScript -- обычный текст**. Они не требуют какой-то специальной подготовки.
В этом плане JavaScript сильно отличается от другого языка, который называется Java.
[smart header="Почему <u>Java</u>Script?"]
Когда создавался язык JavaScript, у него изначально было другое название: "LiveScript". Но тогда был очень популярен язык Java, и маркетологи решили, что схожее название сделает новый язык более популярным.
Планировалось, что JavaScript будет эдаким "младшим братом" Java. Однако, история распорядилась по-своему, JavaScript сильно вырос, и сейчас это совершенно независимый язык, со своей спецификацией, которая называется <a href="http://en.wikipedia.org/wiki/ECMAScript">ECMAScript</a>, и к Java не имеет никакого отношения.
У него много особенностей, которые усложняют освоение, но по ходу учебника мы с ними разберемся.
[/smart]
Чтобы читать и выполнять текст на JavaScript, нужна специальная программа -- [интерпретатор](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D1%80%D0%B5%D1%82%D0%B0%D1%82%D0%BE%D1%80). Процесс выполнения скрипта называют *"интерпретацией"*.
[smart header="Компиляция и интерпретация, для программистов"]
Строго говоря, для выполнения программ существуют "компиляторы" и "интерпретаторы".
Компиляторы преобразуют программу в машинный код. Этот машинный код затем распространяется и запускается.
А интерпретаторы, в частности, встроенный JS-интерпретатор браузера -- получают программу в виде исходного кода. При этом распространяется именно сам исходный код (скрипт).
Современные интерпретаторы перед выполнением преобразуют JavaScript в машинный код или близко к нему, а уже затем выполняют.
[/smart]
Во все основные браузеры встроен интерпретатор JavaScript, именно поэтому они могут выполнять скрипты на странице.
Но, разумеется, этим возможности JavaScript не ограничены. Это полноценный язык, программы на котором можно запускать и на сервере, и даже в стиральной машинке, если в ней установлен соответствующий интерпретатор.
## Что умеет JavaScript?
Современный JavaScript -- это "безопасный" язык программирования общего назначения. Он не предоставляет низкоуровневых средств работы с памятью, процессором, так как изначально был ориентирован на браузеры, в которых это не требуется.
Что же касается остальных возможностей -- они зависят от окружения, в котором запущен JavaScript.
В браузере JavaScript умеет делать все, что относится к манипуляции со страницей, взаимодействию с посетителем и, в какой-то мере, с сервером:
<ul>
<li>Создавать новые HTML-теги, удалять существующие, менять стили элементов, прятать, показывать элементы и т.п.</li>
<li>Реагировать на действия посетителя, обрабатывать клики мыши, перемещение курсора, нажатие на клавиатуру и т.п. </li>
<li>Посылать запросы на сервер и загружать данные без перезагрузки страницы(эта технология называется &quot;AJAX&quot;).</li>
<li>Получать и устанавливать cookie, запрашивать данные, выводить сообщения...</li>
<li>...и многое, многое другое!</li>
</ul>
## Что НЕ умеет JavaScript?
JavaScript -- быстрый и мощный язык, но браузер накладывает на его исполнение некоторые ограничения..
Это сделано для безопасности пользователей, чтобы злоумышленник не мог с помощью JavaScript получить личные данные или как-то навредить компьютеру пользователя.
Этих ограничений нет там, где JavaScript используется вне браузера, например на сервере. Кроме того, различные браузеры предоставляют свои механизмы по установке плагинов и расширений, которые обладают расширенными возможностями, но требуют специальных действий по установке от пользователя
**Большинство возможностей JavaScript в браузере ограничено текущим окном и страницей.**
<img src="jslimit.jpg">
<ul>
<li>JavaScript не может читать/записывать произвольные файлы на жесткий диск, копировать их или вызывать программы. Он не имеет прямого доступа к операционной системе.
Современные браузеры могут работать с файлами, но эта возможность ограничена специально выделенной директорией -- *"песочницей"*. Возможности по доступу к устройствам также прорабатываются в современных стандартах и частично доступны в некоторых браузерах.
</li>
<li>JavaScript, работающий в одной вкладке, не может общаться с другими вкладками и окнами, за исключением случая, когда он сам открыл это окно или несколько вкладок из одного источника (одинаковый домен, порт, протокол).
Есть способы это обойти, и они раскрыты в учебнике, но они требуют внедрения специального кода на оба документа, которые находятся в разных вкладках или окнах. Без него, из соображений безопасности, залезть из одной вкладки в другую при помощи JavaScript нельзя.
</li>
<li>Из JavaScript можно легко посылать запросы на сервер, с которого пришла страница. Запрос на другой домен тоже возможен, но менее удобен, т.к. и здесь есть ограничения безопасности.
</li>
</ul>
## В чем уникальность JavaScript?
Есть как минимум *три* замечательных особенности JavaScript:
[compare]
+Полная интеграция с HTML/CSS.
+Простые вещи делаются просто.
+Поддерживается всеми распространенными браузерами и включен по умолчанию.
[/compare]
**Этих трёх вещей одновременно нет больше ни в одной браузерной технологии.** Поэтому JavaScript и является самым распространенным средством создания браузерных интерфейсов.
## Тенденции развития.
Перед тем, как вы планируете изучить новую технологию, полезно ознакомиться с ее развитием и перспективами. Здесь в JavaScript всё более чем хорошо.
### HTML 5
*HTML 5* -- эволюция стандарта HTML, добавляющая новые теги и, что более важно, ряд новых возможностей браузерам.
Вот несколько примеров:
<ul>
<li>Чтение/запись файлов на диск (в специальной "песочнице", то есть не любые).</li>
<li>Встроенная в браузер база данных, которая позволяет хранить данные на компьютере пользователя.</li>
<li>Многозадачность с одновременным использованием нескольких ядер процессора.</li>
<li>Проигрывание видео/аудио, без Flash.</li>
<li>2d и 3d-рисование с аппаратной поддержкой, как в современных играх.</li>
</ul>
Многие возможности HTML5 всё ещё в разработке, но браузеры постепенно начинают их поддерживать.
[summary]Тенденция: JavaScript становится всё более и более мощным и возможности браузера растут в сторону десктопных приложений.[/summary]
### EcmaScript 6
Сам язык JavaScript улучшается. Современный стандарт EcmaScript 5 включает в себя новые возможности для разработки, EcmaScript 6 будет шагом вперёд в улучшении синтаксиса языка.
Современные браузеры улучшают свои движки, чтобы увеличить скорость исполнения JavaScript, исправляют баги и стараются следовать стандартам.
[summary]Тенденция: JavaScript становится всё быстрее и стабильнее.[/summary]
Очень важно то, что новые стандарты HTML5 и ECMAScript сохраняют максимальную совместимость с предыдущими версиями. Это позволяет избежать неприятностей с уже существующими приложениями.
Впрочем, небольшая проблема с HTML5 всё же есть. Иногда браузеры стараются включить новые возможности, которые еще не полностью описаны в стандарте, но настолько интересны, что разработчики просто не могут ждать.
...Однако, со временем стандарт меняется и браузерам приходится подстраиваться к нему, что может привести к ошибкам в уже написанном (старом) коде. Поэтому следует дважды подумать перед тем, как применять на практике такие "супер-новые" решения.
При этом все браузеры сходятся к стандарту, и различий между ними уже гораздо меньше, чем всего лишь несколько лет назад.
[summary]Тенденция: всё идет к полной совместимости со стандартом.[/summary]
## Недостатки JavaScript
Зачастую, недостатки подходов и технологий -- это обратная сторона их полезности. Стоит ли упрекать молоток в том, что он -- тяжелый? Да, неудобно, зато гвозди забиваются лучше.
В JavaScript, однако, есть вполне объективные недоработки, связанные с тем, что язык, по выражению его автора (Brendan Eich) делался "за 10 бессонных дней и ночей". Поэтому некоторые моменты продуманы плохо, есть и откровенные ошибки (которые признает тот же Brendan).
Конкретные примеры мы увидим в дальнейшем, т.к. их удобнее обсуждать в процессе освоения языка.
Пока что нам важно знать, что некоторые "странности" языка не являются чем-то очень умным, а просто не были достаточно хорошо продуманы в своё время. В этом учебнике мы будем обращать особое внимание на основные недоработки и "грабли". Ничего критичного в них нет, если знаешь -- не наступишь.
**В новых версиях JavaScript (ECMAScript) эти недостатки постепенно убирают.**
Процесс внедрения небыстрый, в первую очередь из-за старых версий IE, но они постепенно вымирают. Современный IE в этом отношении несравнимо лучше.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,101 @@
# Альтернативные браузерные технологии
Современный JavaScript используется во многих областях. Если говорить о браузерах, то вместе с JavaScript на страницах используются и другие технологии.
Самые извеcтные -- это Flash, Java, ActiveX/NPAPI. Связка с ними может помочь достигнуть более интересных результатов в тех местах, где браузерный JavaScript пока не столь хорош, как хотелось бы.
[cut]
## Java
Java -- язык общего назначения, на нем можно писать самые разные программы. Для интернет-страниц есть особая возможность - написание *апплетов*.
*Апплет* -- это программа на языке Java, которую можно подключить к HTML при помощи тега `applet`:
```html
<!--+ run -->
<applet code="BTApplet.class" codebase="/files/tutorial/intro/alt/">
<param name="nodes" value="50,30,70,20,40,60,80,35,65,75,85,90">
<param name="root" value="50">
</applet>
```
Такой тег загружает Java-программу из файла `BTApplet.class` и выполняет ее с параметрами `param`. Апплет выполняется в отдельной части страницы, в прямоугольном "контейнере". Все действия пользователя внутри него обрабатывает апплет. Контейнер, впрочем, может быть и спрятан, если апплету нечего показывать.
Конечно, для этого на компьютере должна быть установлена и включена среда выполнения Java, включая браузерный плагин. Статистика показывает, что это около 80% компьютеров. Кроме того, апплет должен быть подписан сертификатом издателя (в примере выше апплет без подписи), иначе Java заблокирует его.
**Чем нам, JavaScript-разработчикам, может быть интересен Java?**
В первую очередь тем, что подписанный Java-апплет может всё то же, что и обычная программа, установлена на компьютере посетителя.
При попытке сделать потенциально опасное действие -- пользователь получает вопрос, который выглядит примерно так:
<img src="allow.png">
Обойти это подтверждение или поменять его внешний вид нельзя. То есть, согласие посетителя действительно необходимо.
И если оно есть -- то Java-апплет может выполнять любые действия, в отличие от JavaScript.
[compare]
+Java может делать *всё* от имени посетителя, совсем как установленная программа. В целях безопасности, потенциально опасные действия требуют подписанного апплета и доверия пользователя.
-Java требует больше времени для загрузки.
-Среда выполнения Java, включая браузерный плагин, должна быть установлена на компьютере посетителя и включена. Таких посетителей в интернет -- около 80%.
-Java-апплет не интегрирован с HTML-страницей, а выполняется отдельно. Но он может вызывать функции JavaScript.
[/compare]
Подписанный Java-апплет -- это возможность делать все, что угодно, на компьютере посетителя, если он вам доверяет.
Java и JavaScript могут взаимодействовать, так что можно вынести в Java те вызовы, которым нужно обойти контекст безопасности, а для самой страницы использовать JavaScript.
## ActiveX/NPAPI, плагины и расширения для браузера
ActiveX для IE и NPAPI для остальных браузеров позволяют писать плагины для браузера, в том числе на языке C. Как и в ситуации с Java-апплетом, посетитель поставит их в том случае, если вам доверяет.
Эти плагины могут как отображать содержимое специального формата (плагин для проигрывания музыки, для показа PDF), так и взаимодействовать со страницей.
ActiveX при этом еще и очень удобен в установке. Лично я - не фанат Microsoft, но видел отличные приложения, написанные на ActiveX и я могу понять, почему люди используют его и привязываются к IE.
## Adobe Flash
Adobe Flash -- кросс-браузерная платформа для мультимедиа-приложений, анимаций, аудио и видео.
*Flash-ролик* -- это скомпилированная программа, написанная на языке ActionScript. Ее можно подключить к HTML-странице и запустить в прямоугольном контейнере.
В первую очередь Flash полезен тем, что позволяет **кросс-браузерно** работать с микрофоном, камерой, с буфером обмена, а также поддерживает продвинутые возможности по работе с сетевыми соединениями.
[compare]
+Сокеты, UDP для P2P и другие продвинутые возможности по работе с сетевыми соединениями
+Поддержка мультмедиа: изображения, аудио, видео. Работа с веб-камерой и микрофоном.
-Flash должен быть установлен и включен. А на некоторых устройствах он вообще не поддерживается.
-Flash не интегрирован с HTML-страницей, а выполняется отдельно.
-Существуют ограничения безопасности, однако они немного другие, чем в JavaScript.
[/compare]
**JavaScript и ActionScript могут вызывать функции друг друга**, поэтому обычно сайты используют JavaScript, а там, где он не справляется -- можно подумать о Flash.
## Dart
Язык Dart предложен компанией Google как замена JavaScript, у которого, по выражению создателей Dart, есть [фатальные недостатки](http://lurkmore.to/%D0%A4%D0%B0%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA).
Сейчас этот язык, хотя и доступен, находится в стадии разработки. Многие из возможностей еще ожидают своей реализации, есть ряд проблем. Другие ведущие интернет-компании объявляли о своей незаинтересованности в Dart. Но в будущем он может составить конкуренцию JS, если его доведут до ума.
Программы на языке Dart специальным инструментом могут быть преобразованы в JavaScript. Конечно, при этом часть важных возможностей Dart теряется.
## CoffeeScript
Язык [CoffeeScript](http://coffeescript.org) -- это "синтаксический сахар" для JavaScript, который короче и, местами, проще.
Этот язык напрямую в браузере не работает, но предлагается специальная программа для преобразования CoffeeScript в JavaScript, и при этом, так как CoffeeScript изначально задумывался как преобразуемый в JavaScript, результирующий код выглядит вполне хорошо и работает тоже.
Есть и другие языки, которые написаны "поверх" JavaScript, например [TypeScript](http://www.typescriptlang.org/).
Строго говоря, это не альтернативные технологии, для их использования необходимо, как базу, хорошо знать и понимать JavaScript.
Возможно, вы захотите изучить этот язык после того, как освоите JavaScript, многим он нравится.
## Итого
Язык JavaScript уникален благодаря своей полной интеграции с HTML/CSS. Он работает почти у всех посетителей.
**...Но хороший JavaScript-программист не должен забывать и о других технологиях.** Ведь наша цель -- создание хороших приложений, и здесь Flash, Java, ActiveX/NPAPI имеют свои уникальные возможности, которые можно использовать вместе с JavaScript.

View file

@ -0,0 +1,111 @@
# Справочники и спецификации
В этом разделе мы познакомимся со справочниками и спецификациями.
Если вы только начинаете изучение, то вряд ли они будут нужны прямо сейчас. Тем не менее, эта глава находится в начале, так как предсказать точный момент, когда вы захотите заглянуть в справочник -- невозможно, но точно известно, что этот момент настанет.
Поэтому рекомендуется кратко взглянуть на эту страницу и взять её на заметку, чтобы при необходимости вернуться к ней в будущем.
[cut]
## Справочники, и как в них искать
Самая полная и подробная информация по JavaScript и браузерам есть в справочниках.
Её объём таков, что перевести все с английского невозможно. Даже сделать "единый полный справочник" не получается, так как изменений много и они постоянно происходят.
Тем не менее, жить вполне можно если знать, куда смотреть.
**Есть три основных справочника по JavaScript на английском языке**:
<ol>
<li>[Mozilla Developer Network](https://developer.mozilla.org/) -- содержит информацию, верную для основных браузеров. Также там присутствуют расширения только для Firefox, они помечены.
Когда мне нужно быстро найти "стандартную" информацию по `RegExp` - ввожу в Google **"RegExp MDN"**, и ключевое слово "MDN" (Mozilla Developer Network) приводит к информации из этого справочника.
</li>
<li>[MSDN](http://msdn.microsoft.com) -- справочник от Microsoft. Там много информации, в том числе и по JavaScript (они называют его "JScript"). Если нужно что-то, специфичное для IE -- лучше лезть сразу туда.
Например, для информации об особенностях `RegExp` в IE -- полезное сочетание: **"RegExp msdn"**. Иногда к поисковой фразе лучше добавить термин "JScript": **"RegExp msdn jscript"**. </li>
<li>[Safari Developer Library](https://developer.apple.com/library/safari/navigation/index.html) -- менее известен и используется реже, но в нём тоже можно найти ценную информацию.</li>
</ol>
Есть ещё справочники, не от разработчиков браузеров, но тоже хорошие:
<ol>
<li>[http://help.dottoro.com]() -- содержит подробную информацию по HTML/CSS/JavaScript.</li>
<li>[http://javascript.ru/manual]() -- справочник по JavaScript на русском языке, он содержит основную информацию по языку, без функций для работы с документом. К нему можно обращаться и по адресу, если знаете, что искать. Например, так: [http://javascript.ru/RegExp]().
</li>
<li>[http://www.quirksmode.org]() -- информация о поддержке тех или иных возможностей и несовместимостях.
Для поиска можно пользоваться комбинацией **"quirksmode onkeypress"** в Google.
</li>
</ol>
## Спецификации
Спецификация -- это самый главный, определяющий документ, в котором написано, как себя ведёт JavaScript, браузер, CSS и т.п.
Если что-то непонятно, и справочник не даёт ответ, то спецификация, как правило, раскрывает тему гораздо глубже и позволяет расставить точки над i.
### Спецификация ECMAScript
Спецификация (формальное описание синтаксиса, базовых объектов и алгоритмов) языка Javascript называется <a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>.
Ее перевод есть на сайте в разделе [стандарт языка](http://javascript.ru/ecma).
[smart header="Почему не просто &quot;JavaScript&quot; ?"]
Вы можете спросить: "Почему спецификация для JavaScript не называется просто *"JavaScript"*, зачем существует какое-то отдельное название?"
Всё потому, что JavaScript&trade; -- зарегистрированная торговая марка, принадлежащая корпорации Oracle.
Название "ECMAScript" было выбрано, чтобы сохранить спецификацию независимой от владельцев торговой марки.
[/smart]
Спецификация может рассказать многое о том, как работает язык, и является самым фундаментальным, доверенным источником информации.
Мы живем во время, когда все быстро изменяется. Современный стандарт -- это <a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMA-262 5.1</a> (или просто ES5), поддерживается всеми современными браузерами.
Не за горами -- новая спецификация ES6, в которой предусмотрены еще много полезных возможностей, делающих разработку быстрее и веселее :)
### Спецификации HTML/CSS
JavaScript -- язык общего назначения, поэтому в спецификации ECMAScript нет ни слова о браузерах.
Соответствующую информацию вы можете найти на сайте [w3.org](http://w3.org). Там расположены стандарты HTML, CSS и многие другие.
К сожалению, найти в этой куче то, что нужно, может быть нелегко, особенно когда неизвестно в каком именно стандарте искать. Самый лучший способ -- попросить Google с указанием сайта.
Например, для поиска `document.cookie` набрать [document.cookie site:w3.org](https://www.google.com/search?q=document.cookie+site%3Aw3.org).
Последние версии стандартов расположены на домене [dev.w3.org](http://dev.w3.org).
## Итого
Итак, посмотрим какие у нас есть источники информации.
Справочники:
<ul>
<li><a href="https://developer.mozilla.org/">Mozilla Developer Network</a> -- информация для Firefox и большинства браузеров.
Google-комбо: `"RegExp MDN"`, ключевое слово "MDN".</li>
<li><a href="http://msdn.microsoft.com/">MSDN</a> -- информация по IE.
Google-комбо: `"RegExp msdn"`. Иногда лучше добавить термин "JScript": `"RegExp msdn jscript"`.</li>
<li>[Safari Developer Library](https://developer.apple.com/library/safari/navigation/index.html) -- информация по Safari.</li>
<li><a href="http://help.dottoro.com">http://help.dottoro.com</a> -- подробная информация по HTML/CSS/JavaScript с учетом браузерной совместимости.
Google-комбо: `"RegExp dottoro"`.</li>
<li>[](http://javascript.ru/manual) -- справочник по JavaScript на русском языке. К нему можно обращаться и по адресу, если знаете, что искать. Например, так: [](http://javascript.ru/RegExp).
Google-комбо: `"RegExp site:javascript.ru"`.
</li>
</ul>
Спецификации содержат важнейшую информацию о том, как оно "должно работать":
<ul>
<li>JavaScript, современный стандарт [ES5 (англ)](http://www.ecma-international.org/publications/standards/Ecma-262.htm), и предыдущий [ES3 (рус)](http://javascript.ru/ecma).</li>
<li>HTML/DOM/CSS -- на сайте [w3.org](http://www.w3.org).
Google-комбо: `"document.cookie site:w3.org"`.</li>
</ul>
То, как оно на самом деле работает и несовместимости:
<ul>
<li>Смотрите <a href="http://www.quirksmode.org/">http://www.quirksmode.org/</a>. Google-комбо: `"innerHeight quirksmode"`.</li>
</ul>

View file

@ -0,0 +1,70 @@
# Редакторы для кода
Для разработки обязательно нужен хороший редактор.
Тот, который вы выберете должен иметь в своем арсенале:
<ol>
<li>Подсветку синтаксиса.</li>
<li>Автодополнение.</li>
<li>"Фолдинг" (от англ. folding) -- возможность скрыть-раскрыть блок кода.</li>
</ol>
[cut]
## IDE
Термин IDE (Integrated Development Environment) -- "интегрированная среда разработки", означает редактор, который расширен большим количеством "наворотов", умеет работать со вспомогательными системами, такими как багтрекер, контроль версий, и много чего ещё.
Как правило, IDE загружает весь проект целиком, поэтому может предоставлять автодополнение по функциям всего проекта, удобную навигацию по его файлам и т.п.
Если вы еще не задумывались над выбором IDE, присмотритесь к следующим вариантам.
<ul>
<li>Продукты IntelliJ: [WebStorm](http://www.jetbrains.com/webstorm/), а также в зависимости от дополнительного языка программирования [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) и другие.</li>
<li>Visual Studio, в сочетании с разработкой под .NET (Win)</li>
<li>Продукты на основе Eclipse, в частности [Aptana](http://www.aptana.com/) и Zend Studio</li>
<li>[Komodo IDE](http://www.activestate.com/komodo-ide) и его облегчённая версия [Komodo Edit](http://www.activestate.com/komodo-edit).</li>
<li>[Netbeans](http://netbeans.org/)</li>
</ul>
Почти все они, за исключением Visual Studio, кросс-платформенные.
Сортировка в этом списке ничего не означает. Выбор осуществляется по вкусу и по другим технологиям, которые нужно использовать вместе с JavaScript.
Большинство IDE -- платные, с возможностью скачать и бесплатно использовать некоторое время. Но их стоимость, по сравнению с зарплатой веб-разработчика, невелика, поэтому ориентироваться можно на удобство.
## Лёгкие редакторы
Лёгкие редакторы -- не такие мощные, как IDE, но они быстрые и простые, мгновенно стартуют.
Основная сфера применения лёгкого редактора -- мгновенно открыть нужный файл, чтобы что-то в нём поправить.
На практике "лёгкие" редакторы могут обладать большим количеством плагинов, так что граница между IDE и "лёгким" редактором размыта, спорить что именно редактор, а что IDE -- не имеет смысла.
Достойны внимания:
<ul>
<li><a href="http://www.sublimetext.com">Sublime Text</a> (кросс-платформенный, shareware).</li>
<li>TextMate (Mac, платный)</li>
<li><a href="http://www.scintilla.org/">SciTe</a> простой, легкий и очень быстрый (Windows, бесплатный).</li>
<li><a href="http://sourceforge.net/projects/notepad-plus/">Notepad++</a> (Windows, бесплатный).</li>
<li>Vim, Emacs. Если умеете их готовить.</li>
</ul>
## Мои редакторы
Лично мои любимые редакторы:
<ul>
<li>Как IDE -- редакторы от Jetbrains: для чистого JavaScript [WebStorm](http://www.jetbrains.com/webstorm/), если ещё какой-то язык, то в зависимости от языка: [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/). У них есть и другие редакторы под разные языки, но я ими не пользовался.</li>
<li>Как быстрый редактор -- <a href="http://www.sublimetext.com">Sublime Text</a>.</li>
<li>Иногда Visual Studio, если разработка идёт под платформу .NET (Win).</li>
</ul>
Если не знаете, что выбрать -- можно посмотреть на них ;)
## Не будем ссориться
В списках выше перечислены редакторы, которые использую я или мои знакомые -- хорошие разработчики. Конечно, существуют и другие отличные редакторы, если вам что-то нравится -- пользуйтесь.
Выбор редактора, как и любого инструмента, во многом индивидуален и зависит от ваших проектов, привычек, личных предпочтений.

View file

@ -0,0 +1,112 @@
# Консоль разработчика
При разработке скриптов всегда возможны ошибки... Впрочем, что я говорю? У вас абсолютно точно будут ошибки, если конечно вы -- человек, а не <a href="http://ru.wikipedia.org/wiki/%D0%91%D0%B5%D0%BD%D0%B4%D0%B5%D1%80_(%D0%A4%D1%83%D1%82%D1%83%D1%80%D0%B0%D0%BC%D0%B0)">робот инопланетный</a>.
Чтобы читать их в удобном виде, а также получать массу полезной информации о выполнении скриптов, в браузерах есть *инструменты разработки*.
**Для разработки рекомендуется использовать Chrome или Firefox.**
Другие браузеры, как правило, находятся в положении "догоняющих" по возможностям встроенных инструментов разработки. Если ошибка, к примеру, именно в Internet Explorer, тогда уже смотрим конкретно в нём, но обычно -- Chrome/Firefox.
В инструментах разработчика предусмотрена масса возможностей, но на текущем этапе мы просто посмотрим, как их открывать, смотреть в консоли ошибки и запускать команды JavaScript.
[cut]
## Google Chrome
Откройте страницу [bug.html](/devtools/bug.html).
В её JavaScript-коде есть ошибка. Конечно, обычному посетителю она не видна, нужно открыть инструменты разработчика.
Для этого используйте сочетание клавиш [key Ctrl+Shift+J], а если у вас Mac, то [key Cmd+Shift+J].
При этом откроются инструменты разработчика и вкладка Console, в которой будет ошибка.
Выглядеть будет примерно так:
<img src="chrome.png">
<ul>
<li>При клике на `bug.html` вы перейдёте во вкладку с кодом к месту ошибки, там будет и краткое описание ошибки.
В данном случае ошибка вызвана строкой `lalala`, которая интерпретатору непонятна. </li>
<li>Здесь же вы можете набирать команды на JavaScript, например наберите `alert("Hello")` -- команду вывода сообщения. Мы познакомимся с этой и другими командами далее.</li>
<li>Для запуска команды в консоли, нажмите [key Enter], для перевода курсора на следующую строку (если команда состоит из нескольких строк) -- [key Shift+Enter].</li>
</ul>
Далее в учебнике мы подробнее рассмотрим отладку в Chrome в главе [](/debugging-chrome).
## Firefox
Для разработки в Firefox используется расширение Firebug.
<ol>
<li>Первым делом его надо установить.
Это можно сделать со страницы <a href="https://addons.mozilla.org/ru/firefox/addon/firebug/">https://addons.mozilla.org/ru/firefox/addon/firebug/</a>.
Перезапустите браузер. Firebug появится в правом-нижнем углу окна:
<img src="firebug-gray.png">
Если иконки не видно -- возможно, у вас выключена панель расширений. Нажмите [key Ctrl+\] для ее показа.
Ну а если ее нет и там, то нажмите [key F12] -- это горячая клавиша для запуска Firebug, расширение появится, если установлено.
</li>
<li>Далее, для того чтобы консоль заработала, её надо включить.
Если консоль уже была включена ранее, то этот шаг не нужен, но если она серая -- выберите в меню `Консоль` и включите её:
<img src="firefox_console_enable.png">
</li>
<li>Для того, чтобы Firebug работал без глюков, желательно сначала открыть Firebug, а уже потом -- зайти на страницу.
С открытым Firebug зайдите на страницу с ошибкой: [bug.html](/devtools/bug.html).
Консоль покажет ошибку:
<img src="firefox.png">
Кликните на строчке с ошибкой и браузер покажет исходный код. При необходимости включайте дополнительные панели.
</li>
</ol>
Как и в Chrome, можно набирать и запускать команды, область для команд на рисунке находится справа, запуск команд осуществляется нажатием [key Ctrl+Enter] (для Mac -- [key Cmd]).
Можно перенести её вниз. нажав на кнопочку <img src="firefox_console_down.png"> -- на рисунке она не видна, но есть справа-снизу панели разработки.
Об основных возможностях можно прочитать на сайте <a href="http://firebug.ru">firebug.ru</a>.
## Internet Explorer
Панель разработчика запускается нажатием [key F12].
Откройте её и зайдите на страницу с ошибкой: [bug.html](/devtools/bug.html). Если вы разобрались с Chrome/Firefox, то дальнейшее будет вам более-менее понятно, так как инструменты IE построены позже и по аналогии с Chrome/Firefox.
## Safari
Горячие клавиши: [key Ctrl+Shift+I], [key Ctrl+Alt+C] для Mac -- [key Cmd] вместо [key Ctrl].
Для доступа к функционалу разработки через меню:
<ol>
<li>
В Safari первым делом нужно активировать меню разработки.
Откройте меню, нажав на колесико справа-сверху и выберите `Настройки`.
Затем вкладка `Дополнительно`:
<img src="safari.png">
Отметьте `Показывать меню "Разработка" в строке меню`. Закройте настройки.
</li>
<li>Нажмите на колесико и выберите `Показать строку меню`.
Инструменты будут доступны в появившейся строке меню, в пункте `Разработка`.</li>
</ol>
## Итого
Мы разобрали, как открывать инструменты разработчика и смотреть ошибки, а также запускать простые команды, не отходя от браузера.
Далее мы приступим к изучению JavaScript.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -0,0 +1,3 @@
# Введение
Про язык JavaScript и окружение для разработки на нём.

View file

@ -0,0 +1,19 @@
Код страницы:
```html
<!--+ run -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
alert('Я - JavaScript!');
</script>
</body>
</html>
```

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
alert('Я - JavaScript!');
</script>
</body>
</html>

View file

@ -0,0 +1,9 @@
# Выведите alert
[importance 5]
Сделайте страницу, которая выводит "Я - JavaScript!".
Создайте ее на диске, откройте в браузере, убедитесь, что все работает.
[demo src="solution"]

View file

@ -0,0 +1 @@
alert('Я - JavaScript!');

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="alert.js"></script>
</body>
</html>

View file

@ -0,0 +1,12 @@
Код для HTML-файла:
```html
<!--+ src="index.html" -->
```
Для файла `alert.js` из той же директории:
```js
//+ src="alert.js"
```

View file

@ -0,0 +1,7 @@
# Вывести alert внешним скриптом
[importance 5]
Возьмите решение предыдущей задачи [](/task/hello-alert) и вынесите скрипт во внешний файл `alert.js`, который расположите в той же директории.
Откройте страницу и проверьте, что вывод сообщения всё ещё работает.

View file

@ -0,0 +1,6 @@
Ответы:
<ol>
<li>Первым выполнится `big.js`, это нормальная последовательность выполнения подряд идущих скриптов.</li>
<li>Первым выполнится `small.js`, так как скрипты из-за `async` ведут себя совершенно независимо друг от друга, страница тоже от них не зависит.</li>
<li>Первым выполнится `big.js`, так как скрипты, подключённые через `defer`, сохраняют порядок выполнения относительно друг друга.</li>
</ol>

View file

@ -0,0 +1,27 @@
# Какой скрипт выполнится первым?
[importance 4]
В примере ниже подключены два скрипта `small.js` и `big.js`.
Если предположить, что `small.js` загружается гораздо быстрее, чем `big.js` -- какой выполнится первым?
```html
<script src="big.js"></script>
<script src="small.js"></script>
```
А вот так?
```html
<script async src="big.js"></script>
<script async src="small.js"></script>
```
А так?
```html
<script defer src="big.js"></script>
<script defer src="small.js"></script>
```

View file

@ -0,0 +1,17 @@
**Первым выполнится обычный скрипт.**
Заметим, что атрибуты `defer` и `async` на обычном скрипте будут проигнорированы. То есть, он работает так же, как и без них:
```html
<script>
alert("обычный скрипт");
</script>
```
Далее, обычно скрипты с `async/defer` не тормозят обработку страницы. То есть, браузер начнёт их загружать, а сам пойдёт дальше показывать страницу и выполнять скрипты.
То есть, обычный скрипт в этом случае, очевидно, выполнится первым.
...Но более того, даже если скрипты `small.js` и `big.js` не нужно загружать, а браузер берёт их из кеша, то он всё равно устроен так, что выполнит их после основных скриптов страницы.
Таким образом, первым всегда будет обычный скрипт, а вот относительный порядок `small.js` и `big.js` здесь не регламентирован.

View file

@ -0,0 +1,17 @@
# Что выполнится первым из скриптов?
[importance 4]
В этой странице есть три скрипта.
Какой выполнится первым?
```html
<script defer src="small.js"></script>
<script async src="big.js"></script>
<script defer async>
alert("обычный скрипт");
</script>
```

View file

@ -0,0 +1,315 @@
# Привет, мир!
В этой статье мы создадим простой скрипт и посмотрим, как он работает.
[cut]
## Тег SCRIPT
[smart header="А побыстрее?"]
В том (и только в том!) случае, если читатель нетерпелив и уже разрабатывал на JavaScript или имеет достаточно опыта в другом программировании, он может не читать каждую статью этого раздела, а прыгнуть сразу на главу [](/javascript-specials). Там будет кратко самое основное.
Если же у вас есть достаточно времени и желание начать с азов, то читайте дальше :)
[/smart]
Программы на языке JavaScript можно вставить в любое место HTML при помощи тега `SCRIPT`. Например:
```html
<!--+ run height=100 -->
<!DOCTYPE HTML>
<html>
<head>
<!-- Тег meta для указания кодировки -->
<meta charset="utf-8">
</head>
<body>
<p>Начало документа...</p>
*!*
<script>
alert('Привет, Мир!');
</script>
*/!*
<p>...Конец документа</p>
</body>
</html>
```
Этот пример использует следующие элементы:
<dl>
<dt><code>&lt;script&gt; ... &lt;/script&gt;</code></dt>
<dd>Тег `script` содержит исполняемый код. Предыдущие стандарты HTML требовали обязательного указания атрибута `type`, но сейчас он уже не нужен. Достаточно просто `<script>`.
Браузер, когда видит `<script>`:
<ol>
<li>Начинает отображать страницу, показывает часть документа до `script`</li>
<li>Встретив тег `script`, переключается в JavaScript-режим и не показывает, а исполняет его содержимое.</li>
<li>Закончив выполнение, возвращается обратно в HTML-режим и отображает оставшуюся часть документа.</li>
</ol>
Попробуйте этот пример в действии.
</dd>
<dt>`alert(сообщение)`</dt>
<dd>Отображает окно с сообщением и ждёт, пока посетитель не нажмёт "Ок".</dd>
</dl>
[smart header="Кодировка и тег `META`"]
При попытке сделать такой же файл у себя на диске и запустить, вы можете столкнуться с проблемой -- выводятся "кракозяблы", "квадратики" и "вопросики" вместо русского текста.
Чтобы всё было хорошо, нужно:
<ol>
<li>Убедиться, что в `HEAD` есть строка `<meta charset="utf-8">`. Если вы будете открывать файл с диска, то именно он укажет браузеру кодировку.</li>
<li>Убедиться, что редактор сохранил файл именно в кодировке UTF-8, а не, скажем, в `windows-1251`.</li>
</ol>
Указание кодировки -- часть обычного HTML, я упоминаю об этом "на всякий случай", чтобы не было сюрпризов при запуске примеров локально.
[/smart]
## Современная разметка для SCRIPT
В старых скриптах оформление тега `SCRIPT` было немного сложнее. В устаревших руководствах можно встретить следующие элементы:
<dl>
<dt>Атрибут <code>&lt;script <u>type</u>=...&gt;</code></dt>
<dd>В отличие от HTML5, стандарт HTML 4 требовал обязательного указания этого атрибута. Выглядел он так: `type="text/javascript"`. Если указать другое значение `type`, то скрипт выполнен не будет.
В современной разработке атрибут `type` не обязателен.
</dd>
<dt>Атрибут <code>&lt;script <u>language</u>=...&gt;</code></dt>
<dd>Этот атрибут предназначен для указания языка, на котором написан скрипт. По умолчанию, язык -- JavaScript, так что и этот атрибут ставить не обязательно.</dd>
<dt>Комментарии до и после скриптов</dt>
<dd>В старых руководствах и книгах иногда рекомендуют использовать HTML-комментарии внутри `SCRIPT`, чтобы спрятать Javascript от браузеров, которые не поддерживают его.
Выглядит это примерно так:
```html
<script type="text/javascript"><!--
...
//--></script>
```
Браузер, для которого предназначались такие трюки, очень старый Netscape, давно умер. Поэтому в этих комментариях нет нужды.
</dd>
</dl>
Итак, для вставки скрипта мы просто пишем `<script>`, без дополнительных атрибутов и комментариев.
## Внешние скрипты
Если JavaScript-кода много -- его выносят в отдельный файл, который подключается в HTML:
```html
<script src="/path/to/script.js"></script>
```
Здесь `/path/to/script.js` -- это абсолютный путь к файлу, содержащему скрипт (из корня сайта).
Браузер сам скачает скрипт и выполнит.
Можно указать и полный URL, например:
```html
<script src="http://code.jquery.com/jquery.js"></script>
```
Вы также можете использовать путь относительно текущей страницы, в частности `src="jquery.js"` обозначает файл из текущей директории.
Чтобы подключить несколько скриптов, используйте несколько тегов:
```html
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
...
```
[smart]
Как правило, в HTML пишут только самые простые скрипты, а сложные выносят в отдельный файл.
Браузер скачает его только первый раз и в дальнейшем, при правильной настройке сервера, будет брать из своего [кеша](http://ru.wikipedia.org/wiki/%D0%9A%D1%8D%D1%88).
Благодаря этому один и тот же большой скрипт, например, меню или библиотека функций, может использоваться на разных страницах без полной перезагрузки с сервера.
[/smart]
[warn header="Если указан атрибут `src`, то содержимое тега игнорируется."]
В одном теге `SCRIPT` нельзя одновременно подключить внешний скрипт и указать код.
Вот так не cработает:
```html
<script *!*src*/!*="file.js">
alert(1); // так как указан src, то внутренняя часть тега игнорируется
</script>
```
Нужно выбрать: либо `SCRIPT` идёт с `src`, либо содержит код. Тег выше следует разбить на два: один -- с `src`, другой -- с кодом, вот так:
```html
<script src="file.js"></script>
<script>
alert(1);
</script>
```
[/warn]
## Асинхронные скрипты: defer/async
Обычно тег `<script>` блокирует отображение страницы.
Например, в примере ниже -- пока все кролики не будут посчитаны -- нижний `<p>` не будет показан:
```html
<!--+ run height=100 -->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>Начинаем считать:</p>
*!*
<script>
alert('Первый кролик!');
alert('Второй кролик!');
alert('Третий кролик!');
</script>
*/!*
<p>Кролики посчитаны!</p>
</body>
</html>
```
Такое поведение называют "синхронным". Как правило, оно вполне нормально, но бывает беда.
Внешние скрипты `<script src="...">` блокируют отображение страницы не только на время выполнения, которое обычно небольшое, но и на время загрузки тоже!
В ряде случаев это совсем неуместно. Например, мы подключаем внешний скрипт, который показывает рекламу. По-хорошему, он не должен никак задерживать отображение страницы сайта. Реклама -- это дополнение к странице, она не должна как-то тормозить сайт и нарушать его функционал.
А представим на минуту, что сервер, с которого загружается внешний скрипт, перегружен и долго не отвечает? Тогда вся страница "подвиснет", пока он не загрузится.
Вот пример, с подобным скриптом (стоит искусственная задержка загрузки):
```html
<!--+ run height=100 -->
<p>Начало страницы...</p>
<script src="http://js.cx/hello/ads.js?speed=0"></script>
<p>...Важная информация!</p>
```
В примере выше важная информация не покажется, пока не загрузится внешний скрипт. Но действительно ли он так важен, что мы хотим заставить посетителя ждать? Если это реклама или счётчик посещаемости, то вряд ли.
Можно поставить все подобные скрипты в конец страницы -- это уменьшит проблему, но не избавит от неё полностью, поскольку если какой-то один скрипт тормозит или завис, то последующие будут его ждать.
Чтобы это обойти, можно использовать для скриптов атрибуты `async` или `defer`:
<dl>
<dt>Атрибут `async`</dt>
<dd>Поддерживается всеми браузерами, кроме IE9-. Скрипт выполняется полностью асинхронно. То есть, при обнаружении `<script async src="...">` браузер не останавливает обработку страницы, а спокойно работает дальше. Когда скрипт будет загружен -- он выполнится.</dd>
<dt>Атрибут `defer`</dt>
<dd>Поддерживается всеми браузерами, включая самые старые IE. Скрипт выполняется асинхронно, не заставляет ждать страницу, но, в отличие от `async`, браузер гарантирует, что относительный порядок скриптов с `defer` будет сохранён.
То есть, в таком коде (с `async`) первым сработает тот скрипт, который раньше загрузится:
```html
<script src="1.js" async></script>
<script src="2.js" async></script>
```
А в таком коде (с `defer`) первым сработает всегда `1.js`, а скрипт `2.js`, даже если загрузился раньше, будет его ждать.
```html
<script src="1.js" defer></script>
<script src="2.js" defer></script>
```
Атрибут `defer` используют в тех случаях, когда второй скрипт `2.js` зависит от первого `1.js`, к примеру -- использует что-то, описанное первым скриптом.
</dd>
</dl>
[warn header="Либо `async` либо `defer`"]
Одновременно указывать `async` и `defer` не имеет смысла.
В этом случае браузер использует только `async`.
[/warn]
[warn header="Атрибуты `async/defer` -- только для внешних скриптов"]
Атрибуты `async/defer` работают только в том случае, если назначены на внешние скрипты, т.е. имеющие `src`.
При попытке назначить их на обычные скрипты <code>&lt;script&gt;...&lt;/script&gt;</code>, они будут проигнороированы.
[/warn]
Тот же пример с `async`:
```html
<!--+ run height=100 -->
<p>Начало страницы...</p>
<script *!*async*/!* src="http://js.cx/hello/ads.js?speed=0"></script>
<p>...Важная информация!</p>
```
При запуске вы увидите, что вся страница отобразилась тут же, а реклама будет показана позже, когда загрузится скрипт.
### Разные типы скриптов
На странице могут одновременно быть:
<ul>
<li>обычные скрипты</li>
<li>скрипты с `async`</li>
<li>скрипты с `defer`</li>
</ul>
Если на странице используются разные виды подключения скриптов, то:
<ul>
<li>Сначала выполнятся обычные скрипты (без `async/defer`), по очереди.</li>
<li>Затем будут выполнены скрипты с `async` и `defer`, при этом относительный порядок между `defer` сохранится, а для `async` -- нет.</li>
</ul>
То есть, такие скрипты:
```html
<script src="1.js"></script>
<script src="2.js" defer></script>
<script src="3.js"></script>
<script src="4.js" defer></script>
```
Выполнятся строго в порядке `1.js` -> `3.js` -> `2.js` -> `4.js`.
А вот такие:
```html
<script src="1.js" async></script>
<script src="2.js" defer></script>
<script src="3.js" async></script>
<script src="4.js" defer></script>
```
Выполнятся в неопределённом порядке, смотря какой загрузится первым. Единственно, атрибут `defer` гарантирует, что `4.js` запустится после `2.js`.
**Большинство современных системы рекламы и счётчиков знают про эти атрибуты и используют их.**
Конечно, система рекламы должна корректно обрабатывать асинхронную загрузку скрипта, но никаких принципиальных проблем с этим нет, в частности, системы от Google и Яндекс используют такой подход по умолчанию.
## Задачи
Очень важно не только читать учебник, но делать что-то самостоятельно.
Решите задачки, чтобы удостовериться, что вы все правильно поняли.

View file

@ -0,0 +1,6 @@
<ol>
<li>Операция `a^b` ставит бит результата в `1`, если на соответствующей битовой позиции в `a` или `b` (но не одновременно) стоит `1`.
Так как в `0` везде стоят нули, то биты берутся в точности как во втором аргументе.</li>
<li>Первое побитовое НЕ `~` превращает `0` в `1`, а `1` в `0`. А второе НЕ превращает ещё раз, в итоге получается как было.</li>
</ol>

View file

@ -0,0 +1,13 @@
# Побитовый оператор и значение
[importance 5]
Почему побитовые операции в примерах ниже не меняют число? Что они делают внутри?
```js
//+ run
alert( 123 ^ 0 ); // 123
alert( 0 ^ 123 ); // 123
alert( ~~123 ); // 123
```

View file

@ -0,0 +1,14 @@
Один из вариантов такой функции:
```js
//+ run
function isInteger(num) {
return (num ^ 0) === num;
}
alert( isInteger(1) ); // true
alert( isInteger(1.5) ); // false
alert( isInteger(-0.5) ); // false
```
Обратите внимание: `num^0` -- в скобках! Это потому, что приоритет операции `^` очень низкий. Если не поставить скобку, то `===` сработает раньше. Получится `num ^ (0 === num)`, а это уже совсем другое дело.

View file

@ -0,0 +1,14 @@
# Проверка, целое ли число
[importance 3]
Напишите функцию `isInteger(num)`, которая возвращает `true`, если `num` -- целое число, иначе `false`.
Например:
```js
alert( isInteger(1) ); // true
alert( isInteger(1.5) ); // false
alert( isInteger(-0.5) ); // false
```

View file

@ -0,0 +1,23 @@
Операция над числами, в конечном итоге, сводится к битам.
Посмотрим, можно ли поменять местами биты слева и справа.
Например, таблица истинности для `^`:
<table class="bordered">
<tr>
<th>`a`</th>
<th>`b`</th>
<th>результат</th>
</tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
</table>
Случаи `0^0` и `1^1` заведомо не изменятся при перемене мест, поэтому нас не интересуют. А вот `0^1` и `1^0` эквивалентны и равны `1`.
Аналогично можно увидеть, что и другие операторы симметричны.
Ответ: **да**.

View file

@ -0,0 +1,12 @@
# Симметричны ли операции ^, |, &?
[importance 5]
Верно ли, что для любых `a` и `b` выполняются равенства ниже?
<ul>
<li>`a ^ b == b ^ a`</li>
<li>`a & b == b & a`</li>
<li>`a | b == b | a`</li>
</ul>
Иными словами, при перемене мест -- всегда ли результат остаётся тем же?

View file

@ -0,0 +1,29 @@
Всё дело в том, что побитовые операции преобразуют число в 32-битное целое.
Обычно число в JavaScript имеет 64-битный формат с плавающей точкой. При этом часть битов (`52`) отведены под цифры, часть (`11`) отведены под хранение номера позиции, на которой стоит десятичная точка, и один бит -- знак числа.
Это означает, что максимальное целое число, которое можно хранить, занимает `52` бита.
Число `12345678912345` в двоичном виде: `10110011101001110011110011100101101101011001` (44 цифры).
Побитовый оператор `^` преобразует его в 32-битное путём отбрасывания десятичной точки и "лишних" старших цифр. При этом, так как число большое и старшие биты здесь ненулевые, то, естественно, оно изменится.
Вот ещё пример:
```js
//+ run
// в двоичном виде 1000000000000000000000000000000 (31 цифры)
alert( Math.pow(2, 30) ); // 1073741824
alert( Math.pow(2, 30) ^ 0 ); // 1073741824, всё ок, длины хватает
// в двоичном виде 100000000000000000000000000000000 (33 цифры)
alert( Math.pow(2, 32) ); // 4294967296
alert( Math.pow(2, 32) ^ 0 ); // 0, отброшены старшие цифры, остались нули
// пограничный случай
// в двоичном виде 10000000000000000000000000000000 (32 цифры)
alert( Math.pow(2, 31) ); // 2147483648
alert( Math.pow(2, 31) ^ 0 ); // -2147483648, ничего не отброшено,
// но первый бит 1 теперь стоит в начале числа и является знаковым
```

View file

@ -0,0 +1,12 @@
# Почему результат разный?
[importance 5]
Почему результат второго `alert'а` такой странный?
```js
//+ run
alert( 123456789 ^ 0 ); // 123456789
alert( 12345678912345 ^ 0 ); // 1942903641
```

View file

@ -0,0 +1,752 @@
# Побитовые операторы
Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата.
**Эта глава сложная, требует дополнительных знаний в программировании и не очень важная, вы можете пропустить её.**
[cut]
## Формат 32-битного целого числа со знаком [#signed-format]
Побитовые операторы в JavaScript работают с 32-битными целыми числами в их двоичном представлении.
Это представление называется "32-битное целое со знаком, старшим битом слева и дополнением до двойки".
Разберём, как устроены числа внутри подробнее, это необходимо знать для битовых операций с ними.
<ul>
<li>Что такое [двоичная система счисления](http://ru.wikipedia.org/wiki/%C4%E2%EE%E8%F7%ED%E0%FF_%F1%E8%F1%F2%E5%EC%E0_%F1%F7%E8%F1%EB%E5%ED%E8%FF), вам, надеюсь, уже известно. При разборе побитовых операций мы будем обсуждать именно двоичное представление чисел, из 32 бит.
</li>
<li>*Старший бит слева* -- это научное название для самого обычного порядка записи цифр (от большего разряда к меньшему). При этом, если больший разряд отсутствует, то соответствующий бит равен нулю.
Примеры представления чисел в двоичной системе:
```js
a = 0; // 00000000000000000000000000000000
a = 1; // 00000000000000000000000000000001
a = 2; // 00000000000000000000000000000010
a = 3; // 00000000000000000000000000000011
a = 255;// 00000000000000000000000011111111
```
Обратите внимание, каждое число состоит ровно из 32-битов.
[smart header="Младший бит слева"]
Несмотря на то, что нам такой способ записи чисел кажется не совсем обычным, бывают языки и технологии, использующие способ записи "младший бит слева", когда биты пишутся наоборот, от меньшего разряда к большему.
Именно поэтому спецификация EcmaScript явно говорит "старший бит слева".
[/smart]
</li>
<li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел.
**Двоичный вид числа, обратного данному (например, `5` и `-5`) получается путём обращения всех битов с прибавлением 1.**
То есть, нули заменяются на единицы, единицы -- на нули и к числу прибавляется `1`. Получается внутреннее представление того же числа, но со знаком минус.
Например, вот число `314`:
```js
00000000000000000000000100111010
```
Чтобы получить `-314`, первый шаг -- обратить биты числа: заменить `0` на `1`, а `1` на `0`:
```js
11111111111111111111111011000101
```
Второй шаг -- к полученному двоичному числу приплюсовать единицу, обычным двоичным сложением: `11111111111111111111111011000101 + 1 = 11111111111111111111111011000110`.
Итак, мы получили:
```js
-314 = 11111111111111111111111011000110
```
Принцип дополнения до двойки делит все двоичные представления на два множества: если крайний-левый бит равен `0` -- число положительное, если `1` -- число отрицательное. Поэтому этот бит называется <i>знаковым битом</i>.
</li>
</ul>
## Список операторов
В следующей таблице перечислены все побитовые операторы.
Далее операторы разобраны более подробно.
<table class="fullwidth-table">
<tr>
<th>Оператор</th>
<th>Использование</th>
<th>Описание</th>
</tr>
<tr>
<td>Побитовое И (AND)</td>
<td style="white-space: nowrap"><code>a &amp; b</code></td>
<td>Ставит 1 на бит результата, для которого соответствующие биты операндов равны 1.</td>
</tr>
<tr>
<td>Побитовое ИЛИ (OR)</td>
<td style="white-space: nowrap"><code>a | b</code></td>
<td>Ставит 1 на бит результата, для которого хотя бы один из соответствующих битов операндов равен 1.</td>
</tr>
<tr>
<td>Побитовое исключающее ИЛИ (XOR)</td>
<td style="white-space: nowrap"><code>a ^ b</code></td>
<td>Ставит 1 на бит результата, для которого только один из соответствующих битов операндов равен 1 (но не оба).</td>
</tr>
<tr>
<td>Побитовое НЕ (NOT)</td>
<td style="white-space: nowrap"><code>~a</code></td>
<td>Заменяет каждый бит операнда на противоположный.</td>
</tr>
<tr>
<td>Левый сдвиг</td>
<td style="white-space: nowrap">`a << b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов влево, добавляя справа нули.</td>
</tr>
<tr>
<td>Правый сдвиг, переносящий знак</td>
<td style="white-space: nowrap">`a >> b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты.</td>
</tr>
<tr>
<td>Правый сдвиг с заполнением нулями</td>
<td style="white-space: nowrap">`a >>> b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты и добавляя нули слева.</td>
</tr>
</table>
## Описание работы операторов
Побитовые операторы работают следующим образом:
<ol>
<li>Операнды преобразуются в 32-битные целые числа, представленные последовательностью битов. Дробная часть, если она есть, отбрасывается.</li>
<li>Для бинарных операторов -- каждый бит в первом операнде рассматривается вместе с соответствующим битом второго операнда: первый бит с первым, второй со вторым и т.п. Оператор применяется к каждой паре бит, давая соответствующий бит результата.</li>
<li>Получившаяся в результате последовательность бит интерпретируется как обычное число.</li>
</ol>
Посмотрим, как работают операторы, на примерах.
### & (Побитовое И)
Выполняет операцию И над каждой парой бит.
Результат `a & b` равен единице только когда оба бита `a` и `b` равны единице.
Таблица истинности для `&`:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a & b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`0`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 & 9 (по осн. 10)
= 00000000000000000000000000001000 (по осн. 2)
= 8 (по осн. 10)
```
### | (Побитовое ИЛИ)
Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1.
Таблица истинности для `|`:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a | b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 | 9 (по осн. 10)
= 00000000000000000000000000001111 (по осн. 2)
= 15 (по осн. 10)
```
### ^ (Исключающее ИЛИ)
Выполняет операцию "Исключающее ИЛИ" над каждой парой бит.
<code>a</code> Исключающее ИЛИ <code>b</code> равно 1, если только <code>a=1</code> или только <code>b=1</code>, но не оба одновременно <code>a=b=1</code>.
Таблица истинности для исключающего ИЛИ:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a ^ b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
</table>
Как видно, оно даёт 1, если ЛИБО слева `1`, ЛИБО справа `1`, но не одновременно. Поэтому его и называют "исключающее ИЛИ".
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 ^ 9 (по осн. 10)
= 00000000000000000000000000000111 (по осн. 2)
= 7 (по осн. 10)
```
[smart header="Исключающее ИЛИ в шифровании"]
Исключающее или можно использовать для шифрования, так как эта операция полностью обратима. Двойное применение исключающего ИЛИ с тем же аргументом даёт исходное число.
Иначе говоря, верна формула: `a ^ b ^ b == a`.
Пускай Вася хочет передать Пете секретную информацию `data`. Эта информация заранее превращена в число, например строка интерпретируется как последовательность кодов символов.
Вася и Петя заранее договариваются о числовом ключе шифрования `key`.
Алгоритм:
<ul>
<li>Вася берёт двоичное представление `data` и делает операцию `data ^ key`. При необходимости `data` бьётся на части, равные по длине `key`, чтобы можно было провести побитовое ИЛИ `^` для каждой части. В JavaScript оператор `^` работает с 32-битными целыми числами, так что `data` нужно разбить на последовательность таких чисел.</li>
<li>Результат `data ^ key` отправляется Пете, это шифровка.</li>
</ul>
Например, пусть в `data` очередное число равно `9`, а ключ `key` равен `1220461917`.
```js
Данные: 9 в двоичном виде
00000000000000000000000000001001
Ключ: 1220461917 в двоичном виде
01001000101111101100010101011101
Результат операции 9 ^ key:
01001000101111101100010101010100
Результат в 10-ной системе (шифровка):
1220461908
```
<ul>
<li>Петя, получив очередное число шифровки `1220461908`, применяет к нему такую же операцию `^ key`.</li>
<li>Результатом будет исходное число `data`.</li>
</ul>
В нашем случае:
```js
Полученная шифровка в двоичной системе:
9 ^ key = 1220461908
01001000101111101100010101010100
Ключ: 1220461917 в двоичном виде:
01001000101111101100010101011101
Результат операции 1220461917 ^ key:
00000000000000000000000000001001
Результат в 10-ной системе (исходное сообщение):
9
```
Конечно, такое шифрование поддаётся частотному анализу и другим методам дешифровки, поэтому современные алгоритмы используют операцию XOR `^` как одну из важных частей более сложной многоступенчатой схемы.
[/smart]
### ~ (Побитовое НЕ)
Производит операцию НЕ над каждым битом, заменяя его на обратный ему.
Таблица истинности для НЕ:
<table class="standard-table">
<tr><th>`a`</th><th>`~a`</th></tr>
<tr><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
--------------------------------
~9 (по осн. 10)
= 11111111111111111111111111110110 (по осн. 2)
= -10 (по осн. 10)
```
Из-за внутреннего представления отрицательных чисел получается так, что `~n == -(n+1)`.
Например:
```js
//+ run
alert(~3); // -4
alert(~-1); // 0
```
### << (Битовый сдвиг влево)
Операторы битового сдвига принимают два операнда. Первый -- это число для сдвига, а второй -- количество битов, которые нужно сдвинуть в первом операнде.
Оператор `<<` сдвигает первый операнд на указанное число битов влево. Лишние биты отбрасываются, справа добавляются нулевые биты.
Например, `9 << 2` даст `36`:
```js
9 (по осн.10)
= 00000000000000000000000000001001 (по осн.2)
--------------------------------
9 << 2 (по осн.10)
= 00000000000000000000000000100100 (по осн.2)
= 36 (по осн.10)
```
Операция `<< 2` сдвинула и отбросила два левых нулевых бита и добавила справа два новых нулевых.
[smart header="Левый сдвиг почти равен умножению на 2"]
Битовый сдвиг `<< N` обычно имеет тот же эффект, что и умножение на два `N` раз, например:
```js
//+ run
alert( 3 << 1 ); // 6, умножение на 2
alert( 3 << 2 ); // 12, умножение на 2 два раза
alert( 3 << 3 ); // 24, умножение на 2 три раза
```
Конечно, следует иметь в виду, что побитовые операторы работают только с 32-битными числами, поэтому верхний порог такого "умножения" ограничен:
```js
//+ run
*!*
alert(10000000000 << 1); // -1474836480, отброшен крайний-левый бит
*/!*
alert(10000000000 * 2); // 20000000000, обычное умножение
```
[/smart]
### >> (Правый битовый сдвиг, переносящий знак)
Этот оператор сдвигает биты вправо, отбрасывая лишние. При этом слева добавляется *копия* крайнего-левого бита.
Знак числа (представленный крайним-левым битом) при этом не меняется, так как новый крайний-левый бит имеет то же значение, что и исходном числе.
Поэтому он назван "переносящим знак".
Например, `9 >> 2` даст <code>2</code>:
```js
9 (по осн.10)
= 00000000000000000000000000001001 (по осн.2)
--------------------------------
9 >> 2 (по осн.10)
= 00000000000000000000000000000010 (по осн.2)
= 2 (по осн.10)
```
Операция `>> 2` сдвинула вправо и отбросила два правых бита `01` и добавила слева две копии первого бита `00`.
Аналогично, `-9 >> 2` даст `-3`:
```js
-9 (по осн.10)
= 11111111111111111111111111110111 (по осн.2)
--------------------------------
-9 >> 2 (по осн.10)
= 11111111111111111111111111111101 (по осн.2) = -3 (по осн.10)
```
Здесь операция `>> 2` сдвинула вправо и отбросила два правых бита `11` и добавила слева две копии первого бита `11`. , Знак числа сохранён, так как крайний-левый (знаковый) бит сохранил значение `1`.
[smart header="Правый сдвиг почти равен целочисленному делению на 2"]
Битовый сдвиг `>> N` обычно имеет тот же, что и целочисленное деление на два `N` раз:
```js
//+ run
alert( 100 >> 1 ); // 50, деление на 2
alert( 100 >> 2 ); // 25, деление на 2 два раза
alert( 100 >> 3 ); // 12, деление на 2 три раза, целая часть от результата
```
[/smart]
### >>> (Правый сдвиг с заполнением нулями)
Этот оператор сдвигает биты первого операнда вправо. Лишние биты справа отбрасываются. Слева добавляются нулевые биты.
Знаковый бит становится равным 0, поэтому результат всегда положителен.
**Для неотрицательных чисел правый сдвиг с заполнением нулями `>>>` и правый сдвиг с переносом знака `>>` дадут одинаковый результат, т.к в обоих случаях слева добавятся нули.**
Для отрицательных чисел -- результат работы разный. Например, `-9 >>> 2` даст `1073741821`, в отличие от `-9 >> 2` (дает `-3`):
```js
-9 (по осн.10)
= 11111111111111111111111111110111 (по осн.2)
--------------------------------
-9 >>> 2 (по осн.10)
= 00111111111111111111111111111101 (по осн.2)
= 1073741821 (по осн.10)
```
## Применение побитовых операторов
Побитовые операторы используются редко, но всё же используются.
Случаи применения побитовых операторов, которые мы здесь разберём, составляют большинство всех использований в JavaScript.
[warn header="Осторожно, приоритеты!"]
В JavaScript побитовые операторы `^`, `&`, `|` выполняются после сравнений `==`.
Например, в сравнении `a == b^0` будет сначала выполнено сравнение `a == b`, а потом уже операция `^0`, как будто стоят скобки `(a == b)^0`.
Обычно это не то, чего мы хотим. Чтобы гарантировать желаемый порядок, нужно ставить скобки: `a == (b^0)`.
[/warn]
### Маска
Для этого примера представим, что наш скрипт работает с пользователями:
<ul>
<li>`Гость`</li>
<li>`Редактор`</li>
<li>`Админ`</li>
</ul>
У каждого из них есть ряд доступов, которые можно свести в таблицу:
<table>
<tr>
<th>Пользователь</th>
<th>Просмотр статей</th>
<th>Изменение статей</th>
<th>Просмотр товаров</th>
<th>Изменение товаров</th>
<th>Управление правами</th>
</tr>
<tr>
<td>Гость</td>
<td>Да</td>
<td>Нет</td>
<td>Да</td>
<td>Нет</td>
<td>Нет</td>
</tr>
<tr>
<td>Редактор</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Нет</td>
</tr>
<tr>
<td>Админ</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
</tr>
</table>
Если вместо "Да" поставить `1`, а вместо "Нет" -- `0`, то каждый набор доступов описывается числом:
<table>
<tr>
<th>Пользователь</th>
<th>Просмотр статей</th>
<th>Изменение статей</th>
<th>Просмотр товаров</th>
<th>Изменение товаров</th>
<th>Управление правами</th>
<th>В 10-ной системе</th>
</tr>
<tr>
<td>Гость</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td> = 20</td>
</tr>
<tr>
<td>Редактор</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td> = 30</td>
</tr>
<tr>
<td>Админ</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td> = 31</td>
</tr>
</table>
**Мы "упаковали" много информации в одно число. Это экономит память. Но, кроме этого, по нему очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!**
Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности.
<ul>
<li>Доступ, соответствующий только управлению правами: `00001 (=1)` (все нули кроме `1` на позиции, соответствующей этому доступу).</li>
<li>Доступ, соответствующий только изменению товаров: `00010 (=2)`.</li>
<li>Доступ, соответствующий только просмотру товаров: `00100 (=4)`.</li>
<li>Доступ, соответствующий только изменению статей: `01000 (=8)`.</li>
<li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li>
</ul>
Например, просматривать и изменять статьи позволит доступ `access = 11000`.
Как правило, доступы задаются в виде констант:
```js
var ACCESS_ADMIN = 1; // 00001
var ACCESS_GOODS_CHANGE = 2; // 00010
var ACCESS_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000
```
Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`.
```js
var access = ACCESS_ARTICLE_VIEW | ACCESS_ARTICLE_CHANGE; // 11000
```
### Двоичные числа в JavaScript
Для удобной работы с примерами в этой статье пригодятся две функции.
<ul>
<li>`parseInt("11000", 2)` -- переводит строку с двоичной записью числа в число.</li>
<li>`n.toString(2)` -- получает для числа `n` запись в 2-ной системе в виде строки.</li>
</ul>
Например:
```js
//+ run
var access = parseInt("11000", 2); // получаем число из строки
alert(access); // 24, число с таким 2-ным представлением
var access2 = access.toString(2); // обратно двоичную строку из числа
alert(access2); // 11000
```
### Проверка доступов
Для того, чтобы понять, есть ли в доступе `access` нужный доступ, например управление правами -- достаточно применить к нему побитовый оператор И (`&`) с соответствующей маской.
Создадим для примера ряд доступов и проверим их:
```js
//+ run
var access = parseInt("11111", 2); // 31, все 1 означает, что доступ полный
alert(access & ACCESS_ADMIN); // если результат не 0, то есть доступ ACCESS_ADMIN
```
А теперь та же проверка для посетителя с другими правами:
```js
//+ run
var access = parseInt("10100"); // 20, нет 1 в конце
alert(access & ACCESS_ADMIN); // 0, нет доступа к управлению правами
```
Такая проверка работает, потому что оператор И ставит `1` на те позиции результата, на которых в обоих операндах стоит `1`.
Так что `access & 1` для любого числа `access` поставит все биты в ноль, кроме самого правого. А самый правый станет `1` только если он равен `1` в `access`.
Для полноты картины также проверим, даёт ли доступ `11111` право на изменение товаров. Для этого нужно применить к доступу оператор И (`&`) с `00010` (=`2` в 10-ной системе).
```js
//+ run
var adminAccess = 31; // 111*!*1*/!*1
alert(adminAccess & ACCESS_GOODS_CHANGE); // не 0, есть доступ к изменению товаров
```
**Можно проверить один из нескольких доступов.**
Например, проверим, есть ли права на просмотр ИЛИ изменение товаров. Соответствующие права задаются битом `1` на втором и третьем месте с конца, что даёт число `00110` (=`6` в 10-ной системе).
```js
//+ run
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_CHANGE; // 6, 00110
var access = 30; // 11*!*11*/!*0;
alert(access & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
access = parseInt("11100", 2);
alert(access & check); // не 0, есть доступ к просмотру ИЛИ изменению
```
Как видно из примера выше, если в аргументе `check` стоит ИЛИ из нескольких доступов `ACCESS_*`, то и результат проверки скажет, есть ли хотя бы один из них. А какой -- нужно смотреть отдельной проверкой, если это важно.
**Итак, маска даёт возможность удобно "паковать" много битовых значений в одно число при помощи ИЛИ `|`, а также, при помощи оператора И (`&`), проверять маску на комбинацию установленных битов.**
### Маски в функциях
Зачастую маски используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
Например:
```js
// найти пользователей с правами на изменение товаров или администраторов
findUsers(ACCESS_GOODS_CHANGE | ACCESS_ADMIN);
```
### Округление
Так как битовые операции отбрасывают десятичную часть, то их можно использовать для округления. Достаточно взять любую операцию, которая не меняет значение числа.
Например, двойное НЕ (`~`):
```js
//+ run
alert( ~~12.345 ); // 12
```
Подойдёт и Исключающее ИЛИ (`^`) с нулём:
```js
//+ run
alert( 12.345^0 ); // 12
```
Последнее даже более удобно, поскольку отлично читается:
```js
//+ run
alert( 12.3 * 14.5 ^ 0); // (=178) "12.3 умножить на 14.5 *!*и округлить*/!*"
```
У побитовых операторов достаточно низкий приоритет, он меньше чем у остальной арифметики:
```js
//+ run
alert( 1.1 + 1.2 ^ 0 ); // 2, сложение выполнится раньше округления
```
### Проверка на -1
[Внутренний формат](#signed-format) чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`.
Как видно из последнего равенства, `~n == 0` только если `n == -1`. Поэтому можно легко проверить равенство `n == -1`:
```js
//+ run
var n = 5;
if (~n) { // сработает, т.к. ~n = -(5+1) = -6
alert("n не -1"); // выведет!
}
```
```js
//+ run
var n = -1;
if (~n) { // не сработает, т.к. ~n = -(-1+1) = 0
alert("...ничего не выведет...");
}
```
Проверка на `-1` пригождается, например, при поиске символа в строке. Вызов `str.indexOf("подстрока")` возвращает позицию подстроки в `str`, или `-1` если не нашёл.
```js
//+ run
var str = "Проверка";
if (~str.indexOf("верка")) { // Сочетание "if (~...indexOf)" читается как "если найдено"
alert('найдено!');
}
```
### Умножение и деление на степени 2
Оператор `a << b`, сдвигая биты, по сути умножает `a` на <code>2<sup>b</sup></code>.
Например:
```js
//+ run
alert( 1 << 2 ); // 1*(2*2) = 4
alert( 1 << 3 ); // 1*(2*2*2) = 8
alert( 3 << 3 ); // 3*(2*2*2) = 24
```
При этом следует иметь в виду, что максимальный верхний порог такого умножения меньше, чем обычно, так как побитовый оператор оперирует 32-битными целыми, в то время как обычные операторы оперируют числами длиной 64 бита.
**Оператор `a >> b`, сдвигая биты, производит целочисленное деление `a` на <code>2<sup>b</sup></code>.**
```js
//+ run
alert( 8 >> 2 ); // 2 = 8/4, убрали 2 нуля в двоичном представлении
alert( 11 >> 2 ); // 2, целочисленное деление (менее значимые биты просто отброшены)
```
## Итого
<ul>
<li>Бинарные побитовые операторы: `& | ^ << >> >>>`.</li>
<li>Унарный побитовый оператор один: `~`.</li>
</ul>
Как правило, битовое представление числа используется для:
<ul>
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`. Кроме того, такое упакованное значение будет для функции всего одним параметром, это тоже удобно.</li>
<li>Округления числа: `(12.34^0) = 12`.</li>
<li>Проверки на равенство `-1`: `if (~n) { n не -1 }`.</li>
</ul>
[head]
<script>
var ACCESS_ADMIN = 1; // 00001
var ACCESS_GOODS_CHANGE = 2; // 00010
var ACCESS_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000
</script>
[/head]

View file

@ -0,0 +1,27 @@
JS-код:
```js
//+ demo run
var name = prompt("Ваше имя?", "");
alert(name);
```
Полная страница:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
var name = prompt("Ваше имя?", "");
alert(name);
</script>
</body>
</html>
```

View file

@ -0,0 +1,8 @@
# Простая страница
[importance 4]
Создайте страницу, которая спрашивает имя и выводит его.
[demo /]

View file

@ -0,0 +1,110 @@
# Взаимодействие с пользователем: alert, prompt, confirm
В этом разделе мы рассмотрим базовые UI операции: `alert`, `prompt` и `confirm`, которые позволяют работать с данными, полученными от пользователя.
[cut]
## alert
Синтаксис:
```js
alert(сообщение)
```
`alert` выводит на экран окно с сообщением и приостанавливает выполнение скрипта, пока пользователь не нажмет "ОК".
```js
//+ run
alert("Привет");
```
Окно сообщения, которое выводится, является *модальным окном*. Слово "модальное" означает, что посетитель не может взаимодействовать со страницей, нажимать другие кнопки и т.п., пока не разберется с окном. В данном случае - пока не нажмет на "OK".
## prompt
Функция prompt принимает два аргумента:
```js
result = prompt(title, default);
```
Она выводит модальное окно с заголовком `title`, полем для ввода текста, заполненным строкой по умолчанию `default` и кнопками OK/CANCEL.
Пользователь должен либо что-то ввести и нажать OK, либо отменить ввод кликом на CANCEL или нажатием [key Esc] на клавиатуре.
**Вызов `prompt` возвращает то, что ввел посетитель -- строку или специальное значение `null`, если ввод отменен.**
[warn header="Safari 5.1+ не возвращает `null`"]
Единственный браузер, который не возвращает `null` при отмене ввода -- это Safari. При отсутствии ввода он возвращает пустую строку. Предположительно, это ошибка в браузере.
Если нам важен этот браузер, то пустую строку нужно обрабатывать точно так же, как и `null`, т.е. считать отменой ввода.
[/warn]
Как и в случае с `alert`, окно `prompt` модальное.
```js
//+ run
var years = prompt('Сколько вам лет?', 100);
alert('Вам ' + years + ' лет!')
```
[warn header="Всегда указывайте `default`"]
Вообще, второй `default` может отсутствовать. Однако при этом IE вставит в диалог значение по умолчанию `"undefined"`.
Запустите этот код <u>в IE</u>, чтобы понять о чем речь:
```js
//+ run
var test = prompt("Тест");
```
Поэтому рекомендуется *всегда* указывать второй аргумент:
```js
//+ run
var test = prompt("Тест", ''); // <-- так лучше
```
[/warn]
## confirm
Синтаксис:
```js
result = confirm(question);
```
`confirm` выводит окно с вопросом `question` с двумя кнопками: OK и CANCEL.
**Результатом будет `true` при нажатии OK и `false` - при CANCEL([key Esc]).**
Например:
```js
//+ run
var isAdmin = confirm("Вы - администратор?");
alert(isAdmin);
```
## Особенности встроенных функций
Место, где выводится модальное окно с вопросом, и внешний вид окна выбирает браузер. Разработчик не может на это влиять.
С одной стороны -- это недостаток, т.к. нельзя вывести окно в своем дизайне.
С другой стороны, преимущество этих функций по сравнению с другими, более сложными методами взаимодействия, которые мы изучим в дальнейшем -- как раз в том, что они очень просты.
Это самый простой способ вывести сообщение или получить информацию от посетителя. Поэтому их используют в тех случаях, когда простота важна, а всякие "красивости" особой роли не играют.
## Резюме
<ul>
<li>`alert` выводит сообщение.</li>
<li>`prompt` выводит сообщение и ждет, пока пользователь введет текст, а затем возвращает введенное значение или `null`, если ввод отменен (CANCEL/[key Esc]).</li>
<li>`confirm` выводит сообщение и ждет, пока пользователь нажмет "OK" или "CANCEL" и возвращает `true/false`.</li>
</ul>

View file

@ -0,0 +1,13 @@
**Да, выведется,** т.к. внутри `if` стоит строка `"0"`.
Любая строка, кроме пустой (а здесь она не пустая), в логическом контексте является `true`.
Можно запустить и проверить:
```js
//+ run
if ("0") {
alert('Привет');
}
```

View file

@ -0,0 +1,12 @@
# if (строка с нулём)
[importance 5]
Выведется ли `alert`?
```js
if ("0") {
alert('Привет');
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<script>
var value = prompt('Каково "официальное" название JavaScript?', '');
if ( value == 'EcmaScript' ) {
alert('Верно!');
} else {
alert('Не знаете? "EcmaScript"!');
}
</script>
</body>
</html>

View file

@ -0,0 +1,6 @@
```html
<!--+ run src="ifelse_task2/index.html" -->
```

View file

@ -0,0 +1,13 @@
# Проверка стандарта
[importance 2]
Используя конструкцию `if..else`, напишите код, который будет спрашивать: "Каково "официальное" название JavaScript?".
Если посетитель вводит "EcmaScript", то выводить "Верно!", если что-то другое -- выводить "Не знаете? "EcmaScript"!".
Блок-схема:
<img src="ifelse_task2.png">
[demo src="ifelse_task2"]

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<script>
var value = prompt('Каково "официальное" название JavaScript?', '');
if ( value == 'EcmaScript' ) {
alert('Верно!');
} else {
alert('Не знаете? "EcmaScript"!');
}
</script>
</body>
</html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
var value = prompt('Введите число', 0);
if (value > 0) {
alert(1);
} else if (value < 0) {
alert(-1);
} else {
alert(0);
}
</script>
</body>
</html>

View file

@ -0,0 +1,15 @@
```js
//+ run
var value = prompt('Введите число', 0);
if (value > 0) {
alert(1);
} else if (value < 0) {
alert(-1);
} else {
alert(0);
}
```

View file

@ -0,0 +1,12 @@
# Получить знак числа
[importance 2]
Используя конструкцию `if..else`, напишите код, который получает значение `prompt`, а затем выводит `alert`:
<ul>
<li>`1`, если значение больше нуля,</li>
<li>`-1`, если значение меньше нуля,</li>
<li>`0`, если значение равно нулю.</li>
</ul>
[demo src="if_sign"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -0,0 +1,33 @@
```js
//+ run demo
var userName = prompt('Кто пришёл?', '');
if ( userName == 'Админ' ) {
var pass = prompt('Пароль?', '');
if ( pass == 'Чёрный Властелин' ) {
alert('Добро пожаловать!');
} else if ( pass == null ) { // (*)
alert('Вход отменён');
} else {
alert('Пароль неверен');
}
} else if ( userName == null ) { // (**)
alert('Вход отменён');
} else {
alert('Я вас не знаю');
}
```
Обратите внимание на проверку `if` в строках `(*)` и `(**)`. Везде, кроме Safari, нажатие "Отмена" возвращает `null`, а вот Safari возвращает при отмене пустую строку, поэтому в браузере Safari можно было бы добавить дополнительную проверку на неё.
Впрочем, такое поведение Safari является некорректным, надеемся, что скоро его исправят.
Кроме того, обратите внимание на дополнительные вертикальные отступы внутри `if`. Они не обязательны, но полезны для лучшей читаемости кода.

View file

@ -0,0 +1,17 @@
# Проверка логина
[importance 3]
Напишите код, который будет спрашивать логин (`prompt`).
Если посетитель вводит "Админ", то спрашивать пароль, если нажал отмена (escape) -- выводить "Вход отменён", если вводит что-то другое -- "Я вас не знаю".
Пароль проверять так. Если введён пароль "Чёрный Властелин", то выводить "Добро пожаловать!", иначе -- "Пароль неверен", при отмене -- "Вход отменён".
Блок-схема:
<img src="ifelse_task.png">
Для решения используйте вложенные блоки `if`. Обращайте внимание на стиль и читаемость кода.
[demo /]

View file

@ -0,0 +1,6 @@
```js
result = (a + b < 4) ? 'Мало' : 'Много';
```

View file

@ -0,0 +1,14 @@
# Перепишите 'if' в '?'
[importance 5]
Перепишите `if` с использованием оператора `'?'`:
```js
if (a + b < 4) {
result = 'Мало';
} else {
result = 'Много';
}
```

View file

@ -0,0 +1,9 @@
```js
var message = (login == 'Вася') ? 'Привет' :
(login == 'Директор') ? 'Здравствуйте' :
(login == '') ? 'Нет логина' :
'';
```

View file

@ -0,0 +1,22 @@
# Перепишите 'if..else' в '?'
[importance 5]
Перепишите `if..else` с использованием нескольких операторов `'?'`.
Для читаемости -- оформляйте код в несколько строк.
```js
var message;
if (login == 'Вася') {
message = 'Привет';
} else if (login == 'Директор') {
message = 'Здравствуйте';
} else if (login == '') {
message = 'Нет логина';
} else {
message = '';
}
```

View file

@ -0,0 +1,227 @@
# Условные операторы: if, '?'
Иногда, в зависимости от условия, нужно выполнить различные действия. Для этого используется оператор `if`.
[cut]
Например:
```js
//+ run
var year = prompt('В каком году появилась спецификация ECMA-262 5.1?', '');
if (year != 2011) alert('А вот и неправильно!');
```
## Оператор if
Оператор `if` ("если") получает условие, в примере выше это `year != 2011`. Он вычисляет его, и если результат -- `true`, то выполняет команду.
Если нужно выполнить более одной команды -- они оформляются блоком кода в фигурных скобках:
```js
if (year != 2011) {
alert('А вот..');
alert('..и неправильно!');
}
```
**Рекомендуется использовать фигурные скобки всегда, даже когда команда одна.** Это улучшает читаемость кода.
## Преобразование к логическому типу
Оператор `if (...)` вычисляет и преобразует выражение в скобках к логическому типу.
**В логическом контексте число `0`, пустая строка `""`, `null` и `undefined`, а также `NaN` являются `false`, остальные значения -- `true`.**
Например, такое условие никогда не выполнится:
```js
if (0) { // 0 преобразуется к false
...
}
```
... А такое -- выполнится всегда:
```js
if (1) { // 1 преобразуется к true
...
}
```
Вычисление условия в проверке `if (year != 2011)` может быть вынесено в отдельную переменную:
```js
var cond = (year != 2011); // true/false
if (cond) {
...
}
```
## Неверное условие, else
Необязательный блок `else` ("иначе") выполняется, если условие неверно:
```js
//+ run
var year = prompt('Введите год ECMA-262 5.1', '');
if (year == 2011) {
alert('Да вы знаток!');
} else {
alert('А вот и неправильно!'); // любое значение, кроме 2011
}
```
## Несколько условий, else if
Бывает нужно проверить несколько вариантов условия. Для этого используется блок `else if ...`. Например:
```js
//+ run
var year = prompt('В каком году появилась спецификация ECMA-262 5.1?', '');
if (year < 2011) {
alert('Это слишком рано..');
} else if (year > 2011) {
alert('Это поздновато..');
} else {
alert('Да, точно в этом году!');
}
```
В примере выше JavaScript сначала проверит первое условие, если оно ложно -- перейдет ко второму -- и так далее, до последнего `else`.
## Оператор вопросительный знак '?'
Иногда нужно в зависимости от условия присвоить переменную. Например:
```js
//+ run
var access;
var age = prompt('Сколько вам лет?', '');
*!*
if (age > 14) {
access = true;
} else {
access = false;
}
*/!*
alert(access);
```
Оператор вопросительный знак `'?'` позволяет делать это короче и проще.
Он состоит из трех частей:
```js
условие ? значение1 : значение2
```
Проверяется условие, затем если оно верно -- возвращается `значение1 `, если неверно -- `значение2`, например:
```js
access = (age > 14) ? true : false;
```
Оператор `'?'` выполняется позже большинства других, в частности -- позже сравнений, поэтому скобки можно не ставить:
```js
access = age > 14 ? true : false;
```
...Но когда скобки есть -- код лучше читается. Так что рекомендуется их писать.
[smart]
В данном случае можно было бы обойтись и без оператора `'?'`, т.к. сравнение само по себе уже возвращает `true/false`:
```js
access = age > 14;
```
[/smart]
[smart header="\"Тернарный оператор\""]
Вопросительный знак -- единственный оператор, у которого есть аж три аргумента, в то время как у обычных операторов их один-два.
Поэтому его называют *"тернарный оператор"*.
[/smart]
## Несколько операторов '?'
Несколько операторов `if..else` можно заменить последовательностью операторов `'?'`. Например:
```js
//+ run
var a = prompt('a?', 1);
*!*
var res = (a == 1) ? 'значение1' :
(a == 2) ? 'значение2' :
(a > 2) ? 'значение3' :
'значение4';
*/!*
alert(res);
```
Поначалу может быть сложно понять, что происходит. Однако, внимательно приглядевшись, мы замечаем, что это *обычный `if..else`*!
Вопросительный знак проверяет сначала `a == 1`, если верно -- возвращает `значение1`, если нет -- идет проверять `a == 2`. Если это верно -- возвращает `значение2`, иначе проверка `a > 2` и `значение3`... Наконец, если ничего не верно, то `значение4`.
Альтернативный вариант с `if..else`:
```js
var res;
if (a == 1) {
res = 'значение1';
} else if (a == 2) {
res = 'значение2';
} else if (a > 2) {
res = 'значение3';
} else {
res = 'значение4';
}
```
## Нетрадиционное использование '?'
Иногда оператор вопросительный знак `'?'` используют как замену `if`:
```js
//+ run
var company = prompt('Какая компания создала JavaScript?', '');
*!*
(company == 'Netscape') ?
alert('Да, верно') : alert('Неправильно');
*/!*
```
Работает это так: в зависимости от условия, будет выполнена либо первая, либо вторая часть после `'?'`.
Результат выполнения не присваивается в переменную, так что пропадёт (впрочем, `alert` ничего не возвращает).
**Рекомендуется не использовать вопросительный знак таким образом.**
Несмотря на то, что с виду такая запись короче `if`, она является существенно менее читаемой.
Вот, для сравнения, то же самое с `if`:
```js
//+ run
var company = prompt('Какая компания создала JavaScript?', '');
*!*
if (company == 'Netscape') {
alert('Да, верно');
} else {
alert('Неправильно');
}
*/!*
```

View file

@ -0,0 +1,7 @@
Ответ: `2`, это первое значение, которое в логическом контексте даст `true`.
```js
//+ run
alert( null || 2 || undefined );
```

View file

@ -0,0 +1,10 @@
# Что выведет alert (ИЛИ)?
[importance 5]
Что выведет код ниже?
```js
alert( null || 2 || undefined );
```

View file

@ -0,0 +1,15 @@
Ответ: сначала `1`, затем `2`.
```js
//+ run
alert( alert(1) || 2 || alert(3) );
```
Вызов `alert` не возвращает значения, или, иначе говоря, возвращает `undefined`.
<ol>
<li>Первый оператор ИЛИ `||` выполнит первый `alert(1)`, получит `undefined` и пойдёт дальше, ко второму операнду.</li>
<li>Так как второй операнд `2` является истинным, то вычисления завершатся, результатом `undefined || 2` будет `2`, которое будет выведено внешним `alert( .... )`.</li>
</ol>
Второй оператор `||` не будет выполнен, выполнение до `alert(3)` не дойдёт, поэтому `3` выведено не будет.

View file

@ -0,0 +1,10 @@
# Что выведет alert (ИЛИ)?
[importance 3]
Что выведет код ниже?
```js
alert( alert(1) || 2 || alert(3) );
```

View file

@ -0,0 +1,7 @@
Ответ: `null`, это первое ложное значение из списка.
```js
//+ run
alert( 1 && null && 2 );
```

View file

@ -0,0 +1,10 @@
# Что выведет alert (И)?
[importance 5]
Что выведет код ниже?
```js
alert( 1 && null && 2 );
```

View file

@ -0,0 +1,10 @@
Ответ: `1`, а затем `undefined`.
```js
//+ run
alert( alert(1) && alert(2) );
```
Вызов `alert` не возвращает значения, или, иначе говоря, возвращает `undefined`.
Поэтому до правого `alert` дело не дойдёт, вычисления закончатся на левом.

View file

@ -0,0 +1,10 @@
# Что выведет alert (И)?
[importance 3]
Что выведет код ниже?
```js
alert( alert(1) && alert(2) );
```

View file

@ -0,0 +1,6 @@
```js
if (age >= 14 && age <= 90)
```

View file

@ -0,0 +1,7 @@
# Проверка if внутри диапазона
[importance 3]
Напишите условие `if` для проверки того факта, что переменная `age` находится между `14` и `90` включительно.
"Включительно" означает, что концы промежутка включены, то есть `age` может быть равна `14` или `90`.

View file

@ -0,0 +1,12 @@
Первый вариант:
```js
if ( !(age >= 14 && age <= 90) )
```
Второй вариант:
```js
if (age < 14 || age > 90)
```

View file

@ -0,0 +1,7 @@
# Проверка if вне диапазона
[importance 3]
Напишите условие `if` для проверки того факта, что `age` НЕ находится между 14 и 90 включительно.
Сделайте два варианта условия: первый с использованием оператора НЕ `!`, второй - без этого оператора.

View file

@ -0,0 +1,21 @@
Ответ: первое и третье выполнятся.
Детали:
```js
//+ run
// Выполнится
// Результат -1 || 0 = -1, в логическом контексте true
if (-1 || 0) alert('первое');
// Не выполнится
// -1 && 0 = 0, в логическом контексте false
if (-1 && 0) alert('второе');
// Выполнится
// оператор && имеет больший приоритет, чем ||
// так что -1 && 1 выполнится раньше
// вычисления: null || -1 && 1 -> null || 1 -> 1
if (null || -1 && 1) alert('третье');
```

View file

@ -0,0 +1,14 @@
# Вопрос про "if"
[importance 5]
Какие из этих `if` верны, т.е. выполнятся?
Какие конкретно значения будут результатами выражений в условиях `if(...)`?
```js
if (-1 || 0) alert('первое');
if (-1 && 0) alert('второе');
if (null || -1 && 1) alert('третье');
```

View file

@ -0,0 +1,249 @@
# Логические операторы
В JavaScript поддерживаются операторы `||` (ИЛИ), `&&` (И) и `!` (НЕ).
Они называются *"логическими"*, но в JavaScript могут применяться к значениям любого типа и возвращают также значения любого типа.
[cut]
## || (ИЛИ)
Оператор ИЛИ выглядит как двойной символ вертикальной черты:
```js
result = a || b;
```
**Логическое ИЛИ в классическом программировании работает следующим образом: "если *хотя бы один* из аргументов `true`, то возвращает `true`, иначе -- `false`".**
Получается следующая таблица результатов:
```js
//+ run
alert( true || true ); // true
alert( false || true ); // true
alert( true || false); // true
alert( false || false); // false
```
При вычислении ИЛИ в JavaScript можно использовать любые значения. В этом случае они будут интерпретироваться как логические.
Например, число `1` будет воспринято как `true`, а `0` -- как `false`:
```js
//+ run
if ( 1 || 0 ) { // сработает как if( true || false )
alert('верно');
}
```
Обычно оператор ИЛИ используется в `if`, чтобы проверить, выполняется ли хотя бы одно из условий, например:
```js
//+ run
var hour = 9;
*!*
if (hour < 10 || hour > 18) {
*/!*
alert('Офис до 10 или после 18 закрыт');
}
```
Можно передать и больше условий:
```js
//+ run
var hour = 12, isWeekend = true;
if (hour < 10 || hour > 18 || isWeekend) {
alert('Офис до 10 или после 18 или в выходной закрыт');
}
```
## Короткий цикл вычислений
JavaScript вычисляет несколько ИЛИ слева направо. При этом, чтобы экономить ресурсы, используется так называемый *"короткий цикл вычисления"*.
Допустим, вычисляются несколько ИЛИ подряд: `a || b || c || ...`. Если первый аргумент -- `true`, то результат заведомо будет `true` (хотя бы одно из значений -- `true`), и остальные значения игнорируются.
Это особенно заметно, когда выражение, переданное в качестве второго аргумента, имеет *сторонний эффект* -- например, присваивает переменную.
При запуске примера ниже присвоение `x` не произойдёт:
```js
//+ run
var x;
*!*true*/!* || (x = 1); // просто вычислим ИЛИ, без if
alert(x); // undefined, x не присвоен
```
...А в примере ниже первый аргумент -- `false`, так что ИЛИ попытается вычислить второй, запустив тем самым присваивание:
```js
//+ run
var x;
*!*false*/!* || (x = 1);
alert(x); // 1
```
## Значение ИЛИ
Итак, как мы видим, оператор ИЛИ вычисляет ровно столько значений, сколько необходимо -- до первого `true`.
**Оператор ИЛИ возвращает то значение, на котором остановились вычисления.**
Примеры:
```js
//+ run
alert( 1 || 0 ); // 1
alert( true || 'неважно что'); // true
alert( null || 1 ); // 1
alert( undefined || 0 ); // 0
```
Это используют, в частности, чтобы выбрать первое "истинное" значение из списка:
```js
//+ run
var undef; // переменная не присвоена, т.е. равна undefined
var zero = 0;
var emptyStr = "";
var msg = "Привет!";
*!*
var result = undef || zero || emptyStr || msg || 0;
*/!*
alert(result); // выведет "Привет!" - первое значение, которое является true
```
## && (И)
Оператор И пишется как два амперсанда `&&`:
```js
result = a && b;
```
**В классическом программировании И возвращает `true`, если оба аргумента истинны, а иначе -- `false`**
```js
//+ run
alert( true && true ); // true
alert( false && true ); // false
alert( true && false); // false
alert( false && false); // false
```
Пример:
```js
//+ run
var hour = 12, minute = 30;
if (hour == 12 && minute == 30) {
alert('Время 12:30');
}
```
Как и в ИЛИ, допустимы любые значения:
```js
//+ run
if ( 1 && 0 ) { // вычислится как true && false
alert('не сработает, т.к. условие ложно');
}
```
К И применим тот же принцип "короткого цикла вычислений", но немного по-другому, чем к ИЛИ.
**Если левый аргумент -- `false`, оператор И возвращает его и заканчивает вычисления, а иначе -- вычисляет и возвращает правый аргумент.**
Например:
```js
//+ run
// Первый аргумент - true,
// Поэтому возвращается второй аргумент
alert(1 && 0); // 0
alert(1 && 5); // 5
// Первый аргумент - false,
// Он и возвращается, а второй аргумент игнорируется
alert(null && 5); // null
alert(0 && "не важно"); // 0
```
**Приоритет оператора И `&&` больше, чем ИЛИ `||`, т.е. он выполняется раньше.**
Поэтому в следующем коде сначала будет вычислено правое И: `1 && 0 = 0`, а уже потом -- ИЛИ.
```js
//+ run
alert(5 || 1 && 0); // 5
```
[warn header="Не используйте `&&` вместо `if`"]
Оператор `&&` в простых случаях можно использовать вместо `if`, например:
```js
//+ run
var x = 1;
(x > 0) && alert('Больше');
```
Действие в правой части `&&` выполнится только в том случае, если до него дойдут вычисления. То есть, если в левой части будет `true`.
Получился аналог:
```js
//+ run
var x = 1;
if (x > 0) {
alert('Больше');
}
```
Однако, как правило, `if` лучше читается и воспринимается. Он более очевиден, поэтому лучше использовать его. Это, впрочем, относится и к другим неочевидным применениям возможностей языка.
[/warn]
## ! (НЕ)
Оператор НЕ -- самый простой. Он получает один аргумент. Синтаксис:
```js
var result = !value;
```
Действия `!`:
<ol>
<li>Сначала приводит аргумент к логическому типу `true/false`.</li>
<li>Затем возвращает противоположное значение.</li>
</ol>
Например:
```js
//+ run
alert( !true ) // false
alert( !0 ) // true
```
**В частности, двойное НЕ используются для преобразования значений к логическому типу:**
```js
//+ run
alert( !!"строка" ) // true
alert( !!null ) // false
```

View file

@ -0,0 +1,31 @@
```js
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
parseInt("09") = "0" или "9" // (3)
" -9\n" + 5 = " -9\n5"
" -9\n" - 5 = -14
5 && 2 = 2
2 && 5 = 5
5 || 0 = 5
0 || 5 = 5
null + 1 = 1 // (4)
undefined + 1 = NaN // (5)
```
<ol>
<li>Оператор `"+"` в данном случае прибавляет `1` как строку, и затем `0`.</li>
<li>Оператор `"-"` работает только с числами, так что он сразу приводит `""` к `0`.</li>
<li>В некоторых браузерах `parseInt` без второго аргумента интерпретирует `09` как восьмиричное число.</li>
<li>`null` при численном преобразовании становится `0`</li>
<li>`undefined` при численном преобразовании становится `NaN`</li>
</ol>

View file

@ -0,0 +1,28 @@
# Вопросник по преобразованиям, для примитивов
[importance 5]
Подумайте, какой результат будет у выражений ниже. Тут не только преобразования типов. Когда закончите -- сверьтесь с решением.
```js
"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
parseInt("09")
" -9\n" + 5
" -9\n" - 5
5 && 2
2 && 5
5 || 0
0 || 5
null + 1
undefined + 1
```

View file

@ -0,0 +1,210 @@
# Преобразование типов для примитивов
Система преобразования типов в JavaScript очень проста, но отличается от других языков. Поэтому она часто служит "камнем преткновения" для приходящих из других языков программистов.
[cut]
Всего есть три преобразования:
<ol>
<li>роковое преобразование.</li>
<li>Числовое преобразование.</li>
<li>Преобразование к логическому значению.</li>
</ol>
**Эта глава описывает преобразование только примитивных значений, объекты разбираются далее в учебнике.**
## Строковое преобразование
Строковое преобразование происходит, когда требуется представление чего-либо в виде строки. Например, его производит функция `alert`.
```js
//+ run
var a = true;
alert(a); // "true"
```
Можно также осуществить преобразование явным вызовом `String(val)`:
```js
//+ run
alert( String(null) === "null" ); // true
```
Также для явного преобразования применяется оператор `"+"`, у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:
```js
//+ run
alert( true + "test" ); // "truetest"
alert( "123" + undefined); // "123undefined"
```
## Численное преобразование
Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений `===`, `!==`).
Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением оператор унарный плюс `"+"`:
```js
var a = +"123"; // 123
var a = Number("123"); // 123, тот же эффект
```
<table class="bordered">
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
<tr><td>`true / false`</td><td>`1 / 0`</td></tr>
<tr><td>Строка</td><td>Пробельные символы по краям обрезаются.<br>Далее, если остаётся пустая строка, то `0`.<br>Из непустой строки "считывается" число, при ошибке результат: `NaN`.</td></tr>
</table>
Например:
```js
//+ run
alert( +" \n 123 \n \n"); // 123
```
Ещё примеры:
<ul>
<li>Логические значения:
```js
//+ run
alert( +true ); // 1
alert( +false); // 0
```
</li>
<li>Сравнение разных типов -- значит численное преобразование:
```js
//+ run
alert( "\n0\n" == 0 ); // true
```
При этом строка `"\n0\n"` преобразуется к числу -- начальные и конечные пробелы игнорируются, получается `0`.</li>
</li>
<li>
Ещё пример сравнения разных типов:
```js
//+ run
alert( "\n" == false );
alert( "1" == true );
```
Здесь сравнение `"=="` снова приводит обе части к числу. В первой строке слева и справа получается `0`, во второй `1`.
</li>
</ul>
### Специальные значения
Посмотрим на поведение специальных значений более внимательно.
**Интуитивно, значения `null/undefined` ассоциируются с нулём, но при преобразованиях ведут себя иначе.**
Специальные значения преобразуются к числу так:
<table class="bordered">
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
</table>
Это преобразование осуществляется при арифметических операциях и сравнениях `> >= < <=`, но не при проверке равенства `==`. Алгоритм проверки равенства для этих значений в спецификации прописан отдельно (пункт [11.9.3](http://es5.github.com/x11.html#x11.9.3)). В нём считается, что `null` и `undefined` равны `"=="` между собой, но эти значения не равны никакому другому значению.
Это ведёт к забавным последствиям.
Например, `null` не подчиняется законам математики -- он "больше либо равен нулю": `null>=0`, но не больше и не равен:
```js
//+ run
alert(null >= 0); // true, т.к. null преобразуется к 0
alert(null > 0); // false (не больше), т.к. null преобразуется к 0
alert(null == 0 ); // false (и не равен!), т.к. == рассматривает null особо.
```
Значение `undefined` вообще вне сравнений:
```js
//+ run
alert(undefined > 0); // false, т.к. undefined -> NaN
alert(undefined == 0); // false, т.к. это undefined (без преобразования)
alert(undefined < 0); // false, т.к. undefined -> NaN
```
**Для более очевидной работы кода и во избежание ошибок лучше не давать специальным значениям участвовать в сравнениях `> >= < <=`.**
Используйте в таких случаях переменные-числа или приводите к числу явно.
## Логическое преобразование
Преобразование к `true/false` происходит в логическом контексте, таком как `if(obj)`, `while(obj)` и при применении логических операторов.
Все значения, которые интуитивно "пусты", становятся `false`. Их несколько: `0`, пустая строка, `null`, `undefined` и `NaN`.
Остальное, в том числе и любые объекты -- `true`.
Полная таблица преобразований:
<table class="bordered">
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
<tr><td>`undefined`, `null`</td><td>`false`</td></tr>
<tr><td>Числа</td><td>Все `true`, кроме `0`, `NaN` -- `false`.</td></tr>
<tr><td>Строки</td><td>Все `true`, кроме пустой строки `""` -- `false`</td></tr>
<tr><td>Объекты</td><td>Всегда `true`</td></tr>
</table>
**Для явного преобразования используется двойное логическое отрицание `!!value` или вызов `Boolean(value)`.**
[warn header="Обратите внимание: строка `\"0\"` становится `true`"]
В отличие от многих языков программирования (например PHP), `"0"` в JavaScript является `true`, как и строка из пробелов:
```js
//+ run
alert( !!"0" ); // true
alert( !!" " ); // любые непустые строки, даже из пробелов - true!
```
[/warn]
Логическое преобразование интересно тем, как оно сочетается с численным.
**Два значения могут быть равны, но одно из них в логическом контексте `true`, другое -- `false`**.
Например, равенства в следующем примере верны, так как происходит численное преобразование:
```js
//+ run
alert( 0 == "\n0\n" ); // true
alert( false == " " ); // true
```
...А в логическом контексте левая часть даст `false`, правая -- `true`:
```js
//+ run
if ("\n0\n") {
alert("true, совсем не как 0!");
}
```
С точки зрения преобразования типов в JavaScript это совершенно нормально. При равенстве -- численное преобразование, а в `if` -- логическое, только и всего.
## Итого
В JavaScript есть три преобразования:
<ol>
<li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой</li>
<li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`.</li>
<li>Логическое: `Boolean(value)` -- в логическом контексте, можно также сделать двойным НЕ: `!!value`.</li>
</ol>
**Сравнение не осуществляет преобразование типов в следующих случаях:**
<ul>
<li>При сравнении объектов. Две переменные, которые являются объектами равны только, когда ссылаются на один и тот же объект.</li>
<li>При сравнении двух строк. Там отдельный алгоритм сравнения. А вот если хоть один операнд -- не строка, то значения будут приведены: `true > "000"` станет `1 > 0`.</li>
<li>При проверке равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.</li>
</ul>

View file

@ -0,0 +1,26 @@
Ответ: `1`.
```js
//+ run
var i = 3;
while(i) {
alert(i--);
}
```
Каждое выполнение цикла уменьшает `i`. Проверка `while(i)` даст сигнал "стоп" при `i = 0`.
Соответственно, шаги цикла:
```js
var i = 3
alert(i--); // выведет 3, затем уменьшит i до 2
alert(i--) // выведет 2, затем уменьшит i до 1
alert(i--) // выведет 1, затем уменьшит i до 0
// все, проверка while(i) не даст выполняться циклу дальше
```

View file

@ -0,0 +1,14 @@
# Последнее значение цикла
[importance 3]
Какое последнее значение выведет этот код? Почему?
```js
var i = 3;
while(i) {
alert(i--);
}
```

View file

@ -0,0 +1,31 @@
<ol>
<li>**От 1 до 4**
```js
//+ run
var i = 0;
while (++i < 5) alert(i);
```
Первое значение: `i=1`, так как операция `++i` сначала увеличит `i`, а потом уже произойдёт сравнение и выполнение `alert`.
Далее `2,3,4..` Значения выводятся одно за другим. Для каждого значения сначала происходит увеличение, а потом -- сравнение, так как `++` стоит перед переменной.
При `i=4` произойдет увеличение `i` до `5`, а потом сравнение `while(5 < 5)` -- это неверно. Поэтому на этом цикл остановится, и значение `5` выведено не будет.
</li>
<li>**От 1 до 5**
```js
//+ run
var i = 0;
while (i++ < 5) alert(i);
```
Первое значение: `i=1`. Остановимся на нём подробнее. Оператор `i++` увеличивает `i`, возвращая старое значение, так что в сравнении `i++ < 5` будет участвовать старое `i=0`.
Но последующий вызов `alert` уже не относится к этому выражению, так что получит новый `i=1`.
Далее `2,3,4..` Для каждого значения сначала происходит сравнение, а потом -- увеличение, и затем срабатывание `alert`.
Окончание цикла: при `i=4` произойдет сравнение `while(4 < 5)` -- верно, после этого сработает `i++`, увеличив `i` до `5`, так что значение `5` будет выведено. Оно станет последним.</li>
</ol>

View file

@ -0,0 +1,23 @@
# Какие значения i выведет цикл while?
[importance 4]
Для каждого цикла запишите, какие значения он выведет. Потом сравните с ответом.
<ol>
<li>Префиксный вариант
```js
var i = 0;
while (++i < 5) alert(i);
```
</li>
<li>Постфиксный вариант
```js
var i = 0;
while (i++ < 5) alert(i);
```
</li>
</ol>

View file

@ -0,0 +1,17 @@
**Ответ: от 0 до 4 в обоих случаях.**
```js
//+ run
for(var i=0; i<5; ++i) alert(i);
for(var i=0; i<5; i++) alert(i);
```
Такой результат обусловлен алгоритмом работы `for`:
<ol>
<li>Выполнить присвоение `i=0`</li>
<li>Проверить `i<5`</li>
<li>Если верно - выполнить тело цикла `alert(i)`, затем выполнить `i++`</li>
</ol>
Увеличение `i++` выполняется отдельно от проверки условия (2), значение `i` при этом не используется, поэтому нет никакой разницы между `i++` и `++i`.

View file

@ -0,0 +1,21 @@
# Какие значения i выведет цикл for?
[importance 4]
Для каждого цикла запишите, какие значения он выведет. Потом сравните с ответом.
<ol>
<li>Постфиксная форма:
```js
for(var i=0; i<5; i++) alert(i);
```
</li>
<li>Префиксная форма:
```js
for(var i=0; i<5; ++i) alert(i);
```
</li>
</ol>

View file

@ -0,0 +1,12 @@
```js
//+ run demo
for (var i = 2; i <= 10; i++) {
if ( i % 2 == 0) {
alert(i);
}
}
```
Чётность проверяется по остатку при делении на `2`, используя оператор "деление с остатком" `%`: `i % 2`.

View file

@ -0,0 +1,7 @@
# Выведите чётные числа
[importance 5]
При помощи цикла `for` выведите чётные числа от `2` до `10`.
[demo /]

View file

@ -0,0 +1,11 @@
```js
//+ run
var i = 0;
while (i < 3) {
alert("номер " + i + "!");
i++;
}
```

View file

@ -0,0 +1,13 @@
# Замените for на while
[importance 5]
Перепишите код, заменив цикл `for` на `while`, без изменения поведения цикла.
```js
//+ run
for (var i = 0; i < 3; i++) {
alert("номер " + i + "!");
}
```

View file

@ -0,0 +1,18 @@
```js
//+ run demo
var num;
do {
num = prompt("Введите число больше 100?", 0);
} while(num <= 100 && num != null);
```
Цикл `do..while` повторяется, пока верны две проверки:
<ol>
<li>Проверка `num <= 100` -- то есть, введённое число всё еще меньше `100`.</li>
<li>Проверка `num != null` -- значение `null` означает, что посетитель нажал "Отмена", в этом случае цикл тоже нужно прекратить.</li>
</ol>
Кстати, сравнение `num <= 100` при вводе `null` даст `true`, так что вторая проверка необходима.

View file

@ -0,0 +1,11 @@
# Повторять цикл, пока ввод неверен
[importance 5]
Напишите цикл, который предлагает `prompt` ввести число, большее `100`. Если посетитель ввел другое число -- попросить ввести еще раз, и так далее.
Цикл должен спрашивать число пока либо посетитель не введет число, большее `100`, либо не нажмет кнопку Cancel (ESC).
Предполагается, что посетитель вводит только числа.
[demo /]

View file

@ -0,0 +1,179 @@
# Циклы while, for
При написании скриптов зачастую встает задача сделать однотипное действие много раз.
Например, вывести товары из списка один за другим. Или просто перебрать все числа от 1 до 10 и для каждого выполнить одинаковый код.
Для многократного повторения одного участка кода - предусмотрены *циклы*.
[cut]
## Цикл while
Цикл `while` имеет вид:
```js
while (условие) {
// код, тело цикла
}
```
Пока `условие` верно -- выполняется код из тела цикла.
Например, цикл ниже выводит `i` пока `i < 3`:
```js
//+ run
var i = 0;
while (i < 3) {
alert(i);
i++;
}
```
**Повторение цикла по-научному называется *"итерация"*. Цикл в примере выше совершает три итерации.**
Если бы `i++` в коде выше не было, то цикл выполнялся бы (в теории) вечно. На практике, браузер выведет сообщение о "зависшем" скрипте и посетитель его остановит.
**Бесконечный цикл** можно сделать и проще:
```js
while (true) {
// ...
}
```
**Условие в скобках интерпретируется как логическое значение, поэтому вместо `while (i!=0)` обычно пишут `while (i)`**:
```js
//+ run
var i = 3;
*!*
while (i) { // при i=0 значение в скобках будет false и цикл остановится
*/!*
alert(i);
i--;
}
```
## Цикл do..while
Проверку условия можно поставить *под* телом цикла, используя специальный синтаксис `do..while`:
```js
do {
// тело цикла
} while (условие);
```
Цикл, описанный, таким образом, сначала выполняет тело, а затем проверяет условие.
Например:
```js
//+ run
var i = 0;
do {
alert(i);
i++;
} while (i < 3);
```
Синтаксис `do..while` редко используется, т.к. обычный `while` нагляднее -- в нём не приходится искать глазами условие и ломать голову, почему оно проверяется именно в конце.
## Цикл for
Чаще всего применяется цикл `for`. Выглядит он так:
```js
for (начало; условие; шаг) {
// ... тело цикла ...
}
```
Пример цикла, который выполняет `alert(i)` для `i` от `0` до `2` включительно (до `3`):
```js
//+ run
var i;
for (i=0; i<3; i++) {
alert(i);
}
```
Здесь:
<ul>
<li>**Начало:** `i=0`.</li>
<li>**Условие:** `i<3`.</li>
<li>**Шаг:** `i++`.</li>
<li>**Тело:** `alert(i)`, т.е. код внутри фигурных скобок (они не обязательны, если только одна операция)</li>
</ul>
Цикл выполняется так:
<ol>
<li>Начало: `i=0` выполняется один-единственный раз, при заходе в цикл.</li>
<li>Условие: `i<3` проверяется перед каждой итерацией и при входе в цикл, если оно нарушено, то происходит выход.</li>
<li>Тело: `alert(i)`.</li>
<li>Шаг: `i++` выполняется после *тела* на каждой итерации, но перед проверкой условия.</li>
<li>Идти на шаг 2.</li>
</ol>
Иными, словами, поток выполнения: `начало` -> (если `условие` -> `тело` -> `шаг`) -> (если `условие` -> `тело` -> `шаг`) -> ... и так далее, пока верно `условие`.
[smart]
В цикле также можно определить переменную:
```js
//+ run
for (*!*var*/!* i=0; i<3; i++) {
alert(i); // 0, 1, 2
}
```
Эта переменная будет видна и за границами цикла, в частности, после окончания цикла `i` станет равно `3`.
[/smart]
## Пропуск частей for
Любая часть `for` может быть пропущена.
Например, можно убрать `начало`. Цикл в примере ниже полностью идентичен приведённому выше:
```js
//+ run
var i = 0;
for (; i<3; i++) {
alert(i); // 0, 1, 2
}
```
Можно убрать и `шаг`:
```js
//+ run
var i = 0;
for (; i<3;) {
alert(i);
// цикл превратился в аналог while (i<3)
}
```
А можно и вообще убрать все, получив бесконечный цикл:
```js
for (;;) {
// будет выполняться вечно
}
```
При этом сами точки с запятой `;` обязательно должны присутствовать, иначе будет ошибка синтаксиса.
[smart header="`for..in`"]
Существует также специальная конструкция `for..in` для перебора свойств объекта.
Мы познакомимся с ней позже, когда будем [говорить об объектах](#for..in).
[/smart]

View file

@ -0,0 +1,28 @@
# Схема решения
```js
Для всех i от 1 до 10 {
проверить, делится ли число i на какое-либо из чисел до него
если делится, то это i не подходит, берем следующее
если не делится, то i - простое число
}
```
# Решение
Решение с использованием метки:
```js
//+ run
nextPrime:
for(var i=2; i<10; i++) {
for(var j=2; j<i; j++) {
if ( i % j == 0) continue nextPrime;
}
alert(i); // простое
}
```
Конечно же, его можно оптимизировать с точки зрения производительности. Например, проверять все `j` не от `2` до `i`, а от `2` до квадратного корня из `i`. А для очень больших чисел -- существуют более эффективные специализированные алгоритмы проверки простоты числа, например [квадратичное решето](http://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE_%D1%80%D0%B5%D1%88%D0%B5%D1%82%D0%B0) и [решето числового поля](http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%89%D0%B8%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D1%88%D0%B5%D1%82%D0%B0_%D1%87%D0%B8%D1%81%D0%BB%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%BF%D0%BE%D0%BB%D1%8F).

View file

@ -0,0 +1,12 @@
# Вывести простые числа
[importance 4]
Натуральное число, большее 1, называется *простым*, если оно ни на что не делится, кроме себя и `1`.
Другими словами, <code>n&gt;1</code> - простое, если при делении на любое число от `2` до `n-1` есть остаток.
**Создайте код, который выводит все простые числа из интервала от `2` до `10`.** Результат должен быть: `2,3,5,7`.
P.S. Код также должен легко модифицироваться для любых других интервалов.

View file

@ -0,0 +1,198 @@
# Директивы break и continue
Для более гибкого управления циклом используются директивы `break` и `continue`.
[cut]
## Выход: break
Выйти из цикла можно не только при проверке условия но и, вообще, в любой момент. Эту возможность обеспечивает директива `break`.
Например, бесконечный цикл в примере прекратит выполнение при `i==5`:
```js
var i=0;
while(1) {
i++;
*!*if (i==5) break;*/!*
alert(i);
}
alert('Последняя i = '+ i ); // 5 (*)
```
Выполнение продолжится со строки `(*)`, следующей за циклом.
## Следующая итерация: continue [#continue]
Директива `continue` прекращает выполнение *текущей итерации* цикла. Например, цикл ниже не выводит четные значения:
```js
//+ run
for (var i = 0; i < 10; i++) {
*!*if (i % 2 == 0) continue;*/!*
alert(i);
}
```
Для четных `i` срабатывает `continue`, выполнение блока прекращается и управление передается на `for`.
[smart header="Совет по стилю"]
Как правило, `continue` и используют, чтобы не обрабатывать определенные значения в цикле.
Цикл, который обрабатывает только часть значений, мог бы выглядеть так:
```js
for (var i = 0; i < 10; i++) {
if ( checkValue(i) ) {
// функция checkValue проверяет, подходит ли i
// ...
// ... обработка
// ... этого
// ... значения
// ... цикла
// ...
}
}
```
Все хорошо, но мы получили *дополнительный уровень вложенности фигурных скобок, без которого можно и нужно обойтись*.
Гораздо лучше здесь использовать `continue`:
```js
for (var i = 0; i < 10; i++) {
*!*if ( !checkValue(i) ) continue;*/!*
// здесь мы точно знаем, что i подходит
// ...
// ... обработка
// ... этого
// ... значения
// ... цикла
// ...
}
```
[/smart]
[warn header="Нельзя использовать break/continue справа от оператора '?'"]
Обычно мы можем заменить `if` на оператор вопросительный знак `'?'`.
То есть, запись:
```js
if (условие) {
a();
} else {
b();
}
```
..Аналогична записи:
```js
условие ? a() : b();
```
В обоих случаях в зависимости от условия выполняется либо `a()` либо `b()`.
Но разница состоит в том, что оператор вопросительный знак `'?'`, использованный во второй записи, возвращает значение.
**Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе `'?'`.** К таким относятся большинство конструкций и, в частности, `break/continue`.
Поэтому такой код приведёт к ошибке:
```js
(i > 5) ? alert(i) : *!*continue*/!*;
```
[/warn]
## Метки
Бывает нужно выйти одновременно из нескольких уровней цикла.
Представим, что нужно ввести значения точек. У каждой точки есть две координаты `(i, j)`. Цикл для ввода значений `i,j = 0..2` может выглядеть так:
```js
//+ run
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
var input = prompt("Значение в координатах " + i + "," + j, "");
if (input == null) *!*break*/!*; // (*)
}
}
alert('Готово!');
```
Здесь `break` используется, чтобы прервать ввод, если посетитель нажал на `Отмена`. Но обычный вызов `break` в строке `(*)` не может прервать два цикла сразу. Как же прервать ввод полностью? Один из способов -- поставить *метку*.
Метка имеет вид `"имя:"`, имя должно быть уникальным. Она ставится перед циклом, вот так:
```js
outer: for (var i = 0; i < 3; i++) { ... }
```
Можно также выносить ее на отдельную строку. Вызов `break outer` прерывает управление цикла с такой меткой, вот так:
```js
//+ run
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
var input = prompt('Значение в координатах '+i+','+j, '');
if (input == null) *!*break outer*/!*; // (*)
}
}
alert('Готово!');
```
Директива `continue` также может быть использована с меткой. Управление перепрыгнет на следующую итерацию цикла с меткой.
**Метки можно ставить в том числе на блок, без цикла:**
```js
//+ run
my: {
for (;;) {
for (i=0; i<10; i++) {
if (i>4) break my;
}
}
some_code; // произвольный участок кода
}
alert("После my"); // (*)
```
В примере выше, `break` перепрыгнет через `some_code`, выполнение продолжится сразу после блока `my`, со строки `(*)`. Возможность ставить метку на блоке используется редко. Обычно метки ставятся перед циклом.
[smart header="Goto?"]
В некоторых языках программирования есть оператор `goto`, который может передавать управление на любой участок программы.
Операторы `break/continue` более ограниченны. Они работают только внутри циклов, и метка должна быть не где угодно, а выше по уровню вложенности.
В JavaScript нет `goto`.
[/smart]

View file

@ -0,0 +1,20 @@
Если совсем точно следовать условию, то сравнение должно быть строгим `'==='`.
В реальном случае, скорее всего, подойдёт обычное сравнение `'=='`.
```js
if(browser == 'IE') {
alert('О, да у вас IE!');
} else if (browser == 'Chrome'
|| browser == 'Firefox'
|| browser == 'Safari'
|| browser == 'Opera') {
alert('Да, и эти браузеры мы поддерживаем');
} else {
alert('Мы надеемся, что и в вашем браузере все ок!');
}
```
Обратите внимание: конструкция `browser == 'Chrome' || browser == 'Firefox' ...` разбита на несколько строк для лучшей читаемости.
Но всё равно запись через `switch` нагляднее.

View file

@ -0,0 +1,24 @@
# Напишите "if", аналогичный "switch"
[importance 5]
Напишите `if..else`, соответствующий следующему `switch`:
```js
switch(browser) {
case 'IE':
alert('О, да у вас IE!');
break;
case 'Chrome':
case 'Firefox':
case 'Safari':
case 'Opera':
alert('Да, и эти браузеры мы поддерживаем');
break;
default:
alert('Мы надеемся, что и в вашем браузере все ок!');
}
```

View file

@ -0,0 +1,27 @@
Первые две проверки -- обычный `case`, третья разделена на два `case`:
```js
//+ run
var a = +prompt('a?', '');
switch(a) {
case 0:
alert(0);
break;
case 1:
alert(1);
break;
case 2:
case 3:
alert('2,3');
*!*
break;
*/!*
}
```
Обратите внимание: `break` внизу не обязателен, но ставится по "правилам хорошего тона".
Допустим, он не стоит. Есть шанс, что в будущем нам понадобится добавить в конец ещё один `case`, например `case 4`, и мы, вполне вероятно, забудем этот `break` поставить. В результате выполнение `case 2`/`case 3` продолжится на `case 4` и будет ошибка.

View file

@ -0,0 +1,22 @@
# Переписать if'ы в switch
[importance 4]
Перепишите код с использованием одной конструкции `switch`:
```js
//+ run
var a = +prompt('a?', '');
if (a == 0) {
alert(0);
}
if (a == 1) {
alert(1);
}
if (a == 2 || a == 3) {
alert('2,3');
}
```

View file

@ -0,0 +1,185 @@
# Конструкция switch
Конструкция `switch` заменяет собой сразу несколько `if`.
Это -- более наглядный способ сравнить выражение сразу с несколькими вариантами.
[cut]
## Синтаксис
Выглядит она так:
```js
switch(x) {
case 'value1': // if (x === 'value1')
...
[break]
case 'value2': // if (x === 'value2')
...
[break]
default:
...
[break]
}
```
<ul>
<li>
Переменная `x` проверяется на строгое равенство первому значению `value1`, затем второму `value2` и так далее.
</li>
<li>
Если соответствие установлено -- switch начинает выполняться от соответствующей директивы `case` и далее, *до ближайшего `break`* (или до конца `switch`).
</li>
<li>
Если ни один `case` не совпал -- выполняетcя (если есть) вариант `default`.
</li>
</ul>
При этом `case` называют *вариантами `switch`*.
## Пример работы
Пример использования `switch` (сработавший код выделен):
```js
//+ run
var a = 2+2;
switch (a) {
case 3:
alert('Маловато');
break;
*!*
case 4:
alert('В точку!');
break;
*/!*
case 5:
alert('Перебор');
break;
default:
alert('Я таких значений не знаю');
}
```
Будет выведено только одно значение, соответствующее `4`. После чего `break` прервёт выполнение.
**Если его не прервать -- оно пойдёт далее, при этом остальные проверки игнорируются.**
Например:
```js
//+ run
var a = 2+2;
switch (a) {
case 3:
alert('Маловато');
*!*
case 4:
alert('В точку!');
case 5:
alert('Перебор');
default:
alert('Я таких значений не знаю');
*/!*
}
```
В примере выше последовательно выполнятся три `alert`.
```js
alert('В точку!');
alert('Перебор');
alert('Я таких значений не знаю');
```
**В `case` могут быть любые выражения**, в том числе включающие в себя переменные и функции.
Например:
```js
//+ run
var a = 1;
var b = 0;
switch(a) {
*!*
case b+1:
alert(1);
break;
*/!*
default:
alert('нет-нет, выполнится вариант выше')
}
```
## Группировка case
Несколько значений case можно группировать.
В примере ниже `case 3` и `case 5` выполняют один и тот же код:
```js
//+ run
var a = 2+2;
switch (a) {
case 4:
alert('Верно!');
break;
*!*
case 3: // (*)
case 5: // (**)
alert('Неверно!');
alert('Немного ошиблись, бывает.');
break;
*/!*
default:
alert('Странный результат, очень странный');
}
```
При `case 3` выполнение идёт со строки `(*)`, при `case 5` -- со строки `(**)`.
## Тип имеет значение
Следующий пример принимает значение от посетителя.
```js
//+ run
var arg = prompt("Введите arg?")
switch(arg) {
case '0':
case '1':
alert('Один или ноль');
case '2':
alert('Два');
break;
case 3:
alert('Никогда не выполнится');
case null:
alert('Отмена');
break;
default:
alert('Неизвестное значение: ' + arg)
}
```
Что оно выведет при вводе чисел 0, 1, 2, 3? Подумайте и *потом* читайте дальше...
<ul>
<li>При вводе `0` или `1` выполнится первый `alert`, далее выполнение продолжится вниз до первого `break` и выведет второй `alert('Два')`.</li>
<li>При вводе `2`, `switch` перейдет к `case '2'` и выведет `Два`.</li>
<li>**При вводе `3`, `switch` перейдет на `default`.** Это потому, что `prompt` возвращает строку `'3'`, а не число. Типы разные. `Switch` использует строгое равенство `===`, так что совпадения не будет.</li>
<li>При отмене сработает `case null`.</li>
</ul>

View file

@ -0,0 +1 @@
Оба варианта функции работают одинаково, отличий нет.

Some files were not shown because too many files have changed in this diff Show more