update
143
1-js/1-getting-started/1-intro/article.md
Normal 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>Посылать запросы на сервер и загружать данные без перезагрузки страницы(эта технология называется "AJAX").</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 в этом отношении несравнимо лучше.
|
||||
|
BIN
1-js/1-getting-started/1-intro/jslimit.jpg
Executable file
After Width: | Height: | Size: 47 KiB |
BIN
1-js/1-getting-started/2-alternatives/allow.png
Executable file
After Width: | Height: | Size: 20 KiB |
101
1-js/1-getting-started/2-alternatives/article.md
Normal 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.
|
111
1-js/1-getting-started/3-pre-coding/article.md
Normal 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="Почему не просто "JavaScript" ?"]
|
||||
Вы можете спросить: "Почему спецификация для JavaScript не называется просто *"JavaScript"*, зачем существует какое-то отдельное название?"
|
||||
|
||||
Всё потому, что JavaScript™ -- зарегистрированная торговая марка, принадлежащая корпорации 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>
|
70
1-js/1-getting-started/4-editor/article.md
Normal 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>
|
||||
|
||||
Если не знаете, что выбрать -- можно посмотреть на них ;)
|
||||
|
||||
## Не будем ссориться
|
||||
|
||||
В списках выше перечислены редакторы, которые использую я или мои знакомые -- хорошие разработчики. Конечно, существуют и другие отличные редакторы, если вам что-то нравится -- пользуйтесь.
|
||||
|
||||
Выбор редактора, как и любого инструмента, во многом индивидуален и зависит от ваших проектов, привычек, личных предпочтений.
|
112
1-js/1-getting-started/5-devtools/article.md
Normal 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.
|
BIN
1-js/1-getting-started/5-devtools/chrome.png
Executable file
After Width: | Height: | Size: 30 KiB |
BIN
1-js/1-getting-started/5-devtools/chrome@2x.png
Executable file
After Width: | Height: | Size: 56 KiB |
BIN
1-js/1-getting-started/5-devtools/firebug-gray.png
Executable file
After Width: | Height: | Size: 23 KiB |
BIN
1-js/1-getting-started/5-devtools/firefox.png
Executable file
After Width: | Height: | Size: 47 KiB |
BIN
1-js/1-getting-started/5-devtools/firefox@2x.png
Executable file
After Width: | Height: | Size: 82 KiB |
BIN
1-js/1-getting-started/5-devtools/firefox_console_down.png
Executable file
After Width: | Height: | Size: 468 B |
BIN
1-js/1-getting-started/5-devtools/firefox_console_down@2x.png
Executable file
After Width: | Height: | Size: 3.7 KiB |
BIN
1-js/1-getting-started/5-devtools/firefox_console_enable.png
Executable file
After Width: | Height: | Size: 53 KiB |
BIN
1-js/1-getting-started/5-devtools/firefox_console_enable@2x.png
Executable file
After Width: | Height: | Size: 83 KiB |
BIN
1-js/1-getting-started/5-devtools/safari.png
Executable file
After Width: | Height: | Size: 69 KiB |
3
1-js/1-getting-started/index.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Введение
|
||||
|
||||
Про язык JavaScript и окружение для разработки на нём.
|
19
1-js/2-first-steps/1-hello-world/1-hello-alert/solution.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
Код страницы:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
alert('Я - JavaScript!');
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
alert('Я - JavaScript!');
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
9
1-js/2-first-steps/1-hello-world/1-hello-alert/task.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Выведите alert
|
||||
|
||||
[importance 5]
|
||||
|
||||
Сделайте страницу, которая выводит "Я - JavaScript!".
|
||||
|
||||
Создайте ее на диске, откройте в браузере, убедитесь, что все работает.
|
||||
|
||||
[demo src="solution"]
|
1
1-js/2-first-steps/1-hello-world/2-hello-alert-ext/alert.js
Executable file
|
@ -0,0 +1 @@
|
|||
alert('Я - JavaScript!');
|
11
1-js/2-first-steps/1-hello-world/2-hello-alert-ext/index.html
Executable file
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="alert.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
Код для HTML-файла:
|
||||
|
||||
```html
|
||||
<!--+ src="index.html" -->
|
||||
```
|
||||
|
||||
Для файла `alert.js` из той же директории:
|
||||
|
||||
```js
|
||||
//+ src="alert.js"
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Вывести alert внешним скриптом
|
||||
|
||||
[importance 5]
|
||||
|
||||
Возьмите решение предыдущей задачи [](/task/hello-alert) и вынесите скрипт во внешний файл `alert.js`, который расположите в той же директории.
|
||||
|
||||
Откройте страницу и проверьте, что вывод сообщения всё ещё работает.
|
|
@ -0,0 +1,6 @@
|
|||
Ответы:
|
||||
<ol>
|
||||
<li>Первым выполнится `big.js`, это нормальная последовательность выполнения подряд идущих скриптов.</li>
|
||||
<li>Первым выполнится `small.js`, так как скрипты из-за `async` ведут себя совершенно независимо друг от друга, страница тоже от них не зависит.</li>
|
||||
<li>Первым выполнится `big.js`, так как скрипты, подключённые через `defer`, сохраняют порядок выполнения относительно друг друга.</li>
|
||||
</ol>
|
27
1-js/2-first-steps/1-hello-world/3-async-defer-first/task.md
Normal 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>
|
||||
```
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
**Первым выполнится обычный скрипт.**
|
||||
|
||||
Заметим, что атрибуты `defer` и `async` на обычном скрипте будут проигнорированы. То есть, он работает так же, как и без них:
|
||||
|
||||
```html
|
||||
<script>
|
||||
alert("обычный скрипт");
|
||||
</script>
|
||||
```
|
||||
|
||||
Далее, обычно скрипты с `async/defer` не тормозят обработку страницы. То есть, браузер начнёт их загружать, а сам пойдёт дальше показывать страницу и выполнять скрипты.
|
||||
|
||||
То есть, обычный скрипт в этом случае, очевидно, выполнится первым.
|
||||
|
||||
...Но более того, даже если скрипты `small.js` и `big.js` не нужно загружать, а браузер берёт их из кеша, то он всё равно устроен так, что выполнит их после основных скриптов страницы.
|
||||
|
||||
Таким образом, первым всегда будет обычный скрипт, а вот относительный порядок `small.js` и `big.js` здесь не регламентирован.
|
|
@ -0,0 +1,17 @@
|
|||
# Что выполнится первым из скриптов?
|
||||
|
||||
[importance 4]
|
||||
|
||||
В этой странице есть три скрипта.
|
||||
|
||||
Какой выполнится первым?
|
||||
|
||||
```html
|
||||
<script defer src="small.js"></script>
|
||||
<script async src="big.js"></script>
|
||||
|
||||
<script defer async>
|
||||
alert("обычный скрипт");
|
||||
</script>
|
||||
```
|
||||
|
315
1-js/2-first-steps/1-hello-world/article.md
Normal 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><script> ... </script></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><script <u>type</u>=...></code></dt>
|
||||
|
||||
<dd>В отличие от HTML5, стандарт HTML 4 требовал обязательного указания этого атрибута. Выглядел он так: `type="text/javascript"`. Если указать другое значение `type`, то скрипт выполнен не будет.
|
||||
|
||||
В современной разработке атрибут `type` не обязателен.
|
||||
</dd>
|
||||
|
||||
<dt>Атрибут <code><script <u>language</u>=...></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><script>...</script></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 и Яндекс используют такой подход по умолчанию.
|
||||
|
||||
## Задачи
|
||||
|
||||
Очень важно не только читать учебник, но делать что-то самостоятельно.
|
||||
|
||||
Решите задачки, чтобы удостовериться, что вы все правильно поняли.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<ol>
|
||||
<li>Операция `a^b` ставит бит результата в `1`, если на соответствующей битовой позиции в `a` или `b` (но не одновременно) стоит `1`.
|
||||
|
||||
Так как в `0` везде стоят нули, то биты берутся в точности как во втором аргументе.</li>
|
||||
<li>Первое побитовое НЕ `~` превращает `0` в `1`, а `1` в `0`. А второе НЕ превращает ещё раз, в итоге получается как было.</li>
|
||||
</ol>
|
|
@ -0,0 +1,13 @@
|
|||
# Побитовый оператор и значение
|
||||
|
||||
[importance 5]
|
||||
|
||||
Почему побитовые операции в примерах ниже не меняют число? Что они делают внутри?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 123 ^ 0 ); // 123
|
||||
alert( 0 ^ 123 ); // 123
|
||||
alert( ~~123 ); // 123
|
||||
```
|
||||
|
|
@ -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)`, а это уже совсем другое дело.
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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`.
|
||||
|
||||
Аналогично можно увидеть, что и другие операторы симметричны.
|
||||
|
||||
Ответ: **да**.
|
||||
|
|
@ -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>
|
||||
|
||||
Иными словами, при перемене мест -- всегда ли результат остаётся тем же?
|
|
@ -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 теперь стоит в начале числа и является знаковым
|
||||
```
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Почему результат разный?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Почему результат второго `alert'а` такой странный?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 123456789 ^ 0 ); // 123456789
|
||||
alert( 12345678912345 ^ 0 ); // 1942903641
|
||||
```
|
||||
|
752
1-js/2-first-steps/10-bitwise-operators/article.md
Normal 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 & 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]
|
27
1-js/2-first-steps/11-uibasic/1-simple-page/solution.md
Normal 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>
|
||||
```
|
||||
|
8
1-js/2-first-steps/11-uibasic/1-simple-page/task.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Простая страница
|
||||
|
||||
[importance 4]
|
||||
|
||||
Создайте страницу, которая спрашивает имя и выводит его.
|
||||
|
||||
[demo /]
|
||||
|
110
1-js/2-first-steps/11-uibasic/article.md
Normal 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>
|
13
1-js/2-first-steps/12-ifelse/1-if-zero-string/solution.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Да, выведется,** т.к. внутри `if` стоит строка `"0"`.
|
||||
|
||||
Любая строка, кроме пустой (а здесь она не пустая), в логическом контексте является `true`.
|
||||
|
||||
Можно запустить и проверить:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
if ("0") {
|
||||
alert('Привет');
|
||||
}
|
||||
```
|
||||
|
12
1-js/2-first-steps/12-ifelse/1-if-zero-string/task.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# if (строка с нулём)
|
||||
|
||||
[importance 5]
|
||||
|
||||
Выведется ли `alert`?
|
||||
|
||||
```js
|
||||
if ("0") {
|
||||
alert('Привет');
|
||||
}
|
||||
```
|
||||
|
BIN
1-js/2-first-steps/12-ifelse/2-check-standard/ifelse_task2.png
Executable file
After Width: | Height: | Size: 21 KiB |
18
1-js/2-first-steps/12-ifelse/2-check-standard/ifelse_task2/index.html
Executable file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var value = prompt('Каково "официальное" название JavaScript?', '');
|
||||
|
||||
if ( value == 'EcmaScript' ) {
|
||||
alert('Верно!');
|
||||
} else {
|
||||
alert('Не знаете? "EcmaScript"!');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
```html
|
||||
<!--+ run src="ifelse_task2/index.html" -->
|
||||
```
|
||||
|
13
1-js/2-first-steps/12-ifelse/2-check-standard/task.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Проверка стандарта
|
||||
|
||||
[importance 2]
|
||||
|
||||
Используя конструкцию `if..else`, напишите код, который будет спрашивать: "Каково "официальное" название JavaScript?".
|
||||
|
||||
Если посетитель вводит "EcmaScript", то выводить "Верно!", если что-то другое -- выводить "Не знаете? "EcmaScript"!".
|
||||
|
||||
Блок-схема:
|
||||
|
||||
<img src="ifelse_task2.png">
|
||||
|
||||
[demo src="ifelse_task2"]
|
18
1-js/2-first-steps/12-ifelse/2-check-standardifelse_task2/index.html
Executable file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var value = prompt('Каково "официальное" название JavaScript?', '');
|
||||
|
||||
if ( value == 'EcmaScript' ) {
|
||||
alert('Верно!');
|
||||
} else {
|
||||
alert('Не знаете? "EcmaScript"!');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
23
1-js/2-first-steps/12-ifelse/3-sign/if_sign/index.html
Executable 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>
|
15
1-js/2-first-steps/12-ifelse/3-sign/solution.md
Normal 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);
|
||||
}
|
||||
```
|
||||
|
12
1-js/2-first-steps/12-ifelse/3-sign/task.md
Normal 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"]
|
BIN
1-js/2-first-steps/12-ifelse/4-check-login/ifelse_task.png
Executable file
After Width: | Height: | Size: 32 KiB |
33
1-js/2-first-steps/12-ifelse/4-check-login/solution.md
Normal 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`. Они не обязательны, но полезны для лучшей читаемости кода.
|
17
1-js/2-first-steps/12-ifelse/4-check-login/task.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Проверка логина
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите код, который будет спрашивать логин (`prompt`).
|
||||
|
||||
Если посетитель вводит "Админ", то спрашивать пароль, если нажал отмена (escape) -- выводить "Вход отменён", если вводит что-то другое -- "Я вас не знаю".
|
||||
|
||||
Пароль проверять так. Если введён пароль "Чёрный Властелин", то выводить "Добро пожаловать!", иначе -- "Пароль неверен", при отмене -- "Вход отменён".
|
||||
|
||||
Блок-схема:
|
||||
|
||||
<img src="ifelse_task.png">
|
||||
|
||||
Для решения используйте вложенные блоки `if`. Обращайте внимание на стиль и читаемость кода.
|
||||
|
||||
[demo /]
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
```js
|
||||
result = (a + b < 4) ? 'Мало' : 'Много';
|
||||
```
|
||||
|
14
1-js/2-first-steps/12-ifelse/5-rewrite-if-question/task.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Перепишите 'if' в '?'
|
||||
|
||||
[importance 5]
|
||||
|
||||
Перепишите `if` с использованием оператора `'?'`:
|
||||
|
||||
```js
|
||||
if (a + b < 4) {
|
||||
result = 'Мало';
|
||||
} else {
|
||||
result = 'Много';
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
```js
|
||||
var message = (login == 'Вася') ? 'Привет' :
|
||||
(login == 'Директор') ? 'Здравствуйте' :
|
||||
(login == '') ? 'Нет логина' :
|
||||
'';
|
||||
```
|
||||
|
|
@ -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 = '';
|
||||
}
|
||||
```
|
||||
|
227
1-js/2-first-steps/12-ifelse/article.md
Normal 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('Неправильно');
|
||||
}
|
||||
*/!*
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
Ответ: `2`, это первое значение, которое в логическом контексте даст `true`.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( null || 2 || undefined );
|
||||
```
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Что выведет alert (ИЛИ)?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Что выведет код ниже?
|
||||
|
||||
```js
|
||||
alert( null || 2 || undefined );
|
||||
```
|
||||
|
15
1-js/2-first-steps/13-logical-ops/2-alert-or/solution.md
Normal 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` выведено не будет.
|
10
1-js/2-first-steps/13-logical-ops/2-alert-or/task.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Что выведет alert (ИЛИ)?
|
||||
|
||||
[importance 3]
|
||||
|
||||
Что выведет код ниже?
|
||||
|
||||
```js
|
||||
alert( alert(1) || 2 || alert(3) );
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
Ответ: `null`, это первое ложное значение из списка.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 1 && null && 2 );
|
||||
```
|
||||
|
10
1-js/2-first-steps/13-logical-ops/3-alert-1-null-2/task.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Что выведет alert (И)?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Что выведет код ниже?
|
||||
|
||||
```js
|
||||
alert( 1 && null && 2 );
|
||||
```
|
||||
|
10
1-js/2-first-steps/13-logical-ops/4-alert-and/solution.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
Ответ: `1`, а затем `undefined`.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( alert(1) && alert(2) );
|
||||
```
|
||||
|
||||
Вызов `alert` не возвращает значения, или, иначе говоря, возвращает `undefined`.
|
||||
|
||||
Поэтому до правого `alert` дело не дойдёт, вычисления закончатся на левом.
|
10
1-js/2-first-steps/13-logical-ops/4-alert-and/task.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Что выведет alert (И)?
|
||||
|
||||
[importance 3]
|
||||
|
||||
Что выведет код ниже?
|
||||
|
||||
```js
|
||||
alert( alert(1) && alert(2) );
|
||||
```
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
```js
|
||||
if (age >= 14 && age <= 90)
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Проверка if внутри диапазона
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите условие `if` для проверки того факта, что переменная `age` находится между `14` и `90` включительно.
|
||||
|
||||
"Включительно" означает, что концы промежутка включены, то есть `age` может быть равна `14` или `90`.
|
|
@ -0,0 +1,12 @@
|
|||
Первый вариант:
|
||||
|
||||
```js
|
||||
if ( !(age >= 14 && age <= 90) )
|
||||
```
|
||||
|
||||
Второй вариант:
|
||||
|
||||
```js
|
||||
if (age < 14 || age > 90)
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Проверка if вне диапазона
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите условие `if` для проверки того факта, что `age` НЕ находится между 14 и 90 включительно.
|
||||
|
||||
Сделайте два варианта условия: первый с использованием оператора НЕ `!`, второй - без этого оператора.
|
21
1-js/2-first-steps/13-logical-ops/7-if-question/solution.md
Normal 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('третье');
|
||||
```
|
||||
|
14
1-js/2-first-steps/13-logical-ops/7-if-question/task.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Вопрос про "if"
|
||||
|
||||
[importance 5]
|
||||
|
||||
Какие из этих `if` верны, т.е. выполнятся?
|
||||
|
||||
Какие конкретно значения будут результатами выражений в условиях `if(...)`?
|
||||
|
||||
```js
|
||||
if (-1 || 0) alert('первое');
|
||||
if (-1 && 0) alert('второе');
|
||||
if (null || -1 && 1) alert('третье');
|
||||
```
|
||||
|
249
1-js/2-first-steps/13-logical-ops/article.md
Normal 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
|
||||
```
|
||||
|
|
@ -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>
|
|
@ -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
|
||||
```
|
||||
|
210
1-js/2-first-steps/14-types-conversion/article.md
Normal file
|
@ -0,0 +1,210 @@
|
|||
# Преобразование типов для примитивов
|
||||
|
||||
Система преобразования типов в JavaScript очень проста, но отличается от других языков. Поэтому она часто служит "камнем преткновения" для приходящих из других языков программистов.
|
||||
[cut]
|
||||
Всего есть три преобразования:
|
||||
<ol>
|
||||
<li>Cтроковое преобразование.</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>
|
||||
|
|
@ -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) не даст выполняться циклу дальше
|
||||
```
|
||||
|
14
1-js/2-first-steps/15-while-for/1-loop-last-value/task.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Последнее значение цикла
|
||||
|
||||
[importance 3]
|
||||
|
||||
Какое последнее значение выведет этот код? Почему?
|
||||
|
||||
```js
|
||||
var i = 3;
|
||||
|
||||
while(i) {
|
||||
alert(i--);
|
||||
}
|
||||
```
|
||||
|
|
@ -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>
|
23
1-js/2-first-steps/15-while-for/2-which-value-while/task.md
Normal 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>
|
|
@ -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`.
|
21
1-js/2-first-steps/15-while-for/3-which-value-for/task.md
Normal 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>
|
12
1-js/2-first-steps/15-while-for/4-for-even/solution.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
|
||||
```js
|
||||
//+ run demo
|
||||
for (var i = 2; i <= 10; i++) {
|
||||
if ( i % 2 == 0) {
|
||||
alert(i);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Чётность проверяется по остатку при делении на `2`, используя оператор "деление с остатком" `%`: `i % 2`.
|
7
1-js/2-first-steps/15-while-for/4-for-even/task.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Выведите чётные числа
|
||||
|
||||
[importance 5]
|
||||
|
||||
При помощи цикла `for` выведите чётные числа от `2` до `10`.
|
||||
|
||||
[demo /]
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
while (i < 3) {
|
||||
alert("номер " + i + "!");
|
||||
i++;
|
||||
}
|
||||
```
|
||||
|
13
1-js/2-first-steps/15-while-for/5-replace-for-while/task.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Замените for на while
|
||||
|
||||
[importance 5]
|
||||
|
||||
Перепишите код, заменив цикл `for` на `while`, без изменения поведения цикла.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
for (var i = 0; i < 3; i++) {
|
||||
alert("номер " + i + "!");
|
||||
}
|
||||
```
|
||||
|
|
@ -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`, так что вторая проверка необходима.
|
|
@ -0,0 +1,11 @@
|
|||
# Повторять цикл, пока ввод неверен
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите цикл, который предлагает `prompt` ввести число, большее `100`. Если посетитель ввел другое число -- попросить ввести еще раз, и так далее.
|
||||
|
||||
Цикл должен спрашивать число пока либо посетитель не введет число, большее `100`, либо не нажмет кнопку Cancel (ESC).
|
||||
|
||||
Предполагается, что посетитель вводит только числа.
|
||||
|
||||
[demo /]
|
179
1-js/2-first-steps/15-while-for/article.md
Normal 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]
|
||||
|
|
@ -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).
|
12
1-js/2-first-steps/16-break-continue/1-list-primes/task.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Вывести простые числа
|
||||
|
||||
[importance 4]
|
||||
|
||||
Натуральное число, большее 1, называется *простым*, если оно ни на что не делится, кроме себя и `1`.
|
||||
|
||||
Другими словами, <code>n>1</code> - простое, если при делении на любое число от `2` до `n-1` есть остаток.
|
||||
|
||||
**Создайте код, который выводит все простые числа из интервала от `2` до `10`.** Результат должен быть: `2,3,5,7`.
|
||||
|
||||
P.S. Код также должен легко модифицироваться для любых других интервалов.
|
||||
|
198
1-js/2-first-steps/16-break-continue/article.md
Normal 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]
|
||||
|
|
@ -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` нагляднее.
|
|
@ -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('Мы надеемся, что и в вашем браузере все ок!');
|
||||
}
|
||||
```
|
||||
|
27
1-js/2-first-steps/17-switch/2-rewrite-if-switch/solution.md
Normal 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` и будет ошибка.
|
22
1-js/2-first-steps/17-switch/2-rewrite-if-switch/task.md
Normal 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');
|
||||
}
|
||||
```
|
||||
|
185
1-js/2-first-steps/17-switch/article.md
Normal 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>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Оба варианта функции работают одинаково, отличий нет.
|