init
143
01-js/01-getting-started/01-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
01-js/01-getting-started/01-intro/jslimit.jpg
Executable file
After Width: | Height: | Size: 47 KiB |
BIN
01-js/01-getting-started/02-alternatives/allow.png
Executable file
After Width: | Height: | Size: 20 KiB |
101
01-js/01-getting-started/02-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
01-js/01-getting-started/03-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
01-js/01-getting-started/04-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
01-js/01-getting-started/05-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
01-js/01-getting-started/05-devtools/chrome.png
Executable file
After Width: | Height: | Size: 30 KiB |
BIN
01-js/01-getting-started/05-devtools/chrome@2x.png
Executable file
After Width: | Height: | Size: 56 KiB |
BIN
01-js/01-getting-started/05-devtools/firebug-gray.png
Executable file
After Width: | Height: | Size: 23 KiB |
BIN
01-js/01-getting-started/05-devtools/firefox.png
Executable file
After Width: | Height: | Size: 47 KiB |
BIN
01-js/01-getting-started/05-devtools/firefox@2x.png
Executable file
After Width: | Height: | Size: 82 KiB |
BIN
01-js/01-getting-started/05-devtools/firefox_console_down.png
Executable file
After Width: | Height: | Size: 468 B |
BIN
01-js/01-getting-started/05-devtools/firefox_console_down@2x.png
Executable file
After Width: | Height: | Size: 3.7 KiB |
BIN
01-js/01-getting-started/05-devtools/firefox_console_enable.png
Executable file
After Width: | Height: | Size: 53 KiB |
BIN
01-js/01-getting-started/05-devtools/firefox_console_enable@2x.png
Executable file
After Width: | Height: | Size: 83 KiB |
BIN
01-js/01-getting-started/05-devtools/safari.png
Executable file
After Width: | Height: | Size: 69 KiB |
3
01-js/01-getting-started/index.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Введение
|
||||
|
||||
Про язык JavaScript и окружение для разработки на нём.
|
|
@ -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 @@
|
|||
{"name":"alert","plunk":"FhqK9YjFNQB1owusRqMd"}
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
alert('Я - JavaScript!');
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
# Выведите alert
|
||||
|
||||
[importance 5]
|
||||
|
||||
Сделайте страницу, которая выводит "Я - JavaScript!".
|
||||
|
||||
Создайте ее на диске, откройте в браузере, убедитесь, что все работает.
|
||||
|
||||
[demo src="solution"]
|
1
01-js/02-first-steps/01-hello-world/02-hello-alert-ext/alert.js
Executable file
|
@ -0,0 +1 @@
|
|||
alert('Я - JavaScript!');
|
11
01-js/02-first-steps/01-hello-world/02-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>
|
|
@ -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
01-js/02-first-steps/01-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 и Яндекс используют такой подход по умолчанию.
|
||||
|
||||
## Задачи
|
||||
|
||||
Очень важно не только читать учебник, но делать что-то самостоятельно.
|
||||
|
||||
Решите задачки, чтобы удостовериться, что вы все правильно поняли.
|
||||
|
113
01-js/02-first-steps/02-structure/article.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
# Структура кода
|
||||
|
||||
В этой главе мы рассмотрим общую структуру кода, команды и их разделение.
|
||||
[cut]
|
||||
## Команды
|
||||
|
||||
Например, можно вместо одного вызова `alert` сделать два:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert('Привет'); alert('Мир');
|
||||
```
|
||||
|
||||
Как правило, новая команда занимает отдельную строку -- так код лучше читается:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert('Привет');
|
||||
alert('Мир');
|
||||
```
|
||||
|
||||
Точку с запятой *во многих случаях* можно не ставить, если есть переход на новую строку.
|
||||
|
||||
Так тоже будет работать:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert('Привет')
|
||||
alert('Мир')
|
||||
```
|
||||
|
||||
В этом случае JavaScript интерпретирует переход на новую строчку как разделитель команд и автоматически вставляет "виртуальную" точку с запятой между ними.
|
||||
|
||||
**Однако, важно то, что "во многих случаях" не означает "всегда"!**
|
||||
|
||||
Например, запустите этот код:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(3 +
|
||||
1
|
||||
+ 2);
|
||||
```
|
||||
|
||||
Выведет 6.
|
||||
|
||||
То есть, точка с запятой не ставится. Почему? Интуитивно понятно, что здесь дело в "незавершённом выражении", конца которого JavaScript ждёт с первой строки и поэтому не ставит точку с запятой. И здесь это, пожалуй, хорошо и приятно.
|
||||
|
||||
**Но в некоторых важных ситуациях JavaScript "забывает" вставить точку с запятой там, где она нужна.**
|
||||
|
||||
Таких ситуаций не так много, но они всё же есть, и ошибки, которые при этом появляются, достаточно сложно обнаруживать и исправлять.
|
||||
|
||||
**Поэтому рекомендуется точки с запятой ставить. Сейчас это, фактически, стандарт, которому следуют все большие проекты.**
|
||||
|
||||
|
||||
## Комментарии
|
||||
|
||||
Со временем программа становится большой и сложной. Появляется необходимость добавить *комментарии*, которые объясняют, что происходит и почему.
|
||||
|
||||
Комментарии могут находиться в любом месте программы и никак не влияют на ее выполнение. Интерпретатор JavaScript попросту игнорирует их.
|
||||
|
||||
*Однострочные комментарии* начинаются с двойного слэша `//`. Текст считается комментарием до конца строки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// Команда ниже говорит "Привет"
|
||||
alert('Привет');
|
||||
|
||||
alert('Мир'); // Второе сообщение выводим отдельно
|
||||
```
|
||||
|
||||
*Многострочные комментарии* начинаются слешем-звездочкой <code>"/*"</code> и заканчиваются звездочкой-слэшем <code>"*/"</code>, вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
/* Пример с двумя сообщениями.
|
||||
Это - многострочный комментарий.
|
||||
*/
|
||||
alert('Привет');
|
||||
alert('Мир');
|
||||
```
|
||||
|
||||
Все содержимое комментария игнорируется. Если поместить код внутрь <code>/* ... */</code> или после `//` -- он не выполнится.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
/* Закомментировали код
|
||||
alert('Привет');
|
||||
*/
|
||||
alert('Мир');
|
||||
```
|
||||
|
||||
[smart header="Используйте горячие клавиши!"]
|
||||
В большинстве редакторов комментарий можно поставить горячей клавишей, обычно это <code class="key">Ctrl</code> + <code class="key">/</code> для однострочных и что-то вроде <code class="key">Ctrl</code> + <code class="key">Shift</code> + <code class="key">/</code> -- для многострочных комментариев (нужно выделить блок и нажать сочетание клавиш). Детали смотрите в руководстве по редактору.
|
||||
[/smart]
|
||||
|
||||
[warn header="Вложенные комментарии не поддерживаются!"]
|
||||
В этом коде будет ошибка:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
/*
|
||||
/* вложенный комментарий ?!? */
|
||||
*/
|
||||
alert('Мир');
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
|
||||
Не бойтесь комментариев. Чем больше кода в проекте -- тем они важнее. Что же касается увеличения размера кода -- это не страшно, т.к. существуют инструменты сжатия JavaScript, которые при публикации кода легко их удалят.
|
||||
|
||||
На следующих занятиях мы поговорим о переменных, блоках и других структурных элементах программы на JavaScript.
|
|
@ -0,0 +1,13 @@
|
|||
Каждая строчка решения соответствует одному шагу задачи:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var admin, name; // две переменных через запятую
|
||||
|
||||
name = "Василий";
|
||||
|
||||
admin = name;
|
||||
|
||||
alert(admin); // "Василий"
|
||||
```
|
||||
|
10
01-js/02-first-steps/03-variables/01-hello-variables/task.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Работа с переменными
|
||||
|
||||
[importance 2]
|
||||
|
||||
<ol>
|
||||
<li>Объявите две переменные: `admin` и `name`. </li>
|
||||
<li>Запишите в `name` строку `"Василий"`. </li>
|
||||
<li>Скопируйте значение из `name` в `admin`.</li>
|
||||
<li>Выведите `admin` (должно вывести "Василий").</li>
|
||||
</ol>
|
252
01-js/02-first-steps/03-variables/article.md
Normal file
|
@ -0,0 +1,252 @@
|
|||
# Переменные
|
||||
|
||||
В зависимости от того, для чего вы делаете скрипт, понадобится работать с информацией.
|
||||
|
||||
Если это электронный магазин - то это товары, корзина. Если чат - посетители, сообщения и так далее.
|
||||
|
||||
Чтобы хранить информацию, используются *переменные*.
|
||||
[cut]
|
||||
## Переменная
|
||||
|
||||
***Переменная* состоит из имени и выделенной области памяти, которая ему соответствует**.
|
||||
|
||||
Для *объявления* или, другими словами, *создания переменной* используется ключевое слово `var`:
|
||||
|
||||
```js
|
||||
var message;
|
||||
```
|
||||
|
||||
После объявления, можно записать в переменную данные:
|
||||
|
||||
```js
|
||||
var message;
|
||||
message = 'Привет'; // сохраним в переменной строку
|
||||
```
|
||||
|
||||
Эти данные будут сохранены в соответствующей области памяти и в дальнейшем доступны при обращении по имени:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var message;
|
||||
message = 'Привет';
|
||||
|
||||
alert(message); // выведет содержимое переменной
|
||||
```
|
||||
|
||||
Для краткости можно совместить объявление переменной и запись данных:
|
||||
|
||||
```js
|
||||
var message = 'Привет';
|
||||
```
|
||||
|
||||
Можно даже объявить несколько переменных сразу:
|
||||
|
||||
```js
|
||||
var user = 'Вася', age = 25, message = 'Привет';
|
||||
```
|
||||
|
||||
### Аналогия из жизни
|
||||
|
||||
**Проще всего понять переменную, если представить ее как "коробку" для данных, с уникальным именем.**
|
||||
|
||||
Например, переменная `message` - это коробка, в которой хранится значение `"Привет"`:
|
||||
|
||||
<img src="box-message-hello.png">
|
||||
|
||||
В коробку можно положить любое значение, а позже - поменять его. Значение в переменной можно изменять сколько угодно раз:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var message;
|
||||
|
||||
message = 'Привет';
|
||||
|
||||
message = 'Мир'; // заменили значение
|
||||
|
||||
alert(message);
|
||||
```
|
||||
|
||||
<img src="box-trans1.png">
|
||||
|
||||
При изменении значения старое содержимое переменной удаляется.
|
||||
|
||||
[smart]
|
||||
Существуют [функциональные](http://ru.wikipedia.org/wiki/%D0%AF%D0%B7%D1%8B%D0%BA_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F) языки программирования, в которых значение переменной менять нельзя. Например, [Scala](http://www.scala-lang.org/) или [Erlang](http://www.erlang.org/).
|
||||
|
||||
В таких языках положил один раз значение в коробку - и оно хранится там вечно, ни удалить ни изменить. А нужно что-то другое сохранить - изволь создать новую коробку (объявить новую переменную), повторное использование невозможно.
|
||||
|
||||
С виду -- не очень удобно, но, как ни странно, и на таких языках вполне можно успешно программировать. Более того, оказывается, что в ряде областей, например в распараллеливании вычислений, они имеют преимущества. Изучение какого-нибудь функционального языка рекомендуется для расширения кругозора.
|
||||
[/smart]
|
||||
|
||||
### Имена переменных [#variable-naming]
|
||||
|
||||
На имя переменной в JavaScript наложены всего два ограничения.
|
||||
<ol>
|
||||
<li>Имя может состоять из: букв, цифр, символов `$` и `_`</li>
|
||||
<li>Первый символ не должен быть цифрой.</li>
|
||||
</ol>
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
var myName;
|
||||
var test123;
|
||||
```
|
||||
|
||||
**Что особенно интересно -- доллар `'$'` и знак подчеркивания `'_'` являются такими же обычными символами, как буквы:**
|
||||
|
||||
```js
|
||||
//+ run untrusted
|
||||
var $ = 1; // объявили переменную с именем '$'
|
||||
var _ = 2; // переменная с именем '_'
|
||||
|
||||
alert($ + _); // 3
|
||||
```
|
||||
|
||||
А такие переменные были бы неправильными:
|
||||
|
||||
```js
|
||||
var 1a; // начало не может быть цифрой
|
||||
|
||||
var my-name; // дефис '-' не является разрешенным символом
|
||||
```
|
||||
|
||||
[smart header="Регистр букв имеет значение"]
|
||||
Переменные `apple` и `AppLE` - две разные переменные.
|
||||
[/smart]
|
||||
|
||||
[smart header="Русские буквы допустимы, но не рекомендуются"]
|
||||
|
||||
В названии переменных можно использовать и русские буквы, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var имя = "Вася";
|
||||
alert(имя); // "Вася"
|
||||
```
|
||||
|
||||
Технически, ошибки здесь нет, но на практике сложилась традиция использовать в именах только английские буквы.
|
||||
[/smart]
|
||||
|
||||
[warn header="Зарезервированные имена"]
|
||||
|
||||
Существует список зарезервированных слов, которые нельзя использовать для переменных, так как они используются самим языком, например: `var, class, return, export` и др.
|
||||
|
||||
Некоторые такие слова, например, `export`, не используются в современном JavaScript, но они зарезервированы на будущее.
|
||||
|
||||
Например, такой пример выдаст синтаксическую ошибку:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var class = 5; // ошибка, слово зарезервировано
|
||||
alert(class);
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
|
||||
### Копирование значений
|
||||
|
||||
Переменные в JavaScript могут хранить не только строки, но и другие данные, например, числа.
|
||||
|
||||
Объявим две переменные, положим в одну -- строку, а в другую -- число:
|
||||
|
||||
```js
|
||||
var num = 100500;
|
||||
var message = 'Привет';
|
||||
```
|
||||
|
||||
При этом в любой момент значение можно скопировать из одной переменной в другую:
|
||||
|
||||
```js
|
||||
var num = 100500;
|
||||
var message = 'Привет';
|
||||
|
||||
*!*
|
||||
message = num;
|
||||
*/!*
|
||||
```
|
||||
|
||||
При копировании значение из `num` перезаписывает текущее в `message`.
|
||||
|
||||
**В "коробке" `message` изменится значение**:
|
||||
|
||||
<img src="box-trans2.png">
|
||||
|
||||
После этого присваивания в обеих коробках `num` и `message` находится одно и то же значение `100500`.
|
||||
|
||||
## Важность директивы var
|
||||
|
||||
В JavaScript вы можете создать переменную и без `var`, достаточно просто присвоить ей значение:
|
||||
|
||||
```js
|
||||
x = "value"; // переменная создана, если ее не было
|
||||
```
|
||||
|
||||
Технически, это не вызовет ошибки, но делать так все-таки не стоит.
|
||||
|
||||
**Всегда определяйте переменные через `var`**.
|
||||
|
||||
Это хороший тон в программировании и помогает избежать ошибок.
|
||||
|
||||
[warn header="Ошибка в IE8- без `var`"]
|
||||
Если же вы собираетесь поддерживать IE8-, то у меня для вас ещё одна причина всегда использовать `var`.
|
||||
|
||||
Следущий документ в IE8- ничего не выведет, будет ошибка:
|
||||
|
||||
```html
|
||||
<div id="test"></div>
|
||||
<script>
|
||||
*!*
|
||||
test = 5; // здесь будет ошибка!
|
||||
*/!*
|
||||
alert(test); // не сработает
|
||||
</script>
|
||||
```
|
||||
|
||||
Это потому, что переменная `test` не объявлена через `var` и совпадает с `id` элемента `<div>`. Даже не спрашивайте почему -- это ошибка в браузере IE до версии 9.
|
||||
|
||||
Самое "забавное" -- то, что такая ошибка присвоения значений будет только в IE8- и только если на странице присутствует элемент с совпадающим с именем `id`.
|
||||
|
||||
Такие ошибки особенно "весело" исправлять и отлаживать.
|
||||
|
||||
Вывод простой -- всегда объявляем переменные через `var`, и сюрпризов не будет. Даже в старых IE.
|
||||
[/warn]
|
||||
|
||||
## Константы
|
||||
|
||||
*Константа* -- это переменная, которая никогда не меняется. Как правило, их называют большими буквами, через подчёркивание. Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var COLOR_RED = "#F00";
|
||||
var COLOR_GREEN = "#0F0";
|
||||
var COLOR_BLUE = "#00F";
|
||||
var COLOR_ORANGE = "#FF7F00";
|
||||
|
||||
var color = COLOR_ORANGE;
|
||||
alert(color); // #FF7F00
|
||||
```
|
||||
|
||||
Технически, константа является обычной переменной, то есть её *можно* изменить. Но мы *договариваемся* этого не делать.
|
||||
|
||||
Зачем нужны константы? Почему бы просто не писать `var color = "#FF7F00"`?
|
||||
|
||||
<ol>
|
||||
<li>Во-первых, константа `COLOR_ORANGE` -- это понятное имя. По присвоению `var color="#FF7F00"` непонятно, что цвет -- оранжевый. Иными словами, константа `COLOR_ORANGE` является "понятным псевдонимом" для значения `#FF7F00`.</li>
|
||||
<li>Во-вторых, опечатка в строке, особенно такой сложной как `#FF7F00`, может быть не замечена, а в имени константы её допустить куда сложнее.</li>
|
||||
</ol>
|
||||
|
||||
**Константы используют вместо строк и цифр, чтобы сделать программу понятнее и избежать ошибок.**
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>В JavaScript можно объявлять переменные для хранения данных. Это делается при помощи `var`.</li>
|
||||
<li>Технически, можно просто записать значение и без объявления переменной, однако по ряду причин это не рекомендуется.</li>
|
||||
<li>Вместе с объявлением можно сразу присвоить значение: `var x = 10`.</li>
|
||||
<li>Переменные, которые названы `БОЛЬШИМИ_БУКВАМИ`, являются константами, то есть никогда не меняются. Как правило, они используются для удобства, чтобы было меньше ошибок.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
BIN
01-js/02-first-steps/03-variables/box-message-hello.png
Executable file
After Width: | Height: | Size: 34 KiB |
BIN
01-js/02-first-steps/03-variables/box-trans1.png
Executable file
After Width: | Height: | Size: 41 KiB |
BIN
01-js/02-first-steps/03-variables/box-trans2.png
Executable file
After Width: | Height: | Size: 44 KiB |
|
@ -0,0 +1,13 @@
|
|||
Каждая строчка решения соответствует одному шагу задачи:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var ourPlanetName = "Земля"; // буквально "название нашей планеты"
|
||||
|
||||
var userName = "Петя"; // "имя посетителя"
|
||||
```
|
||||
|
||||
Названия переменных можно бы сократить, например, до `planet` и `name`, но тогда станет менее понятно, о чем речь.
|
||||
|
||||
Насколько допустимы такие сокращения -- зависит от скрипта, его размера и сложности, от того, есть ли другие планеты и пользователи. В общем, лучше не жалеть букв и называть переменные так, чтобы по имени можно было легко понять, что в ней находится, и нельзя было перепутать переменные.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Объявление переменных
|
||||
|
||||
[importance 3]
|
||||
|
||||
<ol>
|
||||
<li>Создайте переменную для названия нашей планеты и присвойте ей значение `"Земля"`. *Правильное* имя выберите сами.</li>
|
||||
<li>Создайте переменную для имени посетителя со значением `"Петя"`. Имя также на ваш вкус.</li>
|
||||
</ol>
|
118
01-js/02-first-steps/04-variable-names/article.md
Normal file
|
@ -0,0 +1,118 @@
|
|||
# Правильный выбор имени переменной
|
||||
|
||||
Правильный выбор имени переменной -- одна из самых важных и сложных вещей в программировании, которая отличает начинающего от гуру.
|
||||
|
||||
[cut]
|
||||
|
||||
Дело в том, что большинство времени мы тратим не на изначальное написание кода, а на его развитие.
|
||||
|
||||
Возможно, эти слова не очевидны, если вы пока что ничего большого не писали или пишете код "только для чтения" (написал 5 строк, отдал заказчику и забыл). Но чем более серьёзные проекты вы будете делать, тем более актуальны они будут для вас.
|
||||
|
||||
Что такое это "развитие"? Это когда я вчера написал код, а сегодня (или спустя неделю) прихожу и хочу его поменять. Например, вывести сообщение не так, а эдак... Обработать товары по-другому, добавить функционал.. А где у меня там сообщение хранится? А где товар?...
|
||||
|
||||
**Гораздо проще найти нужные данные, если они правильно помечены, то есть когда переменная названа *правильно*.**
|
||||
|
||||
<ul>
|
||||
<li>**Правило 1.**
|
||||
|
||||
**Никакого транслита. Только английский.**
|
||||
|
||||
Неприемлемы:
|
||||
|
||||
```js
|
||||
var moiTovari;
|
||||
var cena;
|
||||
var ssilka;
|
||||
```
|
||||
|
||||
Подойдут:
|
||||
|
||||
```js
|
||||
var myGoods;
|
||||
var price;
|
||||
var link;
|
||||
```
|
||||
|
||||
Чем плох транслит?
|
||||
|
||||
Во-первых, среди разработчиков всего мира принято использовать английский язык для имён переменных. И если ваш код потом попадёт к кому-то другому, например вы будете в команде больше чем из одного человека, то велик шанс, что транслит ему не понравится.
|
||||
|
||||
Во-вторых, русский транслит хуже читается и длиннее, чем названия на английском.
|
||||
|
||||
В-третьих, в проектах вы наверняка будете применять библиотеки, написанные другими людьми. Многое уже готово, в распоряжении современного разработчика есть масса инструментов, все они используют названия переменных и функций на английском языке, и вы, конечно, будете их использовать. А от кода, где транслит перемешан с английским -- могут волосы смогут встать дыбом, и не только на голове.
|
||||
|
||||
Если вы вдруг не знаете английский -- самое время выучить.
|
||||
</li>
|
||||
<li>**Правило 2.**
|
||||
|
||||
**Использовать короткие имена только для переменных "местного значения".**
|
||||
|
||||
Называть переменные именами, не несущими смысловой нагрузки, например `a`, `e`, `p`, `mg` -- можно только в том случае, если они используются в небольшом фрагменте кода и их применение очевидно.
|
||||
|
||||
Вообще же, название переменной должно быть понятным. Иногда для этого нужно использовать несколько слов.
|
||||
</li>
|
||||
<li>**Правило 3.**
|
||||
|
||||
**Переменные из нескольких слов пишутся `вместеВотТак`.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
var borderLeftWidth;
|
||||
```
|
||||
|
||||
Этот способ записи называется "верблюжьей нотацией" или, по-английски, "camelCase".
|
||||
|
||||
Существует альтернативный стандарт, когда несколько слов пишутся через знак подчеркивания `'_'`:
|
||||
|
||||
```js
|
||||
var border_left_width;
|
||||
```
|
||||
|
||||
Преимущественно в JavaScript используется вариант `borderLeftWidth`, в частности во встроенных языковых и браузерных функциях. Поэтому целесообразно остановиться на нём.
|
||||
|
||||
Ещё одна причина выбрать "верблюжью нотацию" -- запись в ней немного короче, чем c подчеркиванием, т.к. не нужно вставлять `'_'`.
|
||||
</li>
|
||||
<li>**Правило последнее, главное.**
|
||||
|
||||
**Имя переменной должно максимально чётко соответствовать хранимым в ней данным.**
|
||||
|
||||
Придумывание таких имен -- одновременно коротких и точных, при которых всегда понятно, что где лежит, приходит с опытом, но только если сознательно стремиться к этому.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Позвольте поделиться одним небольшим секретом, который позволит улучшить ваши названия переменных и сэкономит вам время.
|
||||
|
||||
Бывает так, что вы написали код, через некоторое время к нему возвращаетесь, и вам надо что-то поправить. Например, изменить какую-то рамку `border`...
|
||||
|
||||
...И вы помните, что переменная, в которой хранится нужное вам значение, называется примерно так: `borderLeftWidth`. Вы ищете ее в коде, не находите, разбираетесь, и обнаруживаете, что на самом деле переменная называлась вот так: `leftBorderWidth`. После чего вносите нужные правки.
|
||||
|
||||
**Если вы искали переменную с одним именем, а нашли -- с другим, то самый лучший ход -- это *переименовать* переменную, чтобы имя было тем, которое вы искали.**
|
||||
|
||||
То есть, у вас в коде `leftBorderWidth`, а вы ее переименовываете на `borderLeftWidth`.
|
||||
|
||||
Зачем? Дело в том, что в следующий раз, когда вы захотите что-то поправить, то вы будете искать по тому же самому имени. Соответственно, это сэкономит вам время.
|
||||
|
||||
Кроме того, поскольку именно это имя переменной пришло вам в голову -- скорее всего, оно больше соответствует хранимым там данным, чем то, которое вы придумали изначально. Исключения бывают, но в любом случае -- такое несовпадение -- это повод задуматься.
|
||||
|
||||
[summary]
|
||||
Смысл имени переменной -- это "имя на коробке", по которому мы сможем максимально быстро находить нужные нам данные.
|
||||
|
||||
**Не нужно бояться переименовывать переменные, если вы придумали имя получше.**
|
||||
|
||||
Современные редакторы позволяют делать это очень удобно и быстро. Это в конечном счете сэкономит вам время.
|
||||
[/summary]
|
||||
|
||||
|
||||
[warn header="Храните в переменной то, что следует"]
|
||||
Бывают ленивые программисты, которые, вместо того чтобы объявить новую переменную, используют существующую.
|
||||
|
||||
В результате получается, что такая переменная -- как коробка, в которую кидают то одно, то другое, то третье, при этом не меняя название. Что в ней лежит сейчас? А кто его знает.. Нужно подойти, проверить.
|
||||
|
||||
Сэкономит такой программист время на объявлении переменной -- потеряет в два раза больше на отладке кода.
|
||||
|
||||
**"Лишняя" переменная -- добро, а не зло.**
|
||||
[/warn]
|
||||
|
||||
|
||||
|
90
01-js/02-first-steps/05-strict-mode/article.md
Normal file
|
@ -0,0 +1,90 @@
|
|||
# Современный стандарт, "use strict"
|
||||
|
||||
Очень долго язык JavaScript развивался без потери совместимости. Новые возможности добавлялись в язык, но старые -- никогда не менялись, чтобы не "сломать" уже существующие HTML/JS-страницы с их использованием.
|
||||
|
||||
Однако, это привело к тому, что любая ошибка в дизайне языка становилась "вмороженной" в него навсегда.
|
||||
|
||||
Так было до появления стандарта EcmaScript 5 (ES5), который одновременно добавил новые возможности и внёс в язык ряд исправлений, которые могут привести к тому, что старый код, который был написан до его появления, перестанет работать.
|
||||
|
||||
Чтобы этого не случилось, решили, что по умолчанию эти опасные изменения будут выключены, и код будет работать по-старому. А для того, чтобы перевести код в режим полного соответствия современному стандарту, нужно указать специальную директиву `use strict`.
|
||||
|
||||
Эта директива не поддерживается IE9-.
|
||||
|
||||
[cut]
|
||||
|
||||
## Директива use strict
|
||||
|
||||
Директива выглядит как строка `"use strict";` или `'use strict';` и ставится в начале скрипта.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
"use strict";
|
||||
|
||||
// этот код будет работать по современному стандарту ES5
|
||||
...
|
||||
```
|
||||
|
||||
**Важный пример отличия современного стандарта от старого -- присвоение переменной без объявления, оно в [старом стандарте](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) было допустимо, а в современном -- нет.**
|
||||
|
||||
Поэтому следующий код выдаст ошибку:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
"use strict";
|
||||
|
||||
*!*
|
||||
x = 5; // error: x is not defined
|
||||
*/!*
|
||||
```
|
||||
|
||||
Обратим внимание, директиву `use strict` нужно ставить до кода, иначе она не сработает:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a;
|
||||
|
||||
"use strict"; // слишком поздно
|
||||
|
||||
*!*
|
||||
x = 5; // ошибки не будет, так как строгий режим не активирован
|
||||
*/!*
|
||||
```
|
||||
|
||||
[warn header="Отменить действие `use strict` никак нельзя"]
|
||||
Не существует директивы `no use strict` или подобной, которая возвращает в старый режим.
|
||||
|
||||
Если уж вошли в современный режим, то это дорога в один конец.
|
||||
[/warn]
|
||||
|
||||
[smart header="`use strict` для функций"]
|
||||
Через некоторое время мы будем проходить [функции](/function-basics). На будущее заметим, что `use strict` также можно указывать в начале функций, тогда строгий режим будет действовать только внутри функции.
|
||||
[/smart]
|
||||
|
||||
## Нужен ли мне use strict?
|
||||
|
||||
В строгом режиме исправлены некоторые ошибки в дизайне языка. Если старый код их эксплуатировал -- будет сюрприз и, возможно, ошибка.
|
||||
|
||||
**Основная проблема при использовании `use strict` -- поддержка браузеров IE9-, которые игнорируют `use strict`.**
|
||||
|
||||
Предположим, что мы, используя `use strict`, разработали код и протестировали его в браузере Chrome. Всё работает... Однако, вероятность ошибок при этом в IE9- выросла! Он-то всегда работает по старому стандарту, а значит, иногда по-другому. Возникающие ошибки придётся отлаживать уже в IE9-, и это намного менее приятно, нежели в Chrome.
|
||||
|
||||
Поэтому строгий режим используют пока ещё не повсеместно.
|
||||
|
||||
Впрочем, проблема не так страшна. Несовместимостей мало. И, если их знать (а в учебнике мы будем останавливаться на них) и писать правильный код, то всё будет в порядке и `use strict` станет нашим верным помощником.
|
||||
|
||||
[smart header="ES5-shim"]
|
||||
Директива `use strict` влияет только на "опасные" возможности языка. Большинство приятных новых возможностей, которые мы изучим далее, будут работать в любом случае, поскольку ни с чем не конфликтуют.
|
||||
|
||||
Браузер IE8 был создан до появления ES5, поэтому не знает о них. Так что же, неужели они для нас закрыты, если мы решаем поддерживать IE8? К счастью, нет.
|
||||
|
||||
Сообществом создана библиотека [ES5 shim](https://github.com/es-shims/es5-shim), которая при подключении файлов `es5-shim.js` и `es5-sham.js` добавляет в IE8- многие возможности из современного стандарта JavaScript.
|
||||
[/smart]
|
||||
|
||||
## Итого
|
||||
|
||||
В этой главе мы познакомились с понятием "строгий режим" и посмотрели одно из важнейших его отличий -- обязательность объявления переменных. Ранее уже упоминалось, что объявлять переменные через `var` полезно с целью избежания конфликтов в IE8-. Теперь есть ещё довод -- соответствие современному стандарту JavaScript.
|
||||
|
||||
Есть и другие отличия работы в строгом режиме, которые мы будем рассматривать далее, по мере погружения в JavaScript.
|
||||
|
||||
Далее мы будем предполагать, что разработка ведётся либо в современном браузере, либо в IE8- с подключённым [ES5 shim](https://github.com/es-shims/es5-shim). Это позволит нам использовать большинство возможностей современного JavaScript во всех браузерах.
|
105
01-js/02-first-steps/06-types-intro/article.md
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Шесть типов данных
|
||||
|
||||
В JavaScript существует несколько основных типов данных.
|
||||
[cut]
|
||||
|
||||
## Число "number"
|
||||
|
||||
```js
|
||||
var n = 123;
|
||||
n = 12.345;
|
||||
```
|
||||
|
||||
Единый тип *число* используется как для целых, так и для дробных чисел.
|
||||
|
||||
Существуют специальные числовые значения `Infinity` (бесконечность) и `NaN` (ошибка вычислений).
|
||||
|
||||
Например, бесконечность `Infinity` получается при делении на ноль:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 1 / 0 ); // Infinity
|
||||
```
|
||||
|
||||
Ошибка вычислений `NaN` будет результатом некорректной математической операции, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "нечисло" * 2 ); // NaN, ошибка
|
||||
```
|
||||
|
||||
Эти значения формально принадлежат типу "число", хотя, конечно, числами в их обычном понимании не являются.
|
||||
|
||||
## Строка "string"
|
||||
|
||||
```js
|
||||
var str = "Мама мыла раму";
|
||||
str = 'Одинарные кавычки тоже подойдут';
|
||||
```
|
||||
|
||||
**В JavaScript одинарные и двойные кавычки равноправны.** Можно использовать или те или другие.
|
||||
|
||||
[smart header="Тип *символ* не существует, есть только *строка*."]
|
||||
В некоторых языках программирования есть специальный тип данных для одного символа. Например, в языке С это `char`. В JavaScript есть только тип "строка" `string`. Что, надо сказать, вполне удобно.
|
||||
[/smart]
|
||||
|
||||
## Булевый (логический) тип "boolean"
|
||||
|
||||
У него всего два значения: `true` (истина) и `false` (ложь).
|
||||
|
||||
Как правило, такой тип используется для хранения значения типа да/нет, например:
|
||||
|
||||
```js
|
||||
var checked = true; // поле формы помечено галочкой
|
||||
checked = false; // поле формы не содержит галочки
|
||||
```
|
||||
|
||||
О нём мы поговорим более подробно, когда будем обсуждать логические вычисления и условные операторы.
|
||||
|
||||
## Специальное значение "null"
|
||||
|
||||
Значение `null` не относится ни к одному из типов выше, а образует свой отдельный тип, состоящий из единственного значения `null`:
|
||||
|
||||
```js
|
||||
var age = null;
|
||||
```
|
||||
|
||||
В JavaScript `null` не является "ссылкой на несуществующий объект" или "нулевым указателем", как в некоторых других языках. Это просто специальное значение, которое имеет смысл "ничего" или "значение неизвестно".
|
||||
|
||||
В частности, код выше говорит о том, что возраст `age` неизвестен.
|
||||
|
||||
## Специальное значение "undefined"
|
||||
|
||||
Значение `undefined`, как и `null`, образует свой собственный тип, состоящий из одного этого значения. Оно имеет смысл "значение не присвоено".
|
||||
|
||||
Если переменная объявлена, но в неё ничего не записано, то ее значение как раз и есть `undefined`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x;
|
||||
alert(x); // выведет "undefined"
|
||||
```
|
||||
|
||||
Можно присвоить `undefined` и в явном виде, хотя это делается редко:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x = 123;
|
||||
x = undefined;
|
||||
|
||||
alert(x); // "undefined"
|
||||
```
|
||||
|
||||
В явном виде `undefined` обычно не присваивают, так как это противоречит его смыслу. Для записи в переменную "пустого" или "неизвестного" значения используется `null`.
|
||||
|
||||
## Объекты "object"
|
||||
|
||||
Первые 5 типов называют *"примитивными"*.
|
||||
|
||||
Особняком стоит шестой тип: *"объекты"*. К нему относятся, например, даты, он используется для коллекций данных и для объявления более сложных сущностей. Позже мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов.
|
||||
|
||||
## Итого
|
||||
|
||||
Есть 5 "примитивных" типов: `number`, `string`, `boolean`, `null`, `undefined` и 6-й тип -- объекты `object`.
|
||||
|
||||
Очень скоро мы изучим их во всех деталях.
|
112
01-js/02-first-steps/07-properties-and-methods/article.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Методы и свойства
|
||||
|
||||
Все значения в JavaScript, за исключением `null` и `undefined`, содержат набор вспомогательных функций и значений, доступных "через точку".
|
||||
|
||||
Такие функции называют "методами", а значения -- "свойствами". Здесь мы рассмотрим основы использования свойств и методов.
|
||||
|
||||
[cut]
|
||||
|
||||
## Свойство str.length
|
||||
|
||||
У строки есть *свойство* `length`, содержащее длину:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Привет, мир!".length ); // 12
|
||||
```
|
||||
|
||||
Можно и записать строку в переменную, а потом запросить её свойство:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Привет, мир!";
|
||||
alert( str.length ); // 12
|
||||
```
|
||||
|
||||
## Метод str.toUpperCase()
|
||||
|
||||
Также у строк есть *метод* `toUpperCase()`, который возвращает строку в верхнем регистре:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var hello = "Привет, мир!";
|
||||
|
||||
*!*
|
||||
alert( hello.toUpperCase() ); // "ПРИВЕТ, МИР!"
|
||||
*/!*
|
||||
```
|
||||
|
||||
[warn header="Вызов метода -- через круглые скобки!"]
|
||||
|
||||
Обратите внимание, для вызова метода обязательно нужны круглые скобки.
|
||||
|
||||
Посмотрите, например, результат обращения к `toUpperCase` без скобок:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var hello = "Привет";
|
||||
|
||||
*!*
|
||||
alert( hello.toUpperCase ); // function...
|
||||
*/!*
|
||||
```
|
||||
|
||||
Метод -- это встроенная команда ("функция", мы поговорим о них позже), которую нужно вызвать для получения значения. Если обратиться к ней без скобок, то результатом будет сама эта функция. Как правило браузер выведет её как-то так: `"function toUpperCase() { ... }"`, а чтобы получить результат -- нужно добавить скобки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var hello = "Привет";
|
||||
|
||||
*!*
|
||||
alert( hello.toUpperCase() ); // ПРИВЕТ
|
||||
*/!*
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
Более подробно с различными свойствами и методами строк мы познакомимся в главе [](/string).
|
||||
|
||||
## Метод num.toFixed(n)
|
||||
|
||||
Есть методы и у чисел, например `num.toFixed(n)`. Он округляет число `num` до `n` знаков после запятой, при необходимости добивает нулями до данной длины и возвращает в виде строки (удобно для форматированного вывода):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = 12.345;
|
||||
|
||||
alert( n.toFixed(2) ); // "12.35"
|
||||
alert( n.toFixed(0) ); // "12"
|
||||
alert( n.toFixed(5) ); // "12.34500"
|
||||
```
|
||||
|
||||
Детали работы `toFixed` разобраны в главе [](/number).
|
||||
|
||||
[warn header="Обращение к методам чисел"]
|
||||
К методу числа можно обратиться и напрямую:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 12.34.toFixed(1) ); // 12.3
|
||||
```
|
||||
|
||||
...Но если число целое, то будет проблема:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 12.toFixed(1) ); // ошибка!
|
||||
```
|
||||
|
||||
Ошибка произойдёт потому, что JavaScript ожидает десятичную дробь после точки.
|
||||
|
||||
Это -- особенность синтаксиса JavaScript. Вот так -- будет работать:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 12..toFixed(1) ); // 12.0
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
|
||||
|
||||
Мы еще встретимся со [строками](/string) и [числами](/number) в последующих главах и глубже познакомимся с их свойствами и методами.
|
|
@ -0,0 +1,23 @@
|
|||
# Разъяснения
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 1, b = 1, c, d;
|
||||
|
||||
// префиксная форма сначала увеличивает a до 2, а потом возвращает
|
||||
c = ++a; alert(c); // 2
|
||||
|
||||
// постфиксная форма увеличивает, но возвращает старое значение
|
||||
d = b++; alert(d); // 1
|
||||
|
||||
// сначала увеличили a до 3, потом использовали в арифметике
|
||||
c = (2+ ++a); alert(c); // 5
|
||||
|
||||
// увеличили b до 3, но в этом выражении оставили старое значение
|
||||
d = (2+ b++); alert(d); // 4
|
||||
|
||||
// каждую переменную увеличили по 2 раза
|
||||
alert(a); // 3
|
||||
alert(b); // 3
|
||||
```
|
||||
|
20
01-js/02-first-steps/08-operators/01-increment-order/task.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Инкремент, порядок срабатывания
|
||||
|
||||
[importance 5]
|
||||
|
||||
Посмотрите, понятно ли вам, почему код ниже работает именно так?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 1, b = 1, c, d;
|
||||
|
||||
c = ++a; alert(c); // 2
|
||||
d = b++; alert(d); // 1
|
||||
|
||||
c = (2+ ++a); alert(c); // 5
|
||||
d = (2+ b++); alert(d); // 4
|
||||
|
||||
alert(a); // 3
|
||||
alert(b); // 3
|
||||
```
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Ответ: `x = 5`.
|
||||
|
||||
Оператор присваивания возвращает значение, которое будет записано в переменную, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 2;
|
||||
alert( a *= 2 ); // 4
|
||||
```
|
||||
|
||||
Отсюда `x = 1 + 4 = 5`.
|
|
@ -0,0 +1,12 @@
|
|||
# Результат присваивания
|
||||
|
||||
[importance 3]
|
||||
|
||||
Чему будет равен `x` в примере ниже?
|
||||
|
||||
```js
|
||||
var a = 2;
|
||||
|
||||
var x = 1 + (a *= 2);
|
||||
```
|
||||
|
415
01-js/02-first-steps/08-operators/article.md
Normal file
|
@ -0,0 +1,415 @@
|
|||
# Основные операторы
|
||||
|
||||
Для работы с переменными, со значениями, JavaScript поддерживает все стандартные операторы, большинство которых есть и в других языках программирования.
|
||||
[cut]
|
||||
|
||||
## Термины: "унарный", "бинарный", "операнд"
|
||||
|
||||
У операторов есть своя терминология, которая используется во всех языках программирования.
|
||||
<ul>
|
||||
<li>*Операнд* -- то, к чему применяется оператор. Например: `5 * 2` -- оператор умножения с левым и правым операндами. Другое название: "аргумент оператора".</li>
|
||||
<li>*Унарным* называется оператор, который применяется к одному выражению. Например, оператор унарный минус `"-"` меняет знак числа на противоположный:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x = 1;
|
||||
alert( -x ); // -1, унарный минус
|
||||
alert( -(x+2) ); // -3, унарный минус применён к результату сложения x+2
|
||||
alert( -(-3) ); // 3
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>*Бинарным* называется оператор, который применяется к двум операндам. Тот же минус существует и в бинарной форме:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x = 1, y = 3;
|
||||
alert( y - x ); // 2, бинарный минус
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Некоторые операторы, например, вычитание `"-"` и сложение `"+"`, бывают в двух вариантах: унарный -- при применении к одному операнду, и бинарный -- к двум.
|
||||
|
||||
## Арифметические операторы
|
||||
|
||||
**Базовые арифметические операторы знакомы нам с детства: это плюс `+`, минус `-`, умножить `*`, поделить `/`.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(2 + 2); // 4
|
||||
```
|
||||
|
||||
Или чуть сложнее:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 2;
|
||||
|
||||
i = (2 + i) * 3 / i;
|
||||
|
||||
alert(i); // 6
|
||||
```
|
||||
|
||||
**Более редкий арифметический оператор `%` интересен тем, что никакого отношения к процентам не имеет. Его результат `a % b` -- это остаток от деления `a` на `b`.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(5 % 2); // 1, остаток от деления 5 на 2
|
||||
alert(8 % 3); // 2, остаток от деления 8 на 3
|
||||
alert(6 % 3); // 0, остаток от деления 6 на 3
|
||||
```
|
||||
|
||||
### Сложение строк, бинарный +
|
||||
|
||||
Если бинарный оператор `+` применить к строкам, то он их объединяет в одну:
|
||||
|
||||
```js
|
||||
var a = "моя" + "строка";
|
||||
alert(a); // моястрока
|
||||
```
|
||||
|
||||
**Если хотя бы один аргумент является строкой, то второй будет также преобразован к строке!**
|
||||
|
||||
Причем не важно, справа или слева находится операнд-строка, в любом случае нестроковый аргумент будет преобразован. Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '1' + 2 ); // "12"
|
||||
alert( 2 + '1' ); // "21"
|
||||
```
|
||||
|
||||
Это приведение к строке -- особенность бинарного оператора `"+"`.
|
||||
|
||||
**Остальные арифметические операторы работают только с числами и всегда приводят аргументы к числу.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '1' - 2 ); // -1
|
||||
alert( 6 / '2'); // 3
|
||||
```
|
||||
|
||||
### Унарный плюс +
|
||||
|
||||
Унарный, то есть применённый к одному значению, плюс как арифметический оператор ничего не делает:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( +1 ); // 1
|
||||
alert( +(1-2) ); // -1
|
||||
```
|
||||
|
||||
Как видно, плюс ничего не изменил в выражениях. Результат -- такой же, как и без него.
|
||||
|
||||
Тем не менее, он широко применяется, так как его "побочный эффект" -- преобразование значения в число.
|
||||
|
||||
Например, у нас есть два числа, в форме строк, и нужно их сложить. Бинарный плюс сложит их как строки, поэтому используем унарный плюс, чтобы преобразовать к числу:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = "2";
|
||||
var b = "3";
|
||||
|
||||
alert( a + b ); // "23", так как бинарный плюс складывает строки
|
||||
alert( +a + b ); // "23", второй операнд - всё ещё строка
|
||||
|
||||
alert( +a + +b); // 5, число, так как оба операнда предварительно преобразованы в числа
|
||||
```
|
||||
|
||||
## Присваивание
|
||||
|
||||
Оператор присваивания выглядит как знак равенства `=`:
|
||||
|
||||
```js
|
||||
var i = 1 + 2;
|
||||
|
||||
alert(i); // 3
|
||||
```
|
||||
|
||||
Он вычисляет выражение, которое находится справа, и присваивает результат переменной. Это выражение может быть достаточно сложным и включать в себя любые другие переменные:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
|
||||
*!*
|
||||
a = b + a + 3; // (*)
|
||||
*/!*
|
||||
|
||||
alert(a); // 6
|
||||
```
|
||||
|
||||
В строке `(*)` сначала произойдет вычисление, использующее текущее значение `a` (т.е. `1`), после чего результат перезапишет старое значение `a`.
|
||||
|
||||
**Возможно присваивание по цепочке:**
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a, b, c;
|
||||
|
||||
*!*
|
||||
a = b = c = 2 + 2;
|
||||
*/!*
|
||||
|
||||
alert(a); // 4
|
||||
alert(b); // 4
|
||||
alert(c); // 4
|
||||
```
|
||||
|
||||
Такое присваивание работает справа-налево, то есть сначала вычислятся самое правое выражение `2+2`, присвоится в `c`, затем выполнится `b = c` и, наконец, `a = b`.
|
||||
|
||||
[smart header="Оператор `\"=\"` возвращает значение"]
|
||||
Все операторы возвращают значение. Вызов `x = выражение` записывает выражение в `x`, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
|
||||
*!*
|
||||
var c = 3 - (a = b + 1);
|
||||
*/!*
|
||||
|
||||
alert(a); // 3
|
||||
alert(c); // 0
|
||||
```
|
||||
|
||||
В примере выше результатом `(a = b + 1)` является значение, которое записывается в `a` (т.е. `3`). Оно используется для вычисления `c`.
|
||||
|
||||
Забавное применение присваивания, не так ли?
|
||||
|
||||
Знать, как это работает -- стоит обязательно, а вот писать самому -- только если вы уверены, что это сделает код более читаемым и понятным.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Приоритет
|
||||
|
||||
В том случае, если в выражении есть несколько операторов - порядок их выполнения определяется *приоритетом*.
|
||||
|
||||
Из школы мы знаем, что умножение в выражении `2 * 2 + 1` выполнится раньше сложения, т.к. его *приоритет* выше, а скобки явно задают порядок выполнения. Но в JavaScript -- гораздо больше операторов, поэтому существует целая [таблица приоритетов](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence).
|
||||
|
||||
Она содержит как уже пройденные операторы, так и те, которые мы еще не проходили. В ней каждому оператору задан числовой приоритет. Тот, у кого число меньше -- выполнится раньше. Если приоритет одинаковый, то порядок выполнения -- слева направо.
|
||||
|
||||
Отрывок из таблицы:
|
||||
|
||||
<table>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
<tr><td>5</td><td>умножение</td><td>`*`</td></tr>
|
||||
<tr><td>5</td><td>деление</td><td>`/`</td></tr>
|
||||
<tr><td>6</td><td>сложение</td><td>`+`</td></tr>
|
||||
<tr><td>6</td><td>вычитание</td><td>`-`</td></tr>
|
||||
<tr><td>17</td><td>присвоение</td><td>`=`</td></tr>
|
||||
<tr><td>...</td><td>...</td><td>...</td></tr>
|
||||
</table>
|
||||
|
||||
Посмотрим на таблицу в действии.
|
||||
|
||||
В выражении `x = 2 * 2 + 1` есть три оператора: присвоение `=`, умножение `*` и сложение `+`. Приоритет умножения `*` равен `5`, оно выполнится первым, затем произойдёт сложение `+`, у которого приоритет `6`, и после них -- присвоение `=`, с приоритетом 17.
|
||||
|
||||
## Инкремент/декремент: ++, --
|
||||
|
||||
Одной из наиболее частых операций в JavaScript, как и во многих других языках программирования, является увеличение или уменьшение переменной на единицу.
|
||||
|
||||
Для этого существуют даже специальные операторы:
|
||||
<ul>
|
||||
<li>**Инкремент** `++` увеличивает на 1:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 2;
|
||||
i++; // более короткая запись для i = i + 1.
|
||||
alert(i); // 3
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>**Декремент** `--` уменьшает на 1:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 2;
|
||||
i--; // более короткая запись для i = i - 1.
|
||||
alert(i); // 1
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
[warn]
|
||||
Инкремент/декремент можно применить только к переменной.
|
||||
Код `5++` даст ошибку.
|
||||
[/warn]
|
||||
|
||||
Вызывать эти операторы можно не только после, но и перед переменной: `i++` (называется "постфиксная форма") или `++i` ("префиксная форма").
|
||||
|
||||
Обе эти формы записи делают одно и то же: увеличивают на `1`.
|
||||
|
||||
Тем не менее, между ними существует разница. Она видна только в том случае, когда мы хотим не только увеличить/уменьшить переменную, но и использовать результат в том же выражении.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
var a = ++i; // (*)
|
||||
|
||||
alert(a); // *!*2*/!*
|
||||
```
|
||||
|
||||
В строке `(*)` вызов `++i` увеличит переменную, а *затем* вернёт ее значение в `a`. **То есть, в `a` попадёт значение `i` *после* увеличения**.
|
||||
|
||||
**Постфиксная форма `i++` отличается от префиксной `++i` тем, что возвращает старое значение, бывшее до увеличения.**
|
||||
|
||||
В примере ниже в `a` попадёт старое значение `i`, равное `1`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
var a = i++; // (*)
|
||||
|
||||
alert(a); // *!*1*/!*
|
||||
```
|
||||
|
||||
<ul>
|
||||
<li>Если результат оператора не используется, а нужно только увеличить/уменьшить переменную -- без разницы, какую форму использовать:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
i++;
|
||||
++i;
|
||||
alert(i); // 2
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если хочется тут же использовать результат, то нужна префиксная форма:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
alert( ++i ); // 1
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если нужно увеличить, но нужно значение переменной *до увеличения* -- постфиксная форма:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 0;
|
||||
alert( i++ ); // 0
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
**Инкремент/декремент можно использовать в любых выражениях.**
|
||||
|
||||
При этом он имеет более высокий приоритет и выполняется раньше, чем арифметические операции:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
alert( 2 * ++i ); // 4
|
||||
```
|
||||
|
||||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
alert( 2 * i++ ); // 2, выполнился раньше но значение вернул старое
|
||||
```
|
||||
|
||||
При этом, нужно с осторожностью использовать такую запись, потому что при чтении кода зачастую неочевидно, что переменая увеличивается. Три строки -- длиннее, зато нагляднее:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var i = 1;
|
||||
alert( 2 * i );
|
||||
i++;
|
||||
```
|
||||
|
||||
## Побитовые операторы
|
||||
|
||||
Побитовые операторы рассматривают аргументы как 32-разрядные целые числа и работают на уровне их внутреннего двоичного представления.
|
||||
|
||||
Эти операторы не являются чем-то специфичным для JavaScript, они поддерживаются в большинстве языков программирования.
|
||||
|
||||
Поддерживаются следующие побитовые операторы:
|
||||
<ul>
|
||||
<li>AND(и) ( `&` )</li>
|
||||
<li>OR(или) ( `|` )</li>
|
||||
<li>XOR(побитовое исключающее или) ( `^` )</li>
|
||||
<li>NOT(не) ( `~` )</li>
|
||||
<li>LEFT SHIFT(левый сдвиг) ( `<<` )</li>
|
||||
<li>RIGHT SHIFT(правый сдвиг) ( `>>` )</li>
|
||||
<li>ZERO-FILL RIGHT SHIFT(правый сдвиг с заполнением нулями) ( `>>>` )</li>
|
||||
</ul>
|
||||
|
||||
Вы можете более подробно почитать о них в отдельной статье [](/bitwise-operators).
|
||||
|
||||
|
||||
|
||||
## Вызов операторов с присваиванием
|
||||
|
||||
Часто нужно применить оператор к переменной и сохранить результат в ней же, например:
|
||||
|
||||
```js
|
||||
var n = 2;
|
||||
n = n + 5;
|
||||
n = n * 2;
|
||||
```
|
||||
|
||||
Эту запись можно укоротить при помощи совмещённых операторов: <code>+=, -=, *=, /=, >>=, <<=, >>>=, &=, |=, ^=</code>.
|
||||
|
||||
Вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var n = 2;
|
||||
n += 5; // теперь n=7 (работает как n = n + 5)
|
||||
n *= 2; // теперь n=14 (работает как n = n * 2)
|
||||
|
||||
alert(n); // 14
|
||||
```
|
||||
|
||||
Все эти операторы имеют в точности такой же приоритет, как обычное присваивание, то есть выполняются после большинства других операций.
|
||||
|
||||
|
||||
|
||||
## Оператор запятая
|
||||
|
||||
Запятая тоже является оператором. Ее можно вызвать явным образом, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
*!*
|
||||
a = (5, 6);
|
||||
*/!*
|
||||
|
||||
alert(a);
|
||||
```
|
||||
|
||||
Запятая позволяет перечислять выражения, разделяя их запятой `','`. Каждое из них -- вычисляется и отбрасывается, за исключением последнего, которое возвращается.
|
||||
|
||||
Запятая -- единственный оператор, приоритет которого ниже присваивания. В выражении `a = (5,6)` для явного задания приоритета использованы скобки, иначе оператор `'='` выполнился бы до запятой `','`, получилось бы `(a=5), 6`.
|
||||
|
||||
Зачем же нужен такой странный оператор, который отбрасывает значения всех перечисленных выражений, кроме последнего?
|
||||
|
||||
Обычно он используется в составе более сложных конструкций, чтобы сделать несколько действий в одной строке. Например:
|
||||
|
||||
```js
|
||||
// три операции в одной строке
|
||||
for (*!*a = 1, b = 3, c = a*b*/!*; a < 10; a++) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Такие трюки используются во многих JavaScript-фреймворках для укорачивания кода.
|
232
01-js/02-first-steps/09-comparison/article.md
Normal file
|
@ -0,0 +1,232 @@
|
|||
# Операторы сравнения и логические значения
|
||||
|
||||
В этом разделе мы познакомимся с операторами сравнения и с логическими значениями, которые такие операторы возвращают.
|
||||
[cut]
|
||||
Многие операторы сравнения знакомы нам со школы:
|
||||
|
||||
<ul>
|
||||
<li>Больше/меньше: <code>a > b</code>, <code>a < b</code>.</li>
|
||||
<li>Больше/меньше или равно: <code>a >= b</code>, <code>a <= b</code>.</li>
|
||||
<li>Равно `a == b`.
|
||||
Для сравнения используется два символа равенства `'='`. Один символ `a = b` означал бы присваивание.</li>
|
||||
<li>"Не равно". В школе он пишется как <code>≠</code>, в JavaScript -- знак равенства с восклицательным знаком перед ним <code>!=</code>.</li>
|
||||
</ul>
|
||||
|
||||
## Логические значения
|
||||
|
||||
Как и другие операторы, сравнение возвращает значение. Это значение имеет специальный *логический* тип.
|
||||
|
||||
Существует всего два логических значения:
|
||||
<ul>
|
||||
<li>`true` -- имеет смысл "да", "верно", "истина".</li>
|
||||
<li>`false` -- означает "нет", "неверно", "ложь".</li>
|
||||
</ul>
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 2 > 1 ); // true, верно
|
||||
alert( 2 == 1 ); // false, неверно
|
||||
alert( 2 != 1 ); // true
|
||||
```
|
||||
|
||||
Логические значения можно использовать и напрямую, присваивать переменным, работать с ними как с любыми другими:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = true; // присвоили явно
|
||||
var b = 3 > 4; // false
|
||||
|
||||
alert( b ); // false
|
||||
|
||||
alert( a == b ); // (true == false) неверно, результат false
|
||||
```
|
||||
|
||||
## Сравнение строк
|
||||
|
||||
**Строки сравниваются побуквенно:**
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Б' > 'А' ); // true
|
||||
```
|
||||
|
||||
[warn header="Осторожно, Unicode!"]
|
||||
Аналогом "алфавита" во внутреннем представлении строк служит кодировка, у каждого символа -- свой номер (код). JavaScript использует кодировку [Unicode](http://ru.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4).
|
||||
|
||||
При этом сравниваются *численные коды символов*. В частности, код у символа `Б` больше, чем у `А`, поэтому и результат сравнения такой.
|
||||
|
||||
**В кодировке Unicode обычно код у строчной буквы больше, чем у прописной.**
|
||||
|
||||
Поэтому регистр имеет значение:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert('а' > 'Я'); // true, строчные буквы больше прописных
|
||||
```
|
||||
|
||||
Для корректного сравнения символы должны быть в одинаковом регистре.
|
||||
[/warn]
|
||||
|
||||
Если строка состоит из нескольких букв, то сравнение осуществляется как в телефонной книжке или в словаре. Сначала сравниваются первые буквы, потом вторые, и так далее, пока одна не будет больше другой.
|
||||
|
||||
Иными словами, больше -- та строка, которая в телефонной книге была бы на большей странице.
|
||||
|
||||
Например:
|
||||
<ul>
|
||||
<li>Если первая буква первой строки больше -- значит первая строка больше, независимо от остальных символов:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Банан' > 'Аят' );
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если одинаковы -- сравнение идёт дальше. Здесь оно дойдёт до третьей буквы:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Вася' > 'Ваня' ); // true, т.к. 'с' > 'н'
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>При этом любая буква больше отсутствия буквы:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Привет' > 'Прив' ); // true, так как 'е' больше чем "ничего".
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
Такое сравнение называется *лексикографическим*.
|
||||
|
||||
|
||||
[warn]
|
||||
Обычно мы получаем значения от посетителя в виде строк. Например, `prompt` возвращает *строку*, которую ввел посетитель.
|
||||
|
||||
Числа, полученные таким образом, в виде строк сравнивать нельзя, результат будет неверен. Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "2" > "14" ); // true, неверно, ведь 2 не больше 14
|
||||
```
|
||||
|
||||
В примере выше `2` оказалось больше `14`, потому что строки сравниваются посимвольно, а первый символ `'2'` больше `'1'`.
|
||||
|
||||
Правильно было бы преобразовать их к числу явным образом. Например, поставив перед ними `+`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( +"2" > +"14" ); // false, теперь правильно
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
## Сравнение разных типов
|
||||
|
||||
**При сравнении значения преобразуются к числам. Исключение: когда оба значения -- строки, тогда не преобразуются.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '2' > 1 ); // true
|
||||
alert( '01' == 1 ); //true
|
||||
alert( false == 0 ); // true, false становится 0, а true 1.
|
||||
```
|
||||
|
||||
Тема преобразований типов будет продолжена далее, в главе [](/types-conversion).
|
||||
|
||||
## Строгое равенство
|
||||
|
||||
Обычное равенство не может отличить `0` от `false`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(0 == false); // true, так как false преобразуется к 0
|
||||
```
|
||||
|
||||
Что же делать, если всё же нужно отличить `0` от `false`?
|
||||
|
||||
**Для проверки равенства без преобразования типов используются операторы строгого равенства `===` (тройное равно) и `!==`.**
|
||||
|
||||
Они сравнивают без приведения типов. Если тип разный, то такие значения всегда неравны (строго):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(0 === false); // false, т.к. типы различны
|
||||
```
|
||||
|
||||
Строгое сравнение предпочтительно, если мы хотим быть уверены, что "сюрпризов" не будет.
|
||||
|
||||
## Сравнение с null и undefined
|
||||
|
||||
Проблемы со специальными значениями возможны, когда к переменной применяется операция сравнения `> < <= >=`, а у неё может быть как численное значение, так и `null/undefined`.
|
||||
|
||||
**Интуитивно кажется, что `null/undefined` эквивалентны нулю, но это не так! Они ведут себя по-другому.**
|
||||
|
||||
<ol>
|
||||
<li>**Значения `null` и `undefined` равны `==` друг другу и не равны чему бы то ни было ещё.**
|
||||
Это жёсткое правило буквально прописано в спецификации языка.</li>
|
||||
<li>**При преобразовании в число `null` становится `0`, а `undefined` становится `NaN`.**</li>
|
||||
</ol>
|
||||
|
||||
Посмотрим забавные следствия.
|
||||
|
||||
[smart header="Некорректный результат сравнения `null` с `0`"]
|
||||
Сравним `null` с нулём:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(null > 0); // false
|
||||
alert(null == 0); // false
|
||||
```
|
||||
|
||||
Итак, мы получили, что `null` не больше и не равен нулю. А теперь...
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(null >= 0); // *!*true*/!*
|
||||
```
|
||||
|
||||
Как такое возможно? Если нечто *"больше или равно нулю"*, то резонно полагать, что оно либо *больше*, либо *равно*. Но здесь это не так.
|
||||
|
||||
Дело в том, что алгоритмы проверки равенства `==` и сравнения `>= > < <=` работают по-разному.
|
||||
|
||||
Сравнение честно приводит к числу, получается ноль. А при проверке равенства значения `null` и `undefined` обрабатываются особым образом: они равны друг другу, но не равны чему-то ещё.
|
||||
|
||||
В результате получается странная с точки зрения здравого смысла ситуация, которую мы видели в примере выше.
|
||||
|
||||
[/smart]
|
||||
|
||||
[smart header="Несравнимый `undefined`"]
|
||||
Значение `undefined` вообще нельзя сравнивать:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert(undefined > 0); // false (1)
|
||||
alert(undefined < 0); // false (2)
|
||||
alert(undefined == 0); // false (3)
|
||||
```
|
||||
|
||||
<ul>
|
||||
<li>Сравнения `(1)` и `(2)` дают `false` потому, что `undefined` при преобразовании к числу даёт `NaN`. А значение `NaN` по стандарту устроено так, что сравнения `==`, `<`, `>`, `<=`, `>=` и даже `===` с ним возвращают `false`.</li>
|
||||
<li>Проверка равенства `(3)` даёт `false`, потому что в стандарте явно прописано, что `undefined` равно лишь `null` и ничему другому.</li>
|
||||
</ul>
|
||||
[/smart]
|
||||
|
||||
**Вывод: любые сравнения с `undefined/null`, кроме точного `===`, следует делать с осторожностью.**
|
||||
|
||||
Желательно не использовать сравнения `>= > < <=` с ними, во избежание ошибок в коде.
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>В JavaScript есть логические значения `true` (истина) и `false` (ложь). Операторы сравнения возвращают их.</li>
|
||||
<li>Строки сравниваются побуквенно.</li>
|
||||
<li>Значения разных типов приводятся к числу при сравнении, за исключением строгого равенства `===` (`!==`).</li>
|
||||
<li>Значения `null` и `undefined` равны `==` друг другу и не равны ничему другому. В других сравнениях (с участием `>`,`<`) их лучше не использовать, так как они ведут себя не как `0`.</li>
|
||||
</ul>
|
|
@ -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
01-js/02-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
01-js/02-first-steps/11-uibasic/01-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
01-js/02-first-steps/11-uibasic/01-simple-page/task.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Простая страница
|
||||
|
||||
[importance 4]
|
||||
|
||||
Создайте страницу, которая спрашивает имя и выводит его.
|
||||
|
||||
[demo /]
|
||||
|
110
01-js/02-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
01-js/02-first-steps/12-ifelse/01-if-zero-string/solution.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Да, выведется,** т.к. внутри `if` стоит строка `"0"`.
|
||||
|
||||
Любая строка, кроме пустой (а здесь она не пустая), в логическом контексте является `true`.
|
||||
|
||||
Можно запустить и проверить:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
if ("0") {
|
||||
alert('Привет');
|
||||
}
|
||||
```
|
||||
|
12
01-js/02-first-steps/12-ifelse/01-if-zero-string/task.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# if (строка с нулём)
|
||||
|
||||
[importance 5]
|
||||
|
||||
Выведется ли `alert`?
|
||||
|
||||
```js
|
||||
if ("0") {
|
||||
alert('Привет');
|
||||
}
|
||||
```
|
||||
|
BIN
01-js/02-first-steps/12-ifelse/02-check-standard/ifelse_task2.png
Executable file
After Width: | Height: | Size: 21 KiB |
18
01-js/02-first-steps/12-ifelse/02-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
01-js/02-first-steps/12-ifelse/02-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
01-js/02-first-steps/12-ifelse/02-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
01-js/02-first-steps/12-ifelse/03-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
01-js/02-first-steps/12-ifelse/03-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
01-js/02-first-steps/12-ifelse/03-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
01-js/02-first-steps/12-ifelse/04-check-login/ifelse_task.png
Executable file
After Width: | Height: | Size: 32 KiB |
33
01-js/02-first-steps/12-ifelse/04-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
01-js/02-first-steps/12-ifelse/04-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) ? 'Мало' : 'Много';
|
||||
```
|
||||
|
|
@ -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
01-js/02-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
01-js/02-first-steps/13-logical-ops/02-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
01-js/02-first-steps/13-logical-ops/02-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 );
|
||||
```
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Что выведет alert (И)?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Что выведет код ниже?
|
||||
|
||||
```js
|
||||
alert( 1 && null && 2 );
|
||||
```
|
||||
|
10
01-js/02-first-steps/13-logical-ops/04-alert-and/solution.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
Ответ: `1`, а затем `undefined`.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( alert(1) && alert(2) );
|
||||
```
|
||||
|
||||
Вызов `alert` не возвращает значения, или, иначе говоря, возвращает `undefined`.
|
||||
|
||||
Поэтому до правого `alert` дело не дойдёт, вычисления закончатся на левом.
|
10
01-js/02-first-steps/13-logical-ops/04-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 включительно.
|
||||
|
||||
Сделайте два варианта условия: первый с использованием оператора НЕ `!`, второй - без этого оператора.
|
|
@ -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
01-js/02-first-steps/13-logical-ops/07-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
01-js/02-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
01-js/02-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
01-js/02-first-steps/15-while-for/01-loop-last-value/task.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Последнее значение цикла
|
||||
|
||||
[importance 3]
|
||||
|
||||
Какое последнее значение выведет этот код? Почему?
|
||||
|
||||
```js
|
||||
var i = 3;
|
||||
|
||||
while(i) {
|
||||
alert(i--);
|
||||
}
|
||||
```
|
||||
|