`table.caption/tHead/tFoot` -- ссылки на элементы таблицы `CAPTION`, `THEAD`, `TFOOT`.
-
`table.tBodies` -- список элементов таблицы `TBODY`, по спецификации их может быть несколько.
+
`table.tBodies` -- коллекция элементов таблицы `TBODY`, по спецификации их может быть несколько.
`THEAD/TFOOT/TBODY`
-
`tbody.rows` -- список строк `TR` секции.
+
`tbody.rows` -- коллекция строк `TR` секции.
`TR`
-
**`tr.cells`** -- список ячеек `TD/TH`
+
**`tr.cells`** -- коллекция ячеек `TD/TH`
**`tr.sectionRowIndex`** -- номер строки в текущей секции `THEAD/TBODY`
`tr.rowIndex` -- номер строки в таблице
diff --git a/2-ui/1-document/5-searching-elements-dom/2-tree/solution.md b/2-ui/1-document/5-searching-elements-dom/2-tree/solution.md
deleted file mode 100644
index 55b45737..00000000
--- a/2-ui/1-document/5-searching-elements-dom/2-tree/solution.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Сделаем цикл по узлам `
`:
-
-```js
-var lis = document.getElementsByTagName('li');
-
-for (i = 0; i < lis.length; i++) {
- ...
-}
-```
-
-В цикле для каждого `lis[i]` можно получить текст, используя свойство `firstChild`. Ведь первым в `
` является как раз текстовый узел, содержащий текст названия.
-
-Также можно получить количество потомков, используя `lis[i].getElementsByTagName('li')`.
-
-Напишите код с этой подсказкой.
-
-Если уж не выйдет -- тогда откройте решение.
-
diff --git a/2-ui/1-document/5-searching-elements-dom/2-tree/solution.view/index.html b/2-ui/1-document/5-searching-elements-dom/2-tree/solution.view/index.html
deleted file mode 100644
index e9b5d644..00000000
--- a/2-ui/1-document/5-searching-elements-dom/2-tree/solution.view/index.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
Животные
-
-
Млекопитающие
-
-
Коровы
-
Ослы
-
Собаки
-
Тигры
-
-
-
Другие
-
-
Змеи
-
Птицы
-
Ящерицы
-
-
-
-
-
Рыбы
-
-
Аквариумные
-
-
Гуппи
-
Скалярии
-
-
-
-
Морские
-
-
Морская форель
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/2-ui/1-document/5-searching-elements-dom/2-tree/source.view/index.html b/2-ui/1-document/5-searching-elements-dom/2-tree/source.view/index.html
deleted file mode 100644
index 2f45460c..00000000
--- a/2-ui/1-document/5-searching-elements-dom/2-tree/source.view/index.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
Животные
-
-
Млекопитающие
-
-
Коровы
-
Ослы
-
Собаки
-
Тигры
-
-
-
Другие
-
-
Змеи
-
Птицы
-
Ящерицы
-
-
-
-
-
Рыбы
-
-
Аквариумные
-
-
Гуппи
-
Скалярии
-
-
-
-
Морские
-
-
Морская форель
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/2-ui/1-document/5-searching-elements-dom/2-tree/task.md b/2-ui/1-document/5-searching-elements-dom/2-tree/task.md
deleted file mode 100644
index 91980462..00000000
--- a/2-ui/1-document/5-searching-elements-dom/2-tree/task.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Дерево
-
-[importance 5]
-
-Есть дерево [edit src="source"]в песочнице[/edit].
-
-Напишите код, который для каждого элемента списка(`LI`) выведет:
-
-
Текст в нём.
-
Количество вложенных в него элементов.
-
-
-[demo src="solution"]
-
diff --git a/2-ui/1-document/6-searching-elements-internals/article.md b/2-ui/1-document/6-searching-elements-internals/article.md
index bc5c7cfa..d2935b7c 100644
--- a/2-ui/1-document/6-searching-elements-internals/article.md
+++ b/2-ui/1-document/6-searching-elements-internals/article.md
@@ -52,11 +52,11 @@
```
-Как видно, длина коллекции, найденной через `querySelectorAll`, осталась прежней. А длина списка, возвращённого `getElementsByTagName`, изменилась.
+Как видно, длина коллекции, найденной через `querySelectorAll`, осталась прежней. А длина коллекции, возвращённой `getElementsByTagName`, изменилась.
-Дело в том, что результат запросов `getElementsBy*` -- это не массив, а специальный объект, имеющий тип NodeList или HTMLCollection. Он похож на массив, так как имеет нумерованные элементы и длину, но внутри это не готовый список, а "живой поисковой запрос".
+Дело в том, что результат запросов `getElementsBy*` -- это не массив, а специальный объект, имеющий тип NodeList или HTMLCollection. Он похож на массив, так как имеет нумерованные элементы и длину, но внутри это не готовая коллекция, а "живой поисковой запрос".
-Собственно поиск выполняется только при обращении к элементам списка или к его длине.
+Собственно поиск выполняется только при обращении к элементам коллекции или к её длине.
## Алгоритмы getElementsBy*
@@ -132,7 +132,7 @@ alert( elems.length );
Если точнее -- будут сброшены все коллекции, привязанные к элементам вверх по иерархии от непосредственного родителя нового `div` и выше, то есть такие, которые потенциально могли измениться. И только они.
Во-вторых, если добавлен только `div`, то не будут сброшены запомненные элементы для поиска по другим тегам, например `elem.getElementsByTagName('a')`.
-
...И, конечно же, не любые изменения DOM приводят к сбросу кешей, а только те, которые могут повлиять на список. Если где-то добавлен новый атрибут -- с поиском по тегу ничего не произойдёт.
+
...И, конечно же, не любые изменения DOM приводят к сбросу кешей, а только те, которые могут повлиять на коллекцию. Если где-то добавлен новый атрибут элементу -- с кешем для `getElementsByTagName` ничего не произойдёт, так как атрибут никак не может повлиять на результат поиска по тегу.
Прочие поисковые методы, такие как `getElementsByClassName` тоже сбрасывают кеш при изменениях интеллектуально.
diff --git a/4-ajax/6-xhr-onprogress/article.md b/4-ajax/6-xhr-onprogress/article.md
index f4e5d844..07f532c9 100644
--- a/4-ajax/6-xhr-onprogress/article.md
+++ b/4-ajax/6-xhr-onprogress/article.md
@@ -2,21 +2,19 @@
Запрос `XMLHttpRequest` состоит из двух фаз:
-
Стадия закачки (upload). На ней данные загружаются на сервер. Эта фаза может быть долгой для POST-запросов.
-
Стадия скачивания (download). После того, как данные загружены, браузер скачивает ответ с сервера. Если он большой, то это может занять существенное время.
+
Стадия закачки (upload). На ней данные загружаются на сервер. Эта фаза может быть долгой для POST-запросов. Для отслеживания прогресса на стадии закачки существует объект типа [XMLHttpRequestUpload](https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget), доступный как `xhr.upload` и события на нём.
+
Стадия скачивания (download). После того, как данные загружены, браузер скачивает ответ с сервера. Если он большой, то это может занять существенное время. На этой стадии используется обработчик `xhr.onprogress`.
-Для каждой стадии предусмотрены события, "рассказывающие" о процессе выполнения.
+Далее -- обо всём по порядку.
[cut]
-## XMLHttpRequestUpload
+## Стадия закачки
-Для отслеживания прогресса на стадии закачки существует объект [XMLHttpRequestUpload](https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget), доступный как `xhr.upload`.
+На стадии закачки для получения информации используем объект `xhr.upload`. У этого объекта нет методов, он только генерирует события в процессе закачки. А они-то как раз и нужны.
-У него нет методов, он предназначен исключительно для обработки событий при закачке.
-
-Вот их полный список:
+Вот полный список событий:
`loadstart`
`progress`
@@ -43,9 +41,13 @@ xhr.upload.onerror = function() {
}
```
-После того, как загрузка завершена, будет начато скачивание ответа.
+## Стадия скачивания
-На этой фазе `xhr.upload` уже не нужен, а в дело вступают обработчики событий на самом объекте `xhr`, в частности `xhr.onprogress`:
+После того, как загрузка завершена, и сервер соизволит ответить на запрос, `XMLHttpRequest` начнёт скачивание ответа сервера.
+
+На этой фазе `xhr.upload` уже не нужен, а в дело вступают обработчики событий на самом объекте `xhr`. В частности, событие `xhr.onprogress` содержит информацию о количестве принятых байт ответа.
+
+Пример обработчика:
```js
xhr.onprogress = function(event) {
@@ -55,13 +57,13 @@ xhr.onprogress = function(event) {
Все события, возникающие в этих обработчиках, имеют тип [ProgressEvent](https://xhr.spec.whatwg.org/#progressevent), то есть имеют свойства `loaded` -- количество уже пересланных данных в байтах и `total` -- общее количество данных.
-## Загрузка файла с индикатором прогресса
+## Демо: загрузка файла с индикатором прогресса
Современный `XMLHttpRequest` позволяет отправить на сервер всё, что угодно. Текст, файл, форму.
Мы, для примера, рассмотрим загрузку файла с индикацией прогресса. Это требует от браузера поддержки [File API](http://www.w3.org/TR/FileAPI/), то есть исключает IE9-.
-File API позволяет получить доступ к содержимому файла, который перенесён в браузер при помощи Drag'n'Drop или выбран в поле формы.
+File API позволяет получить доступ к содержимому файла, который перенесён в браузер при помощи Drag'n'Drop или выбран в поле формы, и отправить его при помощи `XMLHttpRequest`.
Форма для выбора файла с обработчиком `submit`:
@@ -73,7 +75,8 @@ File API позволяет получить доступ к содержимо
```
-Здесь мы почти не пользуемся File API. Его роль -- получить файл из формы через свойство `files` элемента `` и далее мы сразу передадим его `xhr.send` в функции `upload`:
+Мы получаем файл из формы через свойство `files` элемента `` и передаём его в функцию `upload`:
```js
function upload(file) {
var xhr = new XMLHttpRequest();
- // обработчики можно объединить в один,
+ // обработчик для закачки
+ xhr.upload.onprogress = function(event) {
+ log(event.loaded + ' / ' + event.total);
+ }
+
+ // обработчики успеха и ошибки
// если status == 200, то это успех, иначе ошибка
xhr.onload = xhr.onerror = function() {
if (this.status == 200) {
@@ -101,10 +109,6 @@ function upload(file) {
}
};
- // обработчик для закачки
- xhr.upload.onprogress = function(event) {
- log(event.loaded + ' / ' + event.total);
- }
xhr.open("POST", "upload", true);
xhr.send(file);
@@ -112,15 +116,21 @@ function upload(file) {
}
```
+Этот код отправит файл на сервер и будет сообщать о прогрессе при его закачке (`xhr.upload.onprogress`), а также об окончании запроса (`xhr.onload`, `xhr.onerror`).
+
Полный пример, основанный на коде выше:
[codetabs src="progress"]
## Событие onprogress в деталях
-Событие `onprogress` имеет одинаковый вид при закачке на сервер (`xhr.upload.onprogress`) и при получении ответа (`xhr.onprogress`).
+При обработке события `onprogress` есть ряд важных тонкостей.
-Оно получает объект `event` типа [ProgressEvent](https://xhr.spec.whatwg.org/#progressevent) со свойствами:
+Можно, конечно, их игнорировать, но лучше бы знать.
+
+Заметим, что событие, возникающее при `onprogress`, имеет одинаковый вид на стадии закачки (в обработчике `xhr.upload.onprogress`) и при получении ответа (в обработчике `xhr.onprogress`).
+
+Оно представляет собой объект типа [ProgressEvent](https://xhr.spec.whatwg.org/#progressevent) со свойствами:
`loaded`
@@ -132,10 +142,11 @@ function upload(file) {
`total`
Общее количество байт для пересылки, если известно.
-При HTTP-запросах оно передаётся в заголовке `Content-Length`.
+А может ли оно быть неизвестно?
+
-
При закачке на сервер браузер всегда знает полный размер пересылаемых данных. Поэтому в `xhr.upload.onprogress` значение `lengthComputable` всегда будет `true`, а `total` -- содержать этот размер.
-
При скачивании данных -- уже задача сервера поставить этот заголовок. Если его нет, то `total = 0`, а для того, чтобы понять, что данных не `0`, а неизвестное количество -- служит `lengthComputable`, которое в данном случае равно `false`.
+
При закачке на сервер браузер всегда знает полный размер пересылаемых данных, так что `total` всегда содержит конкретное количество байт, а значение `lengthComputable` всегда будет `true`.
+
При скачивании данных -- обычно сервер в начале сообщает их общее количество в HTTP-заголовке `Content-Length`. Но он может и не делать этого, например если сам не знает, сколько данных будет или если генерирует их динамически. Тогда `total` будет равно `0`. А чтобы отличить нулевой размер данных от неизвестного -- как раз служит `lengthComputable`, которое в данном случае равно `false`.
@@ -147,10 +158,10 @@ function upload(file) {
Это обозначено в [спецификации progress notifications](http://www.w3.org/TR/XMLHttpRequest/#make-progress-notifications).
-
**При получении данных доступен `xhr.responseText`.**
+
**В процессе получения данных, ещё до их полной передачи, доступен `xhr.responseText`.**
Можно до окончания запроса заглянуть в него и прочитать текущие полученные данные. Важно, что при пересылке строки в кодировке UTF-8 русские символы кодируются 2 байтами. Возможно, что в конце одного пакета данных окажется первая половинка символа, а в начале следующего -- вторая. Поэтому полагаться на то, что до окончания запроса в `responoseText` находится корректная строка нельзя. Исключение -- заведомо однобайтные символы, например цифры.
-
**При закачки `xhr.upload.onprogress` не гарантирует обработку загрузки сервером.**
+
**Сработавшее событие `xhr.upload.onprogress` не гарантирует, что данные дошли.**
Событие `xhr.upload.onprogress` срабатывает, когда данные отправлены браузером. Но оно не гарантирует, что сервер получил, обработал и записал данные на диск. Он говорит лишь о самом факте отправки.
@@ -174,5 +185,5 @@ formData.append("myfile", file);
xhr.send(formData);
```
-Данные будут отправлены в кодировке `multipart/form-data`. Серверный фреймворк обработает это как обычную форму.
+Данные будут отправлены в кодировке `multipart/form-data`. Серверный фреймворк увидит это как обычную форму с файлом. Практически все серверные технологии имеют их встроенную поддержку.
diff --git a/4-ajax/8-xhr-longpoll/article.md b/4-ajax/8-xhr-longpoll/article.md
index 717f2f3c..0d31af5f 100644
--- a/4-ajax/8-xhr-longpoll/article.md
+++ b/4-ajax/8-xhr-longpoll/article.md
@@ -1,4 +1,4 @@
-# XMLHttpRequest: длинные опросы
+# COMET с XMLHttpRequest: длинные опросы
В этой главе мы рассмотрим способ организации COMET, то есть непрерывного получения данных с сервера, который очень прост и подходит в 90% реальных случаев.
diff --git a/6-optimize/1-memory-leaks/article.md b/6-optimize/1-memory-leaks/article.md
deleted file mode 100644
index 08ed1610..00000000
--- a/6-optimize/1-memory-leaks/article.md
+++ /dev/null
@@ -1,317 +0,0 @@
-# Утечки памяти
-
-*Утечки памяти* происходят, когда браузер по какой-то причине не может освободить память от недостижимых объектов.
-
-Обычно это происходит автоматически ([](/memory-management)). Кроме того, браузер освобождает память при переходе на другую страницу. Поэтому утечки в реальной жизни проявляют себя в двух ситуациях:
-
-[cut]
-
-
Приложение, в котором посетитель все время на одной странице и работает со сложным JavaScript-интерфейсом. В этом случае утечки могут постепенно съедать доступную память.
-
Страница регулярно делает что-то, вызывающее утечку памяти. Посетитель (например, менеджер) оставляет компьютер на ночь включенным, чтобы не закрывать браузер с кучей вкладок. Приходит утром -- а браузер съел всю память и рухнул и сильно тормозит.
-
-Утечки бывают из-за ошибок браузера, ошибок в расширениях браузера и, гораздо реже, по причине ошибок в архитектуре JavaScript-кода. Мы разберём несколько наиболее частых и важных примеров.
-
-## Коллекция утечек в IE
-
-### Утечка DOM ↔ JS в IE8-
-
-IE до версии 8 не умел очищать циклические ссылки, появляющиеся между DOM-объектами и объектами JavaScript. В результате и DOM и JS оставались в памяти навсегда.
-
-В браузере IE8 была проведена серьёзная работа над ошибками, но утечка в IE8- появляется, если круговая ссылка возникает "через объект".
-
-Чтобы было понятнее, о чём речь, посмотрите на следующий код. Он вызывает утечку памяти в IE8-:
-
-```
-function leak() {
- // Создаём новый DIV, добавляем к BODY
- var elem = document.createElement('div');
- document.body.appendChild(elem);
-
- // Записываем в свойство жирный объект
- elem.__expando = {
- bigAss: new Array(1000000).join('lalala')
- };
-
-*!*
- // Создаём круговую ссылку. Без этой строки утечки не будет.
- elem.__expando.__elem = elem;
-*/!*
-
- // Удалить элемент из DOM. Браузер должен очистить память.
- elem.parentElement.removeChild(elem);
-}
-```
-
-Полный пример (только для IE8-, а также IE9 в режиме совместимости с IE8):
-
-[codetabs src="leak-ie8"]
-
-
-Круговая ссылка и, как следствие, утечка может возникать и неявным образом, через замыкание:
-
-```js
-function leak() {
- var elem = document.createElement('div');
- document.body.appendChild(elem);
-
- elem.__expando = {
- bigAss: new Array(1000000).join('lalala'),
-*!*
- method: function() {} // создаётся круговая ссылка через замыкание
-*/!*
- };
-
- // Удалить элемент из DOM. Браузер должен очистить память.
- elem.parentElement.removeChild(elem);
-}
-```
-
-Полный пример (IE8-, IE9 в режиме совместимости с IE8):
-
-[codetabs src="leak-ie8-2"]
-
-Без привязки метода `method` к элементу здесь утечки не возникнет.
-
-Бывает ли такая ситуация в реальной жизни? Или это -- целиком синтетический пример, для заумных программистов?
-
-Да, конечно бывает. Например, при разработке графических компонент -- бывает удобно присвоить DOM-элементу ссылку на JavaScript-объект, который представляет собой компонент. Это упрощает делегирование и, в общем-то, логично, что DOM-элемент знает о компоненте на себе. Но в IE8- прямая привязка ведёт к утечке памяти!
-
-Примерно так:
-```js
-function Menu(elem) {
- elem.onclick = function() {};
-}
-
-var menu = new Menu(elem); // Menu содержит ссылку на elem
-*!*
-elem.menu = menu; // такая привязка или что-то подобное ведёт к утечке в IE8
-*/!*
-```
-
-Полный пример (IE8-, IE9 в режиме совместимости с IE8):
-
-[codetabs src="leak-ie8-widget"]
-
-### Утечка IE8 при обращении к коллекциям таблицы
-
-Эта утечка происходит только в IE8 в стандартном режиме. В нём при обращении к табличным псевдо-массивам (напр. `rows`) создаются и не очищаются внутренние ссылки, что приводит к утечкам.
-
-Также воспроизводится в новых IE в режиме совместимости с IE8.
-
-Код:
-
-```js
-var elem = document.createElement('div'); // любой элемент
-
-function leak() {
-
- elem.innerHTML = '
1
';
-
-*!*
- elem.firstChild.rows[0]; // просто доступ через rows[] приводит к утечке
- // при том, что мы даже не сохраняем значение в переменную
-*/!*
-
- elem.removeChild(elem.firstChild); // удалить таблицу (*)
- // alert(elem.childNodes.length) // выдал бы 0, elem очищен, всё честно
-}
-```
-
-Полный пример (IE8):
-
-[codetabs src="leak-ie8-table"]
-
-Особенности:
-
-
Если убрать отмеченную строку, то утечки не будет.
-
Если заменить строку `(*)` на `elem.innerHTML = ''`, то память будет очищена, т.к. этот способ работает по-другому, нежели просто `removeChild` (см. главу [](/memory-management)).
-
Утечка произойдёт не только при доступе к `rows`, но и к другим свойствам, например `elem.firstChild.tBodies[0]`.
-
-
-Эта утечка проявляется, в частности, при удалении детей элемента следующей функцией:
-
-```js
-function empty(elem) {
- while (elem.firstChild) elem.removeChild(elem.firstChild);
-}
-```
-
-Если идёт доступ к табличным коллекциям и регулярное обновление таблиц при помощи DOM-методов -- утечка в IE8 будет расти.
-
-Более подробно вы можете почитать об этой утечке в статье [Утечки памяти в IE8, или страшная сказка со счастливым концом](http://habrahabr.ru/post/141451/).
-
-### Утечка через XmlHttpRequest в IE8-
-
-Следующий код вызывает утечки памяти в IE8-:
-
-```js
-function leak() {
- var xhr = new XMLHttpRequest();
-
- xhr.open('GET', '/server.do', true);
-
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4 && xhr.status == 200) {
- // ...
- }
- }
-
- xhr.send(null);
-}
-```
-
-Как вы думаете, почему? Если вы внимательно читали то, что написано выше, то имеете информацию для ответа на этот вопрос..
-
-Посмотрим, какая структура памяти создается при каждом запуске:
-
-
-
-Когда запускается асинхронный запрос `xhr`, браузер создаёт специальную внутреннюю ссылку (internal reference) на этот объект. находится в процессе коммуникации. Именно поэтому объект `xhr` будет жив после окончания работы функции.
-
-Когда запрос завершен, браузер удаляет внутреннюю ссылку, `xhr` становится недостижимым и память очищается... Везде, кроме IE8-.
-
-
-Полный пример (IE8):
-
-[codetabs src="leak-ie8-xhr"]
-
-Чтобы это исправить, нам нужно разорвать круговую ссылку `XMLHttpRequest ↔ JS`. Например, можно удалить `xhr` из замыкания:
-
-```js
-function leak() {
- var xhr = new XMLHttpRequest();
-
- xhr.open('GET', 'something.js?' + Math.random(), true);
-
- xhr.onreadystatechange = function() {
- if (xhr.readyState != 4) return;
-
- if (xhr.status == 200) {
- document.getElementById('test').innerHTML++;
- }
-
-*!*
- xhr = null; // по завершении запроса удаляем ссылку из замыкания
-*/!*
- }
-
- xhr.send(null);
-}
-```
-
-
-
-Теперь циклической ссылки нет -- и не будет утечки.
-
-
-## Объемы утечек памяти
-
-Объем "утекающей" памяти может быть небольшим. Тогда это почти не ощущается. Но так как замыкания ведут к сохранению переменных внешних функций, то одна функция может тянуть за собой много чего ещё.
-
-Представьте, вы создали функцию, и одна из ее переменных содержит очень большую по объему строку (например, получает с сервера).
-
-```js
-function f() {
- var data = "Большой объем данных, например, переданных сервером"
-
- /* делаем что-то хорошее (ну или плохое) с полученными данными */
-
- function inner() {
- // ...
- }
-
- return inner;
-}
-```
-
-Пока функция `inner` остается в памяти, `LexicalEnvironment` с переменной большого объема внутри висит в памяти.
-
-Висит до тех пор, пока функция `inner` жива.
-
-Как правило, JavaScript не знает, какие из переменных функции `inner` будут использованы, поэтому оставляет их все. Исключение -- виртуальная машина V8 (Chrome, Opera, Node.JS), она часто (не всегда) видит, что переменная не используется во внутренних функциях, и очистит память.
-
-В других же интерпретаторах, даже если код спроектирован так, что никакой утечки нет, по вполне разумной причине может создаваться множество функций, а память будет расти потому, что функция тянет за собой своё замыкание.
-
-Сэкономить память здесь вполне можно. Мы же знаем, что переменная `data` не используется в `inner`. Поэтому просто обнулим её:
-
-```js
-function f() {
- var data = "Большое количество данных, например, переданных сервером"
-
- /* действия с data */
-
- function inner() {
- // ...
- }
-
-*!*
- data = null; // когда data станет не нужна -
-*/!*
-
- return inner;
-}
-```
-
-## Поиск и устранение утечек памяти
-
-### Проверка на утечки
-
-Существует множество шаблонов утечек и ошибок в браузерах, которые могут приводить к утечкам. Для их устранения сперва надо постараться изолировать и воспроизвести утечку.
-
-
-
**Необходимо помнить, что браузер может очистить память не сразу когда объект стал недостижим, а чуть позже.** Например, сборщик мусора может ждать, пока не будет достигнут определенный лимит использования памяти, или запускаться время от времени.
-
-Поэтому если вы думаете, что нашли проблему и тестовый код, запущенный в цикле, течёт -- подождите примерно минуту, добейтесь, чтобы памяти ело стабильно и много. Тогда будет понятно, что это не особенность сборщика мусора.
**Если речь об IE, то надо смотреть "Виртуальную память" в списке процессов, а не только обычную "Память".** Обычная может очищаться за счет того, что перемещается в виртуальную (на диск).
-
Для простоты отладки, если есть подозрение на утечку конкретных объектов, в них добавляют большие свойства-маркеры. Например, подойдет фрагмент текста: `new Array(999999).join('leak')`.
-
-
-### Настройка браузера
-
-Утечки могут возникать из-за расширений браузера, взимодействющих со страницей. Еще более важно, что **утечки могут быть следствием конфликта двух браузерных расширений** Например, было такое: память текла когда включены расширения Skype и плагин антивируса одновременно.
-
-Чтобы понять, в расширениях дело или нет, нужно отключить их:
-
-
-
Отключить Flash.
-
Отключить анивирусную защиту, проверку ссылок и другие модули и дополнения.
-
Отключить плагины. Отключить ВСЕ плагины.
-
-
- Для IE есть параметр коммандной строки:
-
-```
-"C:\Program Files\Internet Explorer\iexplore.exe" -extoff
-```
-
-Кроме того необходимо отключить сторонние расширения в свойствах IE.
-
-
-
-
-
-
Firefox необходимо запускать с чистым профилем. Используйте следующую команду для запуска менеджера профилей и создания чистого пустого профиля:
-
-```
-firefox --profilemanager
-```
-
-
-
-
-
-
-## Инструменты
-
-Пожалуй, единственный браузер с поддержкой отладки памяти -- это Chrome. В инструментах разработчика вкладка Timeline -- Memory показывает график использования памяти.
-
-
-
-Можем посмотреть, сколько памяти и куда он использует.
-
-Также в Profiles есть кнопка Take Heap Snapshot, здесь можно сделать и исследовать снимок текущего состояния страницы. Снимки можно сравнивать друг с другом, выяснять количество новых объектов. Можно смотреть, почему объект не очищен и кто на него ссылается.
-
-Замечательная статья на эту тему есть в документации: [Chrome Developer Tools: Heap Profiling](http://code.google.com/chrome/devtools/docs/heap-profiling.html).
-
-Утечки памяти штука довольно сложная. В борьбе с ними вам определенно понадобится одна вещь: *Удача!*
-
-
diff --git a/6-optimize/1-memory-leaks/chrome.png b/6-optimize/1-memory-leaks/chrome.png
deleted file mode 100644
index 0c88b10b..00000000
Binary files a/6-optimize/1-memory-leaks/chrome.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/goodluck.png b/6-optimize/1-memory-leaks/goodluck.png
deleted file mode 100644
index 918b5022..00000000
Binary files a/6-optimize/1-memory-leaks/goodluck.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/ie1.png b/6-optimize/1-memory-leaks/ie1.png
deleted file mode 100644
index ae04e2cc..00000000
Binary files a/6-optimize/1-memory-leaks/ie1.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/ie2.png b/6-optimize/1-memory-leaks/ie2.png
deleted file mode 100644
index 93617434..00000000
Binary files a/6-optimize/1-memory-leaks/ie2.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/ie9_disable1.png b/6-optimize/1-memory-leaks/ie9_disable1.png
deleted file mode 100644
index 84c98b78..00000000
Binary files a/6-optimize/1-memory-leaks/ie9_disable1.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/ie9_disable2.png b/6-optimize/1-memory-leaks/ie9_disable2.png
deleted file mode 100644
index 3817bc53..00000000
Binary files a/6-optimize/1-memory-leaks/ie9_disable2.png and /dev/null differ
diff --git a/6-optimize/1-memory-leaks/leak-ie8-2.view/index.html b/6-optimize/1-memory-leaks/leak-ie8-2.view/index.html
deleted file mode 100644
index b89b80de..00000000
--- a/6-optimize/1-memory-leaks/leak-ie8-2.view/index.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.
-
-
-
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-ie8-table.view/index.html b/6-optimize/1-memory-leaks/leak-ie8-table.view/index.html
deleted file mode 100644
index e0017dce..00000000
--- a/6-optimize/1-memory-leaks/leak-ie8-table.view/index.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-ie8-widget.view/index.html b/6-optimize/1-memory-leaks/leak-ie8-widget.view/index.html
deleted file mode 100644
index 1bdd947c..00000000
--- a/6-optimize/1-memory-leaks/leak-ie8-widget.view/index.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-ie8-xhr.view/index.html b/6-optimize/1-memory-leaks/leak-ie8-xhr.view/index.html
deleted file mode 100644
index 67329b91..00000000
--- a/6-optimize/1-memory-leaks/leak-ie8-xhr.view/index.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Страница создаёт объект XMLHttpRequest каждые 50мс.
-
-
Нажмите на кнопку и смотрите на память, она течёт в IE<9.
-
-
-
-
-
-
Количество запросов: 0
-
-
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-ie8.view/index.html b/6-optimize/1-memory-leaks/leak-ie8.view/index.html
deleted file mode 100644
index c2c1a4fc..00000000
--- a/6-optimize/1-memory-leaks/leak-ie8.view/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-xhr-2.svg b/6-optimize/1-memory-leaks/leak-xhr-2.svg
deleted file mode 100644
index e6faf68c..00000000
--- a/6-optimize/1-memory-leaks/leak-xhr-2.svg
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
\ No newline at end of file
diff --git a/6-optimize/1-memory-leaks/leak-xhr.svg b/6-optimize/1-memory-leaks/leak-xhr.svg
deleted file mode 100644
index 967a4b3c..00000000
--- a/6-optimize/1-memory-leaks/leak-xhr.svg
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
\ No newline at end of file
diff --git a/6-optimize/2-memory-leaks-jquery/article.md b/6-optimize/2-memory-leaks-jquery/article.md
deleted file mode 100644
index 70b76c45..00000000
--- a/6-optimize/2-memory-leaks-jquery/article.md
+++ /dev/null
@@ -1,144 +0,0 @@
-
-# Утечки памяти при использовании jQuery
-
-В jQuery для хранения обработчиков событий и других вспомогательных данных, связанных с DOM-элементами, используется внутренний объект, который в jQuery 1 доступен через $.data.
-
-В jQuery 2 доступ к нему закрыт через замыкание, он стал локальной переменной внутри jQuery с именем `data_priv`, но в остальном всё работает точно так, как описано, и с теми же последствиями.
-
-## $.data
-
-Встроенная функция `$.data` позволяет хранить привязывать произвольные значения к DOM-узлам.
-
-Например:
-
-```js
-//+ no-beautify
-// присвоить
-$(document).data('prop', { anything: "любой объект" })
-
-// прочитать
-alert( $(document).data('prop').anything ) // любой объект
-```
-
-Реализована она хитрым образом. Данные не хранятся в самом элементе, а во внутреннем объекте jQuery.
-
-jQuery-вызов `elem.data(prop, val)` делает следующее:
-
-
-
Элемент получает уникальный идентификатор, если у него такого еще нет:
-
-```js
-elem[jQuery.expando] = id = ++jQuery.uuid; // средствами jQuery
-```
-
-`jQuery.expando` -- это случайная строка, сгенерированная jQuery один раз при входе на страницу. Уникальное свойство, чтобы ничего важного не перезаписать.
-
...А сами данные сохраняются в специальном объекте `jQuery.cache`:
-
-```js
-//+ no-beautify
-jQuery.cache[id]['prop'] = { anything: "любой объект" };
-```
-
-
-
-
-Когда данные считываются из элемента:
-
-
-
Уникальный идентификатор элемента извлекается из `id = elem[ jQuery.expando]`.
-
Данные считываются из `jQuery.cache[id]`.
-
-
-Смысл этого API в том, что DOM-элемент никогда не ссылается на JavaScript объект напрямую. Задействуется идентификатор, а сами данные хранятся в `jQuery.cache`. Утечек в IE не будет.
-
-К тому же все данные известны библиотеке, так что можно клонировать с ними и т.п.
-
-Как побочный эффект -- возникает утечка памяти, если элемент удален из DOM без дополнительной очистки.
-
-## Примеры утечек в jQuery
-
-Следующая функция `leak` создает jQuery-утечку во всех браузерах:
-
-
-```html
-
-
-
-
-
-
-
-Утечка идёт...
-
-
-```
-
-
-Утечка происходит потому, что обработчик события в jQuery хранится в данных элемента. В строке `(*)` элемент удален очисткой родительского `innerHTML`, но в `jQuery.cache` данные остались.
-
-Более того, система обработки событий в jQuery устроена так, что вместе с обработчиком в данных хранится и ссылка на элемент, так что в итоге оба -- и обработчик и элемент -- остаются в памяти вместе со всем замыканием!
-
-Ещё более простой пример утечки:
-
-Этот код также создает утечку:
-
-```js
-function leak() {
- $('')
- .click(function() {})
-}
-```
-
-...То есть, мы создаём элемент, вешаем на него обработчик... И всё.
-
-Такой код ведёт к утечке памяти как раз потому, что элемент `
` создан, но нигде не размещен :). После выполнения функции ссылка на него теряется. Но обработчик события `click` уже сохранил данные в `jQuery.cache`, которые застревают там навсегда.
-
-## Используем jQuery без утечек
-
-Чтобы избежать утечек, описанных выше, для удаления элементов используйте функции jQuery API, а не чистый JavaScript.
-
-Методы remove(), empty() и html() проверяют дочерние элементы на наличие данных и очищают их. Это несколько замедляет процедуру удаления, но зато освобождается память.
-
-К счастью обнаружить такие утечки легко. Проверьте размер `$.cache`. Если он большой и растет, то изучите кэш, посмотрите, какие записи остаются и почему.
-
-## Улучшение производительности jQuery
-
-У способа организации внутренних данных, применённого в jQuery, есть важный побочный эффект.
-
-Функции, удаляющие элементы, также должны удалить и связанные с ними внутренние данные. Для этого нужно для каждого удаляемого элемента проверить -- а нет ли чего во внутреннем хранилище? И, если есть -- удалить.
-
-Представим, что у нас есть большая таблица `
`, и мы хотим обновить её содержимое на новое. Вызов `$('table').html(новые данные)` перед вставкой новых данных аккуратно удалит старые: пробежит по всем ячейкам и проверит внутреннее хранилище.
-
-Если это большая таблица, то обработчики, скорее всего, стоят не на ячейках, а на самом элементе `
`, то есть используется делегирование. А, значит, тратить время на проверку всех подэлементов ни к чему.
-
-Но jQuery-то об этом не знает!
-
-Чтобы "грязно" удалить элемент, без чистки, мы можем сделать это через "обычные" DOM-вызовы или воспользоваться методом detach(). Его официальное назначение -- в том, чтобы убрать элемент из DOM, но сохранить возможность для вставки (и, соответственно, оставить на нём все данные). А неофициальное -- быстро убрать элемент из DOM, без чистки.
-
-Возможен и промежуточный вариант: никто не мешает сделать `elem.detach()` и поместить вызов `elem.remove()` в `setTimeout`. В результате очистка будет происходить асинхронно и незаметно.
-
-## Итого
-
-
-
Утечки памяти при использовании jQuery возможны, если через DOM-методы удалять элементы, к которым привязаны данные или обработчики.
-
Чтобы утечки не было, достаточно убедиться, что элемент удаляется при помощи методов jQuery.
-
Побочный эффект -- при удалении элементов jQuery должна проверить наличие данных для них. Это сильно замедляет процесс удаления большого поддерева DOM.
-
Если мы значем, что обработчиков и данных нет -- гораздо быстрее удалять элементы при помощи вызова `detach` или обычного DOM.
-
-
-
diff --git a/6-optimize/3-minification/article.md b/6-optimize/3-minification/article.md
deleted file mode 100644
index 22cb1700..00000000
--- a/6-optimize/3-minification/article.md
+++ /dev/null
@@ -1,513 +0,0 @@
-# Как работают сжиматели JavaScript
-
-Перед выкладыванием JavaScript на "боевую" машину -- пропускаем его через минификатор (также говорят "сжиматель"), который удаляет пробелы и по-всякому оптимизирует код, уменьшая его размер.
-
-В этой статье мы посмотрим, как работают современные минификаторы, за счёт чего они укорачивают код и какие с ними возможны проблемы.
-[cut]
-
-## Современные сжиматели
-
-Рассматриваемые в этой статье алгоритмы и подходы относятся к минификаторам последнего поколения.
-
-Вот их список:
-
-
-Самые широко используемые -- первые два, поэтому будем рассматривать в первую очередь их.
-
-Наша цель -- понять, как они работают, и что интересного с их помощью можно сотворить.
-
-## С чего начать?
-
-Для GCC:
-
-
-
Убедиться, что стоит [Java](http://java.oracle.com)
-
Скачать и распаковать [](http://closure-compiler.googlecode.com/files/compiler-latest.zip), нам нужен файл `compiler.jar`.
-
-
-Обратите внимание на флаг `--charset` для GCC. Без него русские буквы будут закодированы во что-то типа `\u1234`.
-
-Google Closure Compiler также содержит [песочницу](http://closure-compiler.appspot.com/home) для тестирования сжатия и [веб-сервис](https://developers.google.com/closure/compiler/docs/gettingstarted_api?hl=ru), на который код можно отправлять для сжатия. Но скачать файл обычно гораздо проще, поэтому его редко где используют.
-
-Для UglifyJS:
-
-
-
Убедиться, что стоит [Node.js](http://nodejs.org)
-
Поставить `npm install -g uglify-js`.
-
Сжать файл `my.js`: `uglifyjs my.js -o my.min.js`
-
-
-## Что делает минификатор?
-
-Все современные минификаторы работают следующим образом:
-
-
Разбирают JavaScript-код в синтаксическое дерево.
-
-Также поступает любой интерпретатор JavaScript перед тем, как его выполнять. Но затем, вместо исполнения кода...
-
Бегают по этому дереву, анализируют и оптимизируют его.
-
Записывают из синтаксического дерева получившийся код.
-
-
-## Как выглядит дерево?
-
-Посмотреть синтаксическое дерево можно, запустив компилятор со специальным флагом.
-
-Для GCC есть даже способ вывести его:
-
-
-
Сначала сгенерируем дерево в формате [DOT](http://en.wikipedia.org/wiki/DOT_language):
-
-```
-java -jar compiler.jar --js my.js --use_only_custom_externs --print_tree >my.dot
-```
-
-Здесь флаг `--print_tree` выводит дерево, а `--use_only_custom_externs` убирает лишнюю служебную информацию.
-
-
Файл в этом формате используется в различных программах для графопостроения.
-
-Чтобы превратить его в обычную картинку, подойдёт утилита `dot` из пакета [Graphviz](http://www.graphviz.org/):
-
-```
-// конвертировать в формат png
-dot -Tpng my.dot -o my.png
-
-// конвертировать в формат svg
-dot -Tsvg my.dot -o my.svg
-```
-
-
-
-
-Пример кода `my.js`:
-
-```js
-function User(name) {
-
- this.sayHi = function() {
- alert( name );
- };
-
-}
-```
-
-Результат, получившееся из `my.js` дерево:
-
-
-
-В узлах-эллипсах на иллюстрации выше стоит тип, например `FUNCTION` (функция) или `NAME` (имя переменной). Комментарии к ним на русском языке добавлены мной вручную.
-
-Кроме него к каждому узлу привязаны конкретные данные. Сжиматель умеет ходить по этому дереву и менять его, как пожелает.
-
-[smart header="Комментарии JSDoc"]
-Обычно когда код превращается в дерево -- из него естественным образом исчезают комментарии и пробелы. Они не имеют значения при выполнении, поэтому игнорируются.
-
-Но Google Closure Compiler добавляет в дерево информацию из *комментариев JSDoc*, т.е. комментариев вида `/** ... */`, например:
-
-```js
-*!*
-/**
- * Номер минимальной поддерживаемой версии IE
- * @const
- * @type {number}
- */
-*/!*
-var minIEVersion = 8;
-```
-
-Такие комментарии не создают новых узлов дерева, а добавляются в качестве информации к существующем. В данном случае -- к переменной `minIEVersion`.
-
-В них может содержаться информация о типе переменной (`number`) и другая, которая поможет сжимателю лучше оптимизировать код (`const` -- константа).
-
-[/smart]
-
-## Оптимизации
-
-Сжиматель бегает по дереву, ищет "паттерны" -- известные ему структуры, которые он знает, как оптимизировать, и обновляет дерево.
-
-В разных минификаторах реализован разный набор оптимизаций, сами оптимизации применяются в разном порядке, поэтому результаты работы могут отличаться. В примерах ниже даётся результат работы GCC.
-
-
-
Лишние переменные убраны. Для этого сжиматель создаёт вспомогательную внутреннюю структуру данных, в которой хранятся сведения о "пути использования" каждой переменной. Если одна переменная заканчивает свой путь и начинает другая, то вполне можно дать им одно имя.
-
Кроме того, операции `elem = getElementsById` и `elem.parentNode` объединены, но это уже другая оптимизация.
Если переменная присваивается, но не используется, она может быть удалена. В примере выше эта оптимизация была применена к переменной `parent`, а затем и к параметру `node`.
-
Заведомо ложная ветка `if(0) { .. }` убрана, заведомо истинная -- оставлена.
-
-То же самое будет с условиями в других конструкциях, например `a = true ? c : d` превратится в `a = c`.
-
Код после `return` удалён как недостижимый.
-
-
-
Переписывание синтаксических конструкций
-
-До оптимизации:
-
-```js
-var i = 0;
-while (i++ < 10) {
- alert( i );
-}
-
-if (i) {
- alert( i );
-}
-
-if (i == '1') {
- alert( 1 );
-} else if (i == '2') {
- alert( 2 );
-} else {
- alert( i );
-}
-```
-
-После оптимизации:
-
-```js
-//+ no-beautify
-for(var i=0;10>i++;)alert(i);i&&alert(i);"1"==i?alert(1):"2"==i?alert(2):alert(i);
-```
-
-
-
Конструкция `while` переписана в `for`.
-
Конструкция `if (i) ...` переписана в `i&&...`.
-
Конструкция `if (cond) ... else ...` была переписана в `cond ? ... : ...`.
-
-
-
Инлайнинг функций
-
-*Инлайнинг функции* -- приём оптимизации, при котором функция заменяется на своё тело.
-
-До оптимизации:
-
-```js
-function sayHi(message) {
-
- var elem = createMessage('div', message);
- showElement(elem);
-
- function createMessage(tagName, message) {
- var el = document.createElement(tagName);
- el.innerHTML = message;
- return el;
- }
-
- function showElement(elem) {
- document.body.appendChild(elem);
- }
-}
-```
-
-После оптимизации (переводы строк также будут убраны):
-
-```js
-function sayHi(b) {
- var a = document.createElement("div");
- a.innerHTML = b;
- document.body.appendChild(a)
-};
-```
-
-
-
Вызовы функций `createMessage` и `showElement` заменены на тело функций. В данном случае это возможно, так как функции используются всего по разу.
-
Эта оптимизация применяется не всегда. Если бы каждая функция использовалась много раз, то с точки зрения размера выгоднее оставить их "как есть".
-
-
-
Инлайнинг переменных
-
Переменные заменяются на значение, если оно заведомо известно.
-
-До оптимизации:
-
-```js
-(function() {
- var isVisible = true;
- var hi = "Привет вам из JavaScript";
-
- window.sayHi = function() {
- if (isVisible) {
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- alert( hi );
- }
- }
-
-})();
-```
-
-После оптимизации:
-
-```js
-(function() {
- window.sayHi = function() {
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- alert( "Привет вам из JavaScript" );
- };
- }
-})();
-```
-
-
-
Переменная `isVisible` заменена на `true`, после чего `if` стало возможным убрать.
-
Переменная `hi` заменена на строку.
-
-
-Казалось бы -- зачем менять `hi` на строку? Ведь код стал ощутимо длиннее!
-
-...Но всё дело в том, что минификатор знает, что дальше код будет сжиматься при помощи gzip. Во всяком случае, все правильно настроенные сервера так делают.
-
-[Алгоритм работы gzip](http://www.gzip.org/algorithm.txt) заключается в том, что он ищет повторы в данных и выносит их в специальный "словарь", заменяя на более короткий идентификатор. Архив как раз и состоит из словаря и данных, в которых дубликаты заменены на идентификаторы.
-
-Если вынести строку обратно в переменную, то получится как раз частный случай такого сжатия -- взяли `"Привет вам из JavaScript"` и заменили на идентификатор `hi`. Но gzip справляется с этим лучше, поэтому эффективнее будет оставить именно строку. Gzip сам найдёт дубликаты и сожмёт их.
-
-Плюс такого подхода станет очевиден, если сжать gzip оба кода -- до и после минификации. Минифицированный gzip-сжатый код в итоге даст меньший размер.
-
-
-
Разные мелкие оптимизации
-
Кроме основных оптимизаций, описанных выше, есть ещё много мелких:
-
-
Упрощаются простые вызовы `Array/Object`
-
-```js
-//+ no-beautify
-a = new Array() => a = []
-o = new Object() => o = {}
-```
-
-Эта оптимизация предполагает, что `Array` и `Object` не переопределены программистом. Для включения её в UglifyJS нужен флаг `--unsafe`.
-
-
...И еще некоторые другие мелкие изменения кода...
-
-
-
-
-
-
-## Подводные камни
-
-Описанные оптимизации, в целом, безопасны, но есть ряд подводных камней.
-
-### Конструкция with
-
-Рассмотрим код:
-
-```js
-//+ no-beautify
-function changePosition(style) {
- var position, test;
-
-*!*
- with (style) {
- position = 'absolute';
- }
-*/!*
-}
-```
-
-Куда будет присвоено значение `position = 'absolute'`?
-
-Это неизвестно до момента выполнения: если свойство `position` есть в `style` -- то туда, а если нет -- то в локальную переменную.
-
-Можно ли в такой ситуации заменить локальную переменную на более короткую? Очевидно, нет:
-
-```js
-//+ no-beautify
-function changePosition(style) {
- var a, b;
-
-*!*
- with (style) { // а что, если в style нет такого свойства?
- position = 'absolute';// куда будет осуществлена запись? в window.position?
- }
-*/!*
-}
-```
-
-Такая же опасность для сжатия кроется в использованном `eval`. Ведь `eval` может обращаться к локальным переменным:
-
-```js
-//+ no-beautify
-function f(code) {
- var myVar;
-
- eval(code); // а что, если будет присвоение eval("myVar = ...") ?
-
- alert(myVar);
-```
-
-Получается, что при наличии `eval` мы не имеем права переименовывать локальные переменные. Причём (!), если функция является вложенноой, то и во внешних функциях тоже.
-
-А ведь сжатие переменных -- очень важная оптимизация. Как правило, она уменьшает размер сильнее всего.
-
-Что делать? Разные минификаторы поступают по-разному.
-
-
-
UglifyJS -- не будет переименовывать переменные. Так что наличие `with/eval` сильно повлияет на степень сжатие кода.
-
GCC -- всё равно сожмёт локальные переменные. Это, конечно же, может привести к ошибкам, причём в сжатом коде, отлаживать который не очень-то удобно. Поэтому он выдаст предупреждение о наличии опасной конструкции.
-
-
-Ни тот ни другой вариант нас, по большому счёту, не устраивают.
-
-**Для того, чтобы код сжимался хорошо и работал правильно, не используем `with` и `eval`.**
-
-Либо, если уж очень надо использовать -- делаем это с оглядкой на поведение минификатора, чтобы не было проблем.
-
-### Условная компиляция IE10-
-
-В IE10- поддерживалось [условное выполнение JavaScript](http://msdn.microsoft.com/en-us/library/121hztk3.aspx).
-
-Синтаксис: `/*@cc_on код */`.
-
-Такой код выполнится в IE10-, например:
-
-```js
-//+ run
-var isIE /*@cc_on =true@*/ ;
-
-alert( isIE ); // true в IE10-
-```
-
-Можно хитро сделать, чтобы комментарий остался, например так:
-
-```js
-//+ run
-var isIE = new Function('', '/*@cc_on return true@*/')();
-
-alert( isIE ); // true в IE.
-```
-
-
-...Однако, с учётом того, что в современных IE11+ эта компиляция не работает в любом случае, лучше избавиться от неё вообще.
-
-В следующих главах мы посмотрим, какие продвинутые возможности есть в минификаторах, как сделать сжатие более эффективным.
-
diff --git a/6-optimize/3-minification/my.svg b/6-optimize/3-minification/my.svg
deleted file mode 100644
index a04ec2c4..00000000
--- a/6-optimize/3-minification/my.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/6-optimize/4-better-minification/article.md b/6-optimize/4-better-minification/article.md
deleted file mode 100644
index 5220f678..00000000
--- a/6-optimize/4-better-minification/article.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# Улучшаем сжатие кода
-
-Здесь мы обсудим разные приёмы, которые используются, чтобы улучшить сжатие кода.
-[cut]
-## Больше локальных переменных
-
-Например, код jQuery обёрнут в функцию, запускаемую "на месте".
-
-```js
-(function(window, undefined) {
- // ...
- var jQuery = ...
-
- window.jQuery = jQuery; // сделать переменную глобальной
-
-})(window);
-```
-
-Переменные `window` и `undefined` стали локальными. Это позволит сжимателю заменить их на более короткие.
-
-## ООП без прототипов
-
-Приватные переменные будут сжаты и заинлайнены.
-
-Например, этот код хорошо сожмётся:
-
-```js
-function User(firstName, lastName) {
- var fullName = firstName + ' ' + lastName;
-
- this.sayHi = function() {
- showMessage(fullName);
- }
-
- function showMessage(msg) {
- alert( '**' + msg + '**' );
- }
-}
-```
-
-..А этот -- плохо:
-
-```js
-function User(firstName, lastName) {
- this._firstName = firstName;
- this._lastName = lastName;
-}
-
-User.prototype.sayHi = function() {
- this._showMessage(this._fullName);
-}
-
-
-User.prototype._showMessage = function(msg) {
- alert( '**' + msg + '**' );
-}
-```
-
-Сжимаются только локальные переменные, свойства объектов не сжимаются, поэтому эффект от сжатия для второго кода будет совсем небольшим.
-
-При этом, конечно, нужно иметь в виду общий стиль ООП проекта, достоинства и недостатки такого подхода.
-
-## Сжатие под платформу, define
-
-Можно делать разные сборки в зависимости от платформы (мобильная/десктоп) и браузера.
-
-Ведь не секрет, что ряд функций могут быть реализованы по разному, в зависимости от того, поддерживает ли среда выполнения нужную возможность.
-
-### Способ 1: локальная переменная
-
-Проще всего это сделать локальной переменной в модуле:
-
-```js
-(function($) {
-
-*!*
- /** @const */
- var platform = 'IE';
-*/!*
-
- // .....
-
- if (platform == 'IE') {
- alert( 'IE' );
- } else {
- alert( 'NON-IE' );
- }
-
-})(jQuery);
-```
-
-Нужное значение директивы можно вставить при подготовке JavaScript к сжатию.
-
-Сжиматель заинлайнит её и оптимизирует соответствующие IE.
-
-### Способ 2: define
-
-UglifyJS и GCC позволяют задать значение глобальной переменной из командной строки.
-
-В GCC эта возможность доступна лишь в "продвинутом режиме" работы оптимизатора, который мы рассмотрим далее (он редко используется).
-
-Удобнее в этом плане устроен UglifyJS. В нём можно указать флаг `-d SYMBOL[=VALUE]`, который заменит все переменные `SYMBOL` на указанное значение `VALUE`. Если `VALUE` не указано, то оно считается равным `true`.
-
-Флаг не работает, если переменная определена явно.
-
-Например, рассмотрим код:
-
-```js
-// my.js
-if (isIE) {
- alert( "Привет от IE" );
-} else {
- alert( "Не IE :)" );
-}
-```
-
-Сжатие вызовом `uglifyjs -d isIE my.js` даст:
-
-```js
-alert( "Привет от IE" );
-```
-
-..Ну а чтобы код работал в обычном окружении, нужно определить в нём значение переменной по умолчанию. Это обычно делается в каком-то другом файле (на весь проект), так как если объявить `var isIE` в этом, то флаг `-d isIE` не сработает.
-
-Но можно и "хакнуть" сжиматель, объявив переменную так:
-
-```js
-// объявит переменную при отсутствии сжатия
-// при сжатии не повредит
-window.isIE = window.isIE || getBrowserVersion();
-```
-
-## Убираем вызовы console.*
-
-Минификатор имеет в своём распоряжении дерево кода и может удалить ненужные вызовы.
-
-Для UglifyJS это делают опции компилятора:
-
-
`drop_debugger` -- убирает вызовы `debugger`.
-
`drop_console` -- убирает вызовы `console.*`.
-
-
-Можно написать и дополнительную функцию преобразования, которая убирает другие вызовы, например для `log.*`:
-
-```js
-var uglify = require('uglify-js');
-var pro = uglify.uglify;
-
-function ast_squeeze_console(ast) {
- var w = pro.ast_walker(),
- walk = w.walk,
- scope;
- return w.with_walkers({
- "stat": function(stmt) {
- if (stmt[0] === "call" && stmt[1][0] == "dot" && stmt[1][1] instanceof Array && stmt[1][1][0] == 'name' && stmt[1][1][1] == "log") {
- return ["block"];
- }
- return ["stat", walk(stmt)];
- },
- "call": function(expr, args) {
- if (expr[0] == "dot" && expr[1] instanceof Array && expr[1][0] == 'name' && expr[1][1] == "console") {
- return ["atom", "0"];
- }
- }
- }, function() {
- return walk(ast);
- });
-};
-```
-
-Эту функцию следует вызвать на результате `parse`, и она пройдётся по дереву и удалит все вызовы `log.*`.
-
diff --git a/6-optimize/5-gcc-advanced-optimization/article.md b/6-optimize/5-gcc-advanced-optimization/article.md
deleted file mode 100644
index f2a84d14..00000000
--- a/6-optimize/5-gcc-advanced-optimization/article.md
+++ /dev/null
@@ -1,530 +0,0 @@
-# GCC: продвинутые оптимизации
-
-Продвинутый режим оптимизации google closure compiler включается опцией --compilation_level ADVANCED_OPTIMIZATIONS.
-
-Слово "продвинутый" (advanced) здесь, пожалуй, не совсем подходит. Было бы более правильно назвать его "супер-агрессивный-ломающий-ваш-неподготовленный-код-режим". Кардинальное отличие применяемых оптимизаций от обычных (simple) -- в том, что они небезопасны.
-
-Чтобы им пользоваться -- надо уметь это делать.
-[cut]
-
-## Основной принцип продвинутого режима
-
-
-
Если в обычном режиме переименовываются только локальные переменные внутри функций, то в "продвинутом" -- на более короткие имена заменяется все.
-
Если в обычном режиме удаляется недостижимый код после return, то в продвинутом -- вообще весь код, который не вызывается в явном виде.
-
-
-Например, если запустить продвинутую оптимизацию на таком коде:
-
-```js
-// my.js
-function test(node) {
- node.innerHTML = "newValue"
-}
-```
-
-Строка запуска компилятора:
-
-```
-java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js my.js
-```
-
-...То результат будет -- пустой файл. Google Closure Compiler увидит, что функция test не используется, и с чистой совестью вырежет ее.
-
-А в следующем скрипте функция сохранится:
-
-```js
-function test(n) {
- alert( "this is my test number " + n );
-}
-test(1);
-test(2);
-```
-
-После сжатия:
-
-```js
-function a(b) {
- alert("this is my test number " + b)
-}
-a(1);
-a(2);
-```
-
-Здесь в скрипте присутствует явный вызов функции, поэтому она сохранилась.
-
-Конечно, есть способы, чтобы сохранить функции, вызов которых происходит вне скрипта, и мы их обязательно рассмотрим.
-
-**Продвинутый режим сжатия не предусматривает сохранения глобальных переменных. Он переименовывает, инлайнит, удаляет вообще все символы, кроме зарезервированных.**
-
-Иначе говоря, продвинутый режим (ADVANCED_OPTIMIZATIONS), в отличие от простого (SIMPLE_OPTIMIZATIONS -- по умолчанию), вообще не заботится о доступности кода извне и сохранении ссылочной целостности относительно внешних скриптов.
-
-Единственное, что он гарантирует -- это внутреннюю ссылочную целостность, и то -- при соблюдении ряда условий и практик программирования.
-
-Собственно, за счет такого агрессивного подхода и достигается дополнительный эффект оптимизации и сжатия скриптов.
-
-[summary]
-То есть, продвинутый режим - это не просто "улучшенный обычный", а принципиально другой, небезопасный и обфусцирующий подход к сжатию.
-
-Этот режим является "фирменной фишкой" Google Closure Compiler, недоступной при использовании других компиляторов.
-[/summary]
-
-Для того, чтобы эффективно сжимать Google Closure Compiler в продвинутом режиме, нужно понимать, что и как он делает. Это мы сейчас обсудим.
-
-### Сохранение ссылочной целостности
-
-Чтобы использовать сжатый скрипт, мы должны иметь возможность вызывать функции под теми именами, которые им дали.
-
-То есть, перед нами стоит задача *сохранения ссылочной целостности*, которая заключается в том, чтобы обеспечить доступность нужных функций для обращений по исходному имени извне скрипта.
-
-Существует два способа сохранения внешней ссылочной целостности: экстерны и экспорты. Мы в подробностях рассмотрим оба, но перед этим необходимо упомянуть о модулях -- другой важнейшей возможности GCC.
-
-### Модули
-
-При сжатии GCC можно указать одновременно много JavaScript-файлов. "Эка невидаль, " -- скажете вы, и будете правы. Да, пока что ничего особого.
-
-Но в дополнение к этому можно явно указать, какие исходные файлы сжать в какие файлы результата. То есть, разбить итоговую сборку на модули.
-
-Так что страницы могут грузить модули по мере надобности. Например, по умолчанию -- главный, а дополнительная функциональность -- загружаться лишь там, где она нужна.
-
-Для такой сборки используется флаг компилятора `--module имя:количество файлов`.
-
-Например:
-
-```
-java -jar compiler.jar --js base.js --js main.js --js admin.js --module
-first:2 --module second:1:first
-```
-
-Эта команда создаст модули: first.js и second.js.
-
-Первый модуль, который назван "first", создан из объединённого и оптимизированного кода первых двух файлов (`base.js` и `main.js`).
-
-Второй модуль, который назван "second", создан из `admin.js` -- это следующий аргумент `--js` после включенных в первый модуль.
-
-Второй модуль в нашем случае зависит от первого. Флаг `--module second:1:first` как раз означает, что модуль `second` будет создан из одного файла после вошедших в предыдущий модуль (`first`) и зависит от модуля `first`.
-
-А теперь -- самое вкусное.
-
-**Ссылочная целостность между всеми получившимися файлами гарантируется.**
-
-Если в одном функция `doFoo` заменена на `b`, то и в другом тоже будет использоваться `b`.
-
-Это означает, что проблем между JS-файлами не будет. Они могут свободно вызывать друг друга без экспорта, пока находятся в единой модульной сборке.
-
-### Экстерны
-
-Экстерн (extern) -- имя, которое числится в специальном списке компилятора. Он должен быть определен вне скрипта, в файле экстернов.
-
-**Компилятор никогда не переименовывает экстерны.**
-
-Например:
-
-```js
-document.onkeyup = function(event) {
- alert(event.type)
-}
-```
-
-После продвинутого сжатия:
-
-```js
-document.onkeyup = function(a) {
- alert(a.type)
-}
-```
-
-Как видите, переименованной оказалась только переменная `event`. Такое переименование заведомо безопасно, т.к. `event` -- локальная переменная.
-
-Почему компилятор не тронул остального? Попробуем другой вариант:
-
-```js
-document.blabla = function(event) {
- alert(event.megaProperty)
-}
-```
-
-После компиляции:
-
-```js
-document.a = function(a) {
- alert(a.b)
-}
-```
-
-Теперь компилятор переименовал и blabla и megaProperty.
-
-Дело в том, что названия, использованные до этого, были во внутреннем списке экстернов компилятора. Этот список охватывает основные объекты браузеров и находится (под именем externs.zip) в корне архива compiler.jar.
-
-**Компилятор переименовывает имя списка экстернов только когда так названа локальная переменная.**
-
-Например:
-
-```js
-window.resetNode = function(node) {
- var innerHTML = "test";
- node.innerHTML = innerHTML;
-}
-```
-
-На выходе:
-
-```js
-window.a = function(a) {
- a.innerHTML = "test"
-};
-```
-
-Как видите, внутренняя переменная innerHTML не просто переименована - она заинлайнена (заменена на значение). Так как переменная локальна, то любые действия внутри функции с ней безопасны.
-
-А свойство innerHTML не тронуто, как и объект window -- так как они в списке экстернов и не являются локальными переменными.
-
-Это приводит к следующему побочному эффекту. Иногда свойства, которые следовало бы сжать, не сжимаются. Например:
-
-```js
-window['User'] = function(name, type, age) {
- this.name = name
- this.type = type
- this.age = age
-}
-```
-
-После сжатия:
-
-```js
-window.User = function(a, b, c) {
- this.name = a;
- this.type = b;
- this.a = c
-};
-```
-
-Как видно, свойство age сжалось, а name и type -- нет. Это побочный эффект экстернов: name и type -- в списке объектов браузера, и компилятор просто старается не наломать дров.
-
-Поэтому отметим еще одно полезное правило оптимизации:
-
-**Названия своих свойств не должны совпадать с зарезервированными словами (экстернами). Тогда они будут хорошо сжиматься.**
-
-Для задания списка экстернов их достаточно перечислить в файле и указать этот файл флагом --externs <файл экстернов.js>.
-
-При перечислении объектов в файле экстернов - объявляйте их и перечисляйте свойства. Все эти объявления никуда не идут, они используются только для создания списка, который обрабатывается компилятором.
-
-Например, файл `myexterns.js`:
-
-```js
-var dojo = {}
-dojo._scopeMap;
-```
-
-Использование такого файла при сжатии (опция --externs myexterns.js) приведет к тому, что все обращения к символам dojo и к dojo._scopeMap будут не сжаты, а оставлены "как есть".
-
-
-### Экспорт
-
-*Экспорт* -- программный ход, основанный на следующем правиле поведения компилятора.
-
-**Компилятор заменяет обращения к свойствам через кавычки на точку, и при этом не трогает название свойства.**
-
-Например, window['User'] превратится в window.User, но не дальше.
-
-Таким образом можно *"экспортировать"* нужные функции и объекты:
-
-```js
-function SayWidget(elem) {
- this.elem = elem
- this.init()
-}
-window['SayWidget'] = SayWidget;
-```
-
-На выходе:
-
-```js
-function a(b) {
- this.a = b;
- this.b()
-}
-window.SayWidget = a;
-```
-
-Обратим внимание -- сама функция SayWidget была переименована в a. Но затем -- экспортирована как window.SayWidget, и таким образом доступна внешним скриптам.
-
-Добавим пару методов в прототип:
-
-```js
-function SayWidget(elem) {
- this.elem = elem;
- this.init();
-}
-
-SayWidget.prototype = {
- init: function() {
- this.elem.style.display = 'none'
- },
-
- setSayHandler: function() {
- this.elem.onclick = function() {
- alert("hi")
- };
- }
-}
-
-window['SayWidget'] = SayWidget;
-SayWidget.prototype['setSayHandler'] = SayWidget.prototype.setSayHandler;
-```
-
-После сжатия:
-
-```js
-//+ no-beautify
-function a(b) {
- this.a = b;
- this.b()
-}
-a.prototype = {b:function() {
- this.a.style.display = "none"
-}, c:function() {
- this.a.onclick = function() {
- alert("hi")
- }
-}};
-window.SayWidget = a;
-a.prototype.setSayHandler = a.prototype.c;
-```
-
-Благодаря строке
-
-```js
-SayWidget.prototype['setSayHandler'] = SayWidget.prototype.setSayHandler
-```
-
-метод setSayHandler экспортирован и доступен для внешнего вызова.
-
-Сама строка экспорта выглядит довольно глупо. По виду -- присваиваем свойство самому себе.
-
-Но логика сжатия GCC работает так, что такая конструкция является экспортом. Справа переименование свойства setSayHandler происходит, а слева -- нет.
-
-[smart header="Планируйте жизнь после сжатия"]
-
-Рассмотрим следующий код:
-
-```js
-window['Animal'] = function() {
- this.blabla = 1;
- this['blabla'] = 2;
-}
-```
-
-После сжатия:
-
-```js
-window.Animal = function() {
- this.a = 1;
- this.blabla = 2
-};
-```
-
-Как видно, первое обращение к свойству blabla сжалось, а второе (как и все аналогичные) -- преобразовалось в синтаксис через точку.
-В результате получили некорректное поведение кода.
-
-Так что, используя продвинутый режим оптимизации, планируйте поведение кода после сжатия.
-
-**Если где-то возможно обращение к свойствам через квадратные скобки по полному имени -- такое свойство должно быть экспортировано.**
-[/smart]
-
-### goog.exportSymbol и goog.exportProperty
-
-В библиотеке [Google Closure Library](https://developers.google.com/closure/library/) для экспорта есть специальная функция goog.exportSymbol. Вызывается так:
-
-```js
-goog.exportSymbol('my.SayWidget', SayWidget)
-```
-
-Эта функция по сути работает также, как и рассмотренная выше строка с присвоением свойства, но при необходимости создает нужные объекты.
-
-Она аналогична коду:
-
-```js
-window['my'] = window['my'] || {}
-window['my']['SayWidget'] = SayWidget
-```
-
-То есть, если путь к объекту не существует -- exportSymbol создаст нужные пустые объекты.
-
-Функция goog.exportProperty экспортирует свойство объекта:
-
-```js
-goog.exportProperty(SayWidget.prototype, 'setSayHandler', SayWidget.prototype.setSayHandler)
-```
-
-Строка выше - то же самое, что и:
-
-```js
-SayWidget.prototype['setSayHandler'] = SayWidget.prototype.setSayHandler
-```
-
-Зачем они нужны, если все можно сделать простым присваиванием?
-
-Основная цель этих функций -- во взаимодействии с Google Closure Compiler. Они дают информацию компилятору об экспортах, которую он может использовать.
-
-Например, есть недокументированная внутренняя опция externExportsPath, которая генерирует из всех экспортов файл экстернов. Таким образом можно распространять откомпилированный JavaScript-файл как внешнюю библиотеку, с файлом экстернов для удобного внешнего связывания.
-
-Кроме того, экспорт через эти функциями удобен и нагляден.
-
-Если вы используете продвинутый режим оптимизации, то можно взять их из файла base.js Google Closure Library. Можно и подключить этот файл целиком -- оптимизатор при продвинутом сжатии вырежет из него почти всё лишнее, так что overhead будет минимальным.
-
-### Отличия экспорта от экстерна
-
-Между экспортом и экстерном есть кое-что общее. И то и другое дает возможность доступа к объектам под исходным именем, до переименования.
-
-Но, в остальном, это совершенно разные вещи.
-
-
-
-
-
Экстерн
-
Экспорт
-
-
-
-
-
Служит для тотального запрета на переименование всех обращений к свойству.
-Задумано для сохранения обращений к стандартным объектам браузера, внешним библиотекам.
-
Служит для открытия доступа к свойству извне под указанным именем.
-Задумано для открытия внешнего интерфейса к сжатому скрипту.
-
-
-
Работает со свойством, объявленным вне скрипта.
-Вы не можете объявить новое свойство в скрипте и сделать его экстерном.
-
Создает ссылку на свойство, объявленное в скрипте.
-
-
-
Если window - экстерн, то все обращения к window в скрипте останутся как есть.
-
Если user экспортируется, то создается только одна ссылка под полным именем, а все остальные обращения будут сокращены.
-
-
-
-
-
-## Стиль разработки
-
-Посмотрим, как сжиматель поведёт себя на следующем, типичном, объявлении библиотеки:
-
-```js
-(function(window, undefined) {
-
- // пространство имен и локальная перменная для него
- var MyFramework = window.MyFramework = {};
-
- // функция фреймворка, доступная снаружи
- MyFramework.publicOne = function() {
- makeElem();
- };
-
- // приватная функция фреймворка
- function makeElem() {
- var div = document.createElement('div');
- document.body.appendChild(div);
- }
-
- // еще какая-то функция
- MyFramework.publicTwo = function() {};
-
-})(window);
-
-// использование
-MyFramework.publicOne();
-```
-
-Результат компиляции в обычном режиме:
-
-```js
-//+ no-beautify
-// java -jar compiler.jar --js myframework.js --formatting PRETTY_PRINT
-(function(a) {
- a = a.MyFramework = {};
- a.publicOne = function() {
- var a = document.createElement("div");
- document.body.appendChild(a)
- };
- a.publicTwo = function() {
- }
-})(window);
-MyFramework.publicOne();
-```
-
-Это -- примерно то, что мы ожидали. Неиспользованный метод `publicTwo` остался, локальные свойства переименованы и заинлайнены.
-
-А теперь продвинутый режим:
-
-```js
-// --compilation_level ADVANCED_OPTIMIZATIONS
-window.a = {};
-MyFramework.b();
-```
-
-Оно не работает! Компилятор попросту не разобрался, что и как вызывается, и превратил рабочий JS-файл в один сплошной баг.
-
-В зависимости от версии GCC у вас может быть и что-то другое.
-
-Всё дело в том, что такой стиль объявления нетипичен для инструментов, которые в самом Google разрабатываются и сжимаются этим минификатором.
-
-Типичный правильный стиль:
-
-```js
-// пространство имен и локальная перменная для него
-var MyFramework = {};
-
-MyFrameWork._makeElem = function() {
- var div = document.createElement('div');
- document.body.appendChild(div);
-};
-
-MyFramework.publicOne = function() {
- MyFramework._makeElem();
-};
-
-MyFramework.publicTwo = function() {};
-
-// использование
-MyFramework.publicOne();
-```
-
-Обычное сжатие здесь будет бесполезно, а вот продвинутый режим идеален:
-
-```js
-// в зависимости от версии GCC результат может отличаться
-MyFrameWork.a = function() {
- var a = document.createElement("div");
- document.body.appendChild(a)
-};
-MyFrameWork.a();
-```
-
-Google Closure Compiler не только разобрался в структуре и удалил лишний метод - он заинлайнил функции, чтобы итоговый размер получился минимальным.
-
-Как говорится, преимущества налицо.
-
-## Резюме
-
-Продвинутый режим оптимизации сжимает, оптимизирует и, при возможности, удаляет все свойства и методы, за исключением экстернов.
-
-Это является принципиальным отличием, по сравнению с другими упаковщиками.
-
-Отказ от сохранения внешней ссылочной целостности с одной стороны позволяет увеличить уровень сжатия, но требует поддержки со стороны разработчика.
-
-Основная проблема этого сжатия -- усложнение разработки. Добавляется дополнительный уровень возможных проблем: сжатие. Конечно, можно отлаживать и сжатый код, для этого придуманы [Source Maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/), но клиентская разработка и без того достаточно сложна.
-
-Поэтому его используют редко.
-
-Как правило, есть две причины для использования продвинутого режима:
-
-
**Обфускация кода.**
-
-Если в коде после обычного сжатия ещё как-то можно разобраться, то после продвинутого -- уже нет. Всё переименовано и заинлайнено. В теории это, конечно, возможно, но "порог входа" в такой код несоизмеримо выше.
-
-Судя по виду скриптов на сайтах, созданных Google, сам Google жмет свои скрипты именно продвинутым режимом оптимизации. И библиотека Google Closure Library тоже рассчитана на него.
-
**Хорошие сжатие виджетов, счётчиков.**
-
-Небольшой код, который отдаётся наружу, может быть сжат в продвинутом режиме. Так как он небольшой -- все ошибки можно легко исправить, а продвинутый режим гарантирует наилучшее сжатие.
-
diff --git a/6-optimize/6-gcc-check-types/article.md b/6-optimize/6-gcc-check-types/article.md
deleted file mode 100644
index 8c625359..00000000
--- a/6-optimize/6-gcc-check-types/article.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# GCC: статическая проверка типов
-
-Google Closure Compiler, как и любой кошерный компилятор, старается проверить правильность кода и предупредить о возможных ошибках.
-
-Первым делом он, разумеется, проверяет структуру кода и сразу же выдает такие ошибки как пропущенная скобка или лишняя запятая.
-
-Но, кроме этого, он умеет проверять типы переменных, используя как свои собственные знания о встроенных javascript-функциях и преобразованиях типов,
-так и информацию о типах из JSDoc, указываемую javascript-разработчиком.
-
-Это обеспечивает то, чем так гордятся компилируемые языки -- статическую проверку типов, что позволяет избежать лишних ошибок во время выполнения.
-[cut]
-
-Для вывода предупреждений при проверки типов используется флаг `--jscomp_warning checkTypes`.
-
-## Задание типа при помощи аннотации
-
-Самый очевидный способ задать тип -- это использовать аннотацию. Полный список аннотаций вы найдете в документации.
-
-В следующем примере параметр id функции f1 присваивается переменной boolVar другого типа:
-
-```js
-/** @param {number} id */
-function f(id) {
- /** @type {boolean} */
- var boolVar;
-
- boolVar = id; // (!)
-}
-```
-
-Компиляция с флагом `--jscomp_warning checkTypes` выдаст предупреждение:
-
-```
-f.js:6: WARNING - assignment
-found : number
-required: boolean
- boolVar = id; // (!)
- ^
-```
-
-Действительно: произошло присвоение значения типа number переменной типа boolean.
-
-Типы отслеживаются по цепочке вызовов.
-
-Еще пример, на этот раз вызов функции с некорректным параметром:
-
-```js
-/** @param {number} id */
-function f1(id) {
- f2(id); // (!)
-}
-
-/** @param {string} id */
-function f2(id) {}
-```
-
-Такой вызов приведёт к предупреждению со стороны минификатора:
-
-```
-f2.js:3: WARNING - actual parameter 1 of f2 does not match formal parameter
-found : number
-required: string
- f2(id); // (!)
- ^
-```
-
-Действительно, вызов функции f2 произошел с числовым типом вместо строки.
-
-**Отслеживание приведений и типов идёт при помощи графа взаимодействий и выведению (infer) типов, который строит GCC по коду.**
-
-## Знания о преобразовании типов
-
-Google Closure Compiler знает, как операторы javascript преобразуют типы. Такой код уже не выдаст ошибку:
-
-```js
-/** @param {number} id */
-function f1(id) {
- /** @type {boolean} */
- var boolVar;
-
- boolVar = !!id
-}
-```
-
-Действительно - переменная преобразована к типу boolean двойным оператором НЕ.
-А код boolVar = 'test-'+id выдаст ошибку, т.к. конкатенация со строкой дает тип string.
-
-## Знание о типах встроенных функций, объектные типы
-
-Google Closure Compiler содержит описания большинства встроенных объектов и функций javascript вместе с типами параметров и результатов.
-
-Например, объектный тип Node соответствует узлу DOM.
-
-Пример некорректного кода:
-
-```js
-/** @param {Node} node */
-function removeNode(node) {
- node.parentNode.removeChild(node)
-}
-document.onclick = function() {
- removeNode("123")
-}
-```
-
-Выдаст предупреждение
-
-```
-f3.js:7: WARNING - actual parameter 1 of removeNode does not match formal parameter
-found : string
-required: (Node|null)
- removeNode("123")
- ^
-```
-
-Обратите внимание - в этом примере компилятор выдает required: Node|null. Это потому, что указание объектного типа (не элементарного) подразумевает, что в функцию может быть передан null.
-
-В следующем примере тип указан жестко, без возможности обнуления:
-
-```js
-*!*
-/** @param {!Node} node */
-*/!*
-function removeNode(node) {
- node.parentNode.removeChild(node)
-}
-```
-
-Восклицательный знак означает, что параметр обязатален.
-
-Найти описания встроенных типов и объектов javascript вы можете в файле экстернов: externs.zip находится в корне архива compiler.jar.
-
-## Интеграция с проверками типов из Google Closure Library
-
-В Google Closure Library есть функции проверки типов: goog.isArray, goog.isDef, goog.isNumber и т.п.
-
-Google Closure Compiler знает о них и понимает, что внутри следующего if переменная может быть только функцией:
-
-```js
-var goog = {
- isFunction: function(f) {
- return typeof f == 'function'
- }
-}
-
-if (goog.isFunction(func)) {
- func.apply(1, 2)
-}
-```
-
-Сжатие с проверкой выдаст предупреждение:
-
-```
-f.js:6: WARNING - actual parameter 2 of Function.apply does not match formal parameter
-found : number
-required: (Object|null|undefined)
- func.apply(1, 2)
- ^ ^
-```
-
-То есть, компилятор увидел, что код, использующий func находится в `if (goog.isFunction(func))` и сделал соответствующий вывод, что это в этой ветке `func` является функцией, а значит вызов `func.apply(1,2)` ошибочен (второй аргумент не может быть числом).
-
-Дело тут именно в интеграции с Google Closure Library. Если поменять `goog` на `g` -- предупреждения не будет.
-
-## Резюме
-
-Из нескольких примеров, которые мы рассмотрели, должна быть понятна общая логика проверки типов.
-
-Соответствующие различным типам и ограничениям на типы аннотации вы можете найти в Документации Google. В частности, возможно указание нескольких возможных типов, типа undefined и т.п.
-
-Также можно указывать количество и тип параметров функции, ключевого слова this, объявлять классы, приватные методы и интерфейсы.
-
-Проверка типов javascript, предоставляемая Google Closure Compiler -- пожалуй, самая продвинутая из существующих на сегодняшний день.
-
-C ней аннотации, документирующие типы и параметры, становятся не просто украшением, а реальным средством проверки, уменьшающим количество ошибок на production.
-
-Очень подробно проверка типов описана в книге [Closure: The Definitive Guide](http://www.ozon.ru/context/detail/id/6089988/), автора Michael Bolin.
\ No newline at end of file
diff --git a/6-optimize/7-gcc-closure-library/article.md b/6-optimize/7-gcc-closure-library/article.md
deleted file mode 100644
index 0c02e685..00000000
--- a/6-optimize/7-gcc-closure-library/article.md
+++ /dev/null
@@ -1,180 +0,0 @@
-# GCC: интеграция с Google Closure Library
-
-Google Closure Compiler содержит ряд специальных возможностей для интеграции с Google Closure Library.
-
-Здесь важны две вещи.
-
-
Для их использования возможно использовать минимум от Google Closure Library. Например, взять одну или несколько функций из библиотеки.
-
GCC -- расширяемый компилятор, можно добавить к нему свои "фазы оптимизации" для интеграции с другими инструментами и фреймворками.
-
-[cut]
-
-Интеграция с Google Closure Library подключается флагом --process_closure_primitives, который по умолчанию установлен в true. То есть, она включена по умолчанию.
-
-Этот флаг запускает специальный проход компилятора, описанный классом ProcessClosurePrimitives и подключает дополнительную проверку типов ClosureReverseAbstractInterpreter.
-
-Мы рассмотрим все действия, которые при этом происходят, а также некоторые опции, которые безопасным образом используют символы Google Closure Library без объявления флага.
-
-## Преобразование основных символов
-
-Следующие действия описаны в классе ProcessClosurePrimitives.
-
-### Замена константы COMPILED
-
-В Google Closure Library есть переменная:
-
-```js
-/**
- * @define {boolean} ...
- */
-var COMPILED = false;
-```
-
-Проход ProcessClosurePrimitives переопределяет ее в true и использует это при оптимизациях, удаляя ветки кода, не предназначены для запуска на production.
-
-Такие функции существуют, например, в ядре Google Closure Library. К ним в первую очередь относятся вызовы, предназначенные для сборки и проверки зависимостей. Они содержат код, обрамленный проверкой COMPILED, например:
-
-```js
-goog.require = function(rule) {
- // ...
- if (!COMPILED) {
- // основное тело функции
- }
-}
-```
-
-Аналогично может поступить и любой скрипт, даже без использования Google Closure Library:
-
-```js
-/** @define {boolean} */
-var COMPILED = false
-
-Framework = {}
-
-Framework.sayCompiled = function() {
- if (!COMPILED) {
- alert("Not compressed")
- } else {
- alert("Compressed")
- }
-}
-```
-
-Для того, чтобы сработало, нужно сжать в продвинутом режиме:
-
-```js
-Framework = {};
-Framework.sayCompiled = Framework.a = function() {
- alert( "Compressed" );
-};
-```
-
-Компилятор переопределил COMPILED в true и произвел соответствующие оптимизации.
-
-### Автоподстановка локали
-
-В Google Closure Compiler есть внутренняя опция locale
-
-Эта опция переопределяет переменную goog.LOCALE на установленную при компиляции.
-
-Для использования опции locale, на момент написания статьи, ее нужно задать в Java коде компилятора, т.к. соответствующего флага нет.
-
-Как и COMPILED, константу goog.LOCALE можно и использовать в своем коде без библиотеки Google Closure Library.
-
-### Проверка зависимостей
-
-Директивы goog.provide, goog.require, goog.addDependency обрабатываются особым образом.
-
-Все зависимости проверяются, а сами директивы проверки -- удаляются из сжатого файла.
-
-### Экспорт символов
-
-Вызов goog.exportSymbol задаёт экспорт символа.
-
-Если подробнее, то код goog.exportSymbol('a',myVar) эквивалентен
-`window['a'] = myVar`.
-
-
-### Автозамена классов CSS
-
-Google Closure Library умеет преобразовывать классы CSS на более короткие по списку, который задаётся при помощи `goog.setCssNameMapping`.
-
-Например, следующая функция задает такой список.
-
-```js
- goog.setCssNameMapping({
- "goog-menu": "a",
- "goog-menu-disabled": "a-b",
- "CSS_LOGO": "b",
- "hidden": "c"
- });
-```
-
-Тогда следующий вызов преобразуется в "a a-b":
-
-```js
-goog.getCssName('goog-menu') + ' ' + goog.getCssName('goog-menu', 'disabled')
-```
-
-Google Closure Compiler производит соответствующие преобразования в сжатом файле и удаляет вызов setCssNameMapping из кода.
-
-Чтобы это сжатие работало, в HTML/CSS классы тоже должны сжиматься. По всей видимости, в приложениях Google это и происходит, но соответствующие инструменты закрыты от публики.
-
-### Генерация списка экстернов
-
-При объявлении внутренней опции externExportsPath, содержащей путь к файлу, в этот файл будут записаны все экспорты, описанные через goog.exportSymbol/goog.exportProperty.
-
-В дальнейшем этот файл может быть использован как список экстернов для компиляции.
-
-Эта опция может быть полезна для создания внешних библиотек, распространяемых со списком экстернов.
-
-Для её использования нужна своя обёртка вокруг компилятора на Java. Соответствующий проход компилятора описан в классе ExternExportsPass.
-
-### Проверка типов
-
-В Google Closure Library есть ряд функций для проверки типов. Например: goog.isArray, goog.isString, goog.isNumber, goog.isDef и т.п.
-
-Компилятор использует их для проверки типов, более подробно см. [](/gcc-check-types)
-
-Эта логика описана в классе ClosureReverseAbstractInterpreter. Названия функций, определяющих типы, жестко прописаны в Java-коде, поменять их на свои без модификации исходников нельзя.
-
-### Автогенерация экспортов из аннотаций
-
-Для этого в Google Closure Compiler есть внутренняя опция generateExports.
-
-Эта недокументированная опция добавляет проход компилятора, описанный классом GenerateExports.
-
-Он читает аннотации @export и создает из них экспортирующие вызовы goog.exportSymbol/exportProperty. Название экспортирующих функций находится в классе соглашений кодирования, каким по умолчанию является GoogleCodingConvention.
-
-Например:
-
-```js
-/** @export */
-function Widget() {}
- /** @export */
-Widget.prototype.hide = function() {
- this.elem.style.display = 'none'
-}
-```
-
-После компиляции в продвинутом режиме:
-
-```js
-function a() {}
-goog.d("Widget", a);
-a.prototype.a = function() {
- this.b.style.display = "none"
-};
-goog.c(a.prototype, "hide", a.prototype.a);
-```
-
-Свойства благополучно экспортированы. Удобно.
-
-### Резюме
-
-Google Closure Compiler содержит дополнительные фичи, облегчающие интеграцию с Google Closure Library. Некоторые из них весьма полезны, но требуют создания своего Java-файла, который ставит внутренние опции.
-
-При обработке символов компилятор не смотрит, подключена ли библиотека, он находит обрабатывает их просто по именам. Поэтому вы можете использовать свою реализацию соответствующих функций.
-
-Google Closure Compiler можно легко расширить, добавив свои опции и проходы оптимизатора, для интеграции с вашими инструментами.
-
diff --git a/6-optimize/8-memory-removechild-innerhtml/article.md b/6-optimize/8-memory-removechild-innerhtml/article.md
deleted file mode 100644
index 5882124d..00000000
--- a/6-optimize/8-memory-removechild-innerhtml/article.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Очистка памяти при removeChild/innerHTML
-
-Управление памятью в случае с DOM работает по сути так же, как и с обычными JavaScript-объектами. Пока объект достижим -- он остаётся в памяти.
-
-Но есть и особенности, поскольку DOM весь переплетён ссылками.
-[cut]
-## Пример
-Для примера рассмотрим следующий HTML:
-
-```html
-
-
-
-
-
-
Список
-
- Сосед
-
-
-
-
-```
-
-Его DOM (показаны только основные ссылки):
-
-
-
-## Удаление removeChild
-
-Операция `removeChild` разрывает все связи удаляемым узлом и его родителем.
-
-Поэтому, если удалить `DIV` из `BODY`, то всё поддерево под `DIV` станет недостижимым и будет удалено.
-
-А что происходит, если на какой-то элемент внутри удаляемого поддерева есть ссылка?
-
-Например, `UL` сохранён в переменную `list`:
-
-```js
-var list = document.getElementsByTagName('UL')[0];
-document.body.removeChild(document.body.children[0]);
-```
-
-В этом случае, так как из этого `UL` можно по ссылкам добраться до любого другого места DOM, то получается, что все объекты по-прежнему достижимы и должны остаться в памяти:
-
-
-
-То есть, DOM-объекты при использовании `removeChild` работают по той же логике, что и обычные объекты.
-
-## Удаление через innerHTML
-
-А вот удаление через очистку `elem.innerHTML="..."` браузеры интерпретируют по-разному.
-
-По идее, при присвоении `elem.innerHTML=html` из DOM должны удаляться предыдущие узлы и добавляться новые, из указанного `html`. Но стандарт ничего не говорит о том, что делать с узлами после удаления. И тут разные браузеры имеют разное мнение.
-
-Посмотрим, что произойдёт с DOM-структурой при очистке `BODY`, если на какой-либо элемент есть ссылка.
-
-```js
-var list = document.getElementsByTagName('UL')[0];
-document.body.innerHTML = "";
-```
-
-Обращаю внимание -- связь разрывается только между `DIV` и `BODY`, т.е. на верхнем уровне, а `list` -- это произвольный элемент.
-
-Чтобы увидеть, что останется в памяти, а что нет -- запустим код:
-
-```html
-
-
-
-
Список
-
- Сосед
-
-
-
-```
-
-Как ни странно, браузеры ведут себя по-разному:
-
-
-
-
-
`parentNode`
-
`nextSibling`
-
`children.length`
-
-
-
-
-
Chrome/Safari/Opera
-
`null`
-
`null`
-
`1`
-
-
-
Firefox
-
узел DOM
-
узел DOM
-
`1`
-
-
-
IE 11-
-
`null`
-
`null`
-
`0`
-
-
-
-
-Иными словами, браузеры ведут себя с различной степенью агрессивности по отношению к элементам.
-
-
-
Firefox
-
Главный пацифист. Оставляет всё, на что есть ссылки, т.е. элемент, его родителя, соседей и детей, в точности как при `removeChild`.
-
Chrome/Safari/Opera
-
Считают, что раз мы задали ссылку на `UL`, то нам нужно только это поддерево, а остальные узлы (соседей, родителей) можно удалить.
-
Internet Explorer
-
Как ни странно, самый агрессивный. Удаляет вообще всё, кроме узла, на который есть ссылка. Это поведение одинаково для всех версий IE.
-
-
-На иллюстрации ниже показано, какую часть DOM оставит каждый из браузеров:
-
-
-## Итого
-
-Если на какой-то DOM-узел есть ссылка, то:
-
-
-
При использовании `removeChild` на родителе (или на этом узле, не важно) все узлы, достижимые из данного, остаются в памяти.
-
-То есть, фактически, в памяти может остаться большая часть дерева DOM. Это даёт наибольшую свободу в коде, но может привести к большим "утечкам памяти" из-за сохранения данных, которые реально не нужны.
-
При удалении через `innerHTML` браузеры ведут себя с различной степенью агрессивности. Кросс-браузерно гарантировано одно: сам узел, на который есть ссылка, останется в памяти.
-
-Поэтому обращаться к соседям и детям узла, предок которого удалён через присвоение `innerHTML`, нельзя.
-
-
-
-
-
diff --git a/6-optimize/8-memory-removechild-innerhtml/html-innerhtml.png b/6-optimize/8-memory-removechild-innerhtml/html-innerhtml.png
deleted file mode 100644
index e606be2a..00000000
Binary files a/6-optimize/8-memory-removechild-innerhtml/html-innerhtml.png and /dev/null differ
diff --git a/6-optimize/8-memory-removechild-innerhtml/html-list.png b/6-optimize/8-memory-removechild-innerhtml/html-list.png
deleted file mode 100644
index 1ffd8ace..00000000
Binary files a/6-optimize/8-memory-removechild-innerhtml/html-list.png and /dev/null differ
diff --git a/6-optimize/8-memory-removechild-innerhtml/html.png b/6-optimize/8-memory-removechild-innerhtml/html.png
deleted file mode 100644
index ca8da064..00000000
Binary files a/6-optimize/8-memory-removechild-innerhtml/html.png and /dev/null differ
diff --git a/7-frames-and-windows/6-clickjacking/article.md b/7-frames-and-windows/6-clickjacking/article.md
index f6efd27c..18cb7cc5 100644
--- a/7-frames-and-windows/6-clickjacking/article.md
+++ b/7-frames-and-windows/6-clickjacking/article.md
@@ -63,12 +63,11 @@ iframe { /* iframe с сайта-жертвы */
[codetabs src="clickjacking" height=200]
-Итак, все, что нужно для проведения атаки -- это правильно расположить iframe на вредоносной странице. В большинстве случаев это делается средствами HTML/CSS.
+Итак, все, что нужно для проведения атаки -- это правильно расположить iframe на вредоносной странице, так чтобы кнопка с Facebook оказалась над "Жми тут!". В большинстве случаев это возможно и делается обычным CSS-позиционированием.
[smart header="С клавиатурой так не сделаешь"]
Атака назвается "Clickjacking", то есть "угон клика", так как события клавиатуры "угнать" гораздо труднее.
-
-Посетителя можно заставить сфокусироваться на `` с невидимого `