renovations

This commit is contained in:
Ilya Kantor 2015-02-25 14:42:09 +03:00
parent 3a0d1ff2fe
commit 11f2d7352f
82 changed files with 437 additions and 321 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -1,2 +0,0 @@
# Анимация

View file

@ -1,14 +1,13 @@
# Кривые Безье
Кривые Безье используются в компьютерной графике для рисования плавных изгибов, в [CSS-анимации](#css-animation) для описания процесса анимации и много где ещё.
Кривые Безье используются в компьютерной графике для рисования плавных изгибов, в CSS-анимации и много где ещё.
Тему эту стоит изучить, чтобы в дальнейшем с комфортом пользоваться этим замечательным инструментом.
Несмотря на "умное" название -- это очень простая штука.
В принципе, можно создавать анимацию и без знания кривых Безье, но стоит один раз прочитать, что это такое, так как в векторной графике и продвинутых анимаций без них никак. Это образовательный минимум.
Тему эту стоит изучить один раз, чтобы в дальнейшем с комфортом пользоваться этим замечательным инструментом.
[cut]
<script>
if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")) {
document.write('<h2 style="color:red">Ваш браузер не поддерживает SVG. Живые примеры без него не работают :(</h2>');
}
</script>
## Виды кривых Безье
@ -16,29 +15,24 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
Их может быть две, три, четыре или больше. Например:
<table>
<tr>
<th>По двум точкам</th>
<th>По трём точкам</th>
<th>По четырём точкам</th>
</tr>
<tr>
<td>
По двум точкам:
<img src="bezier2.png">
</td>
<td>
По трём точкам:
<img src="bezier3.png">
</td>
<td>
По четырём точкам:
<img src="bezier4.png">
</td>
</tr>
</table>
Если вы посмотрите внимательно на эти кривые, то "на глазок" заметите:
<ol>
<li>**Точки не всегда на кривой.** Это совершенно нормально, как именно строится кривая мы рассмотрим чуть позже.</li>
<li>**Степень кривой равна числу точек минус один**.
На рисунках выше, соответственно, получаются для двух точек -- линейная кривая (прямая), для трёх точек -- квадратическая кривая (парабола), для четырёх -- кубическая.</li>
Для двух точек -- это линейная кривая (т.е. прямая), для трёх точек -- квадратическая кривая (парабола), для четырёх -- кубическая.</li>
<li>**Кривая всегда находится внутри [выпуклой оболочки](http://ru.wikipedia.org/wiki/%D0%92%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%B0%D1%8F_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B0), образованной опорными точками:**
<img src="bezier4-e.png"> <img src="bezier3-e.png">
@ -47,13 +41,13 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
</li>
</ol>
Основная ценность кривых Безье -- в том, что **кривую можно менять, двигая точки**. При этом **кривая меняется интуитивно понятным образом**.
Основная ценность кривых Безье -- в том, что, двигая точки, кривую можно менять, причём кривая при этом меняется интуитивно понятным образом.
Попробуйте двигать точки мышью в примере ниже:
[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370]
Как можно заметить, **кривая натянута по касательным 1 -> 2 и 3 -> 4.**
**Как можно заметить, кривая натянута по касательным 1 -> 2 и 3 -> 4.**
После небольшой практики становится понятно, как расположить точки, чтобы получить нужную форму. А, соединяя несколько кривых, можно получить практически что угодно.
@ -63,7 +57,9 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
## Математика
У кривых Безье есть математическая формула. Как мы увидим далее, в ней нет особенной необходимости, но для полноты картины -- вот она.
У кривых Безье есть математическая формула.
Как мы увидим далее, для пользования кривыми Безье знать её нет особенной необходимости, но для полноты картины -- вот она.
**Координаты кривой описываются в зависимости от параметра `t⋲[0,1]`**
@ -88,28 +84,39 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
</li>
</ul>
Эти уравнения -- векторные, т.е. вместо <code>P<sub>i</sub></code> нужно подставить координаты i-й опорной точки <code>(x<sub>i</sub>, y<sub>i</sub>)</code>.
Вместо <code>P<sub>i</sub></code> нужно подставить координаты i-й опорной точки <code>(x<sub>i</sub>, y<sub>i</sub>)</code>.
Формула даёт возможность строить кривые, но не очень понятно, почему они именно такие, и как зависят от опорных точек. С этим нам поможет разобраться другой алгоритм.
Эти уравнения векторные, то есть на для каждой из координат:
<ul>
<li><code>x = (1t)<sup>2</sup>x<sub>1</sub> + 2(1t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code></li>
<li><code>y = (1t)<sup>2</sup>y<sub>1</sub> + 2(1t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code></li>
</ul>
Вместо <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> подставляются координаты трёх опорных точек, и в то время как `t` пробегает множество от `0` до `1`, соответствующие значения `(x, y)` как раз и образуют кривую.
Впрочем, это чересчур наукообразно, не очень понятно, почему кривые именно такие, и как зависят от опорных точек. С этим нам поможет разобраться другой, более наглядный алгоритм.
## Рисование "де Кастельжо"
[Метод де Кастельжо](http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B4%D0%B5_%D0%9A%D0%B0%D1%81%D1%82%D0%B5%D0%BB%D1%8C%D0%B6%D0%BE) идентичен математическому определению кривой и наглядно показывает, как она строится.
Посмотрим его на примере трех точек (точки можно двигать). Нажатие на кнопку "edit" запустит демонстрацию.
Посмотрим его на примере трёх точек (точки можно двигать). Нажатие на кнопку "play" запустит демонстрацию.
[iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370]
**Алгоритм построения "де Кастельжо":**
**Алгоритм построения кривой по "методу де Кастельжо":**
<ol>
<li>Строятся отрезки между опорными точками 1-2-3. На рисунке выше они **чёрные**.</li>
<li>Рисуем опорные точки. В примере выше это `1`, `2`, `3`.</li>
<li>Строятся отрезки между опорными точками 1 -> 2 -> 3. На рисунке выше они **чёрные**.</li>
<li>Параметр `t` пробегает значения от `0` до `1`. В примере выше использован шаг `0.05`, т.е. в цикле `0, 0.05, 0.1, 0.15, ... 0.95, 1`.
Для каждого значения `t`:
Значение `t` пробегает интервал от 0 до 1, для каждого `t`:
<ol>
<li>На каждом из этих отрезков берётся точка, находящаяся от начала на расстоянии от 0 до `t` пропорционально длине. То есть, при `t=0` -- точка будет в начале, при `t=0.25` -- на расстоянии в 25% от начала отрезка, при `t=0.5` -- 50%(на середине), при `t=1` -- в конце. Так как **чёрных** отрезков -- два, то и точек выходит две штуки.</li>
<li>На каждом из этих отрезков берётся точка, находящаяся от начала на расстоянии от 0 до `t` пропорционально длине. Так как чёрных отрезков -- два, то и точек выходит две штуки.
То есть, при `t=0` -- точки будут в начале, при `t=0.25` -- на расстоянии в 25% от начала отрезка, при `t=0.5` -- 50%(на середине), при `t=1` -- в конце отрезка.
</li>
<li>Эти точки соединяются. На рисунке ниже соединяющий их отрезок изображён <span style="color:blue">синим</span>.
<table>
@ -121,7 +128,7 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
</table>
</li>
<li>На <span style="color:blue">получившемся</span> отрезке берётся точка на расстоянии, соответствующем `t`. То есть, для `t=0.25` получаем точку в конце первой четверти отрезка, для `t=0.5` -- в середине отрезка. На рисунке выше эта точка отмечена <span style="color:red">красным</span>.
<li>На <span style="color:blue">получившемся</span> отрезке берётся точка на расстоянии, соответствующем `t`. То есть, для `t=0.25` (первый рисунок) получаем точку в конце первой четверти отрезка, для `t=0.5` (второй рисунок) -- в середине отрезка. На рисунках выше эта точка отмечена <span style="color:red">красным</span>.
</li>
</ol>
</li>
@ -138,14 +145,14 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
Алгоритм:
<ul>
<li>Точки по порядку соединяются отрезками: `1-2`, `2-3`, `3-4`. Получается три чёрных отрезка.</li>
<li>Точки по порядку соединяются отрезками: 1 -> 2, 2 -> 3, 3 -> 4. Получается три чёрных отрезка.</li>
<li>На отрезках берутся точки, соответствующие текущему `t`, соединяются. Получается два <span style="color:#0A0">зелёных отрезка</span>.</li>
<li>На этих отрезках берутся точки, соответствующие текущему `t`, соединяются. Получается один <span style="color:blue">синий отрезок</span>.</li>
<li>На синем отрезке берётся точка, соответствующая текущему `t`. При запуске примера выше она <span style="color:red">красная</span>.</li>
<li>Эти точки описывают кривую.</li>
</ul>
Нажмите на кнопку "edit" в примере выше, чтобы увидеть это в действии.
Нажмите на кнопку "play" в примере выше, чтобы увидеть это в действии.
Ещё примеры кривых:
@ -165,12 +172,12 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
Аналогичным образом могут быть построены кривые Безье и более высокого порядка: по пяти точкам, шести и так далее. Но обычно используются 2-3 точки, а для сложных линий несколько кривых соединяются. Это гораздо проще с точки зрения поддержки и расчётов.
[smart header="Как провести кривую через нужные точки?"]
Этот вопрос не связан с кривыми Безье, но он иногда возникает в смежных задачах.
[smart header="Как провести кривую Безье через нужные точки?"]
Кривые Безье обычно проводятся через "опорные" точки, которые, как можно видеть из примеров выше, редко лежат на кривой.
Такая задача называется [интерполяцией](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D1%8F). Существуют математические формулы, которые подбирают коэффициенты кривой по точкам, исходя из требований, например [многочлен Лагранжа](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D1%87%D0%BB%D0%B5%D0%BD_%D0%9B%D0%B0%D0%B3%D1%80%D0%B0%D0%BD%D0%B6%D0%B0).
Если нужно провести кривую именно через нужные точки, то это уже другая задача. Она называется [интерполяцией](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D1%8F). Существуют математические формулы, которые подбирают коэффициенты кривой по точкам, исходя из требований, например [многочлен Лагранжа](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D1%87%D0%BB%D0%B5%D0%BD_%D0%9B%D0%B0%D0%B3%D1%80%D0%B0%D0%BD%D0%B6%D0%B0).
Как правило, в компьютерной графике для интерполяции используют кубические кривые, соединённых гладким образом. Вместе они выглядят как одна кривая. Это называется [интерполяция сплайнами](http://ru.wikipedia.org/wiki/%D0%9A%D1%83%D0%B1%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%81%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD).
Как правило, в компьютерной графике для интерполяции используют кубические кривые, соединённых гладким образом. Вместе они выглядят как одна кривая. Это называется [интерполяция сплайнами](http://ru.wikipedia.org/wiki/%D0%9A%D1%83%D0%B1%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%81%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD).
[/smart]
## Итого
@ -180,12 +187,16 @@ if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Basi
<li>Через процесс построения де Кастельжо.</li>
</ol>
С их помощью можно описать почти любую линию, особенно если соединить несколько.
Их удобство в том, что:
<ul>
<li>Можно легко нарисовать плавные линии вручную, передвигая точки мышкой.</li>
<li>Более сложные изгибы и линии можно составить, если соединить несколько кривых Безье.</li>
</ul>
Применение:
<ul>
<li>В компьютерной графике, моделировании, в графических редакторах. Шрифты описываются с помощью кривых Безье.</li>
<li>В веб-разработке -- для графики на Canvas или в формате SVG. Кстати, все живые примеры выше написаны на SVG. Фактически, это один SVG-документ, к которому точки передаются параметрами. Вы можете открыть его в отдельном окне и посмотреть исходник: <a href="/files/tutorial/browser/animation/bezier/demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1">demo.svg</a>.</li>
<li>В CSS-анимации, для задания временной функции.</li>
<li>В CSS-анимации, для задания траектории и скорости передвижения.</li>
</ul>

View file

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 596 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

@ -1,11 +1,10 @@
# CSS-анимация
# CSS-transitions
Все современные браузеры, кроме IE9- поддерживают <a href="http://www.w3.org/TR/css3-transitions/">CSS transitions</a>, которые позволяют реализовать анимацию средствами CSS, без привлечения JavaScript.
Большинство примеров из этой статьи не будут работать в IE9-.
[cut]
## Анимация свойства [#css-animation]
## Анимация свойства [#css-transition]
Идея проста. Вы указываете, что некоторое свойство будет анимироваться при помощи специальных CSS-правил. Далее, при изменении этого свойства, браузер сам обработает анимацию.
@ -18,7 +17,7 @@
}
```
Любое изменение фонового цвета будет анимироваться в течение 2-х секунд.
Теперь любое изменение фонового цвета будет анимироваться в течение 2-х секунд.
У свойства `"transition"` есть и короткая запись:
@ -28,46 +27,7 @@
}
```
Так как [стандарт CSS Transitions](http://www.w3.org/TR/css3-transitions/) находится в стадии разработки, то `transition` нужно снабжать браузерными префиксами.
### Пример
Этот пример работает во всех современных браузерах, не работает в IE9-.
```html
<style>
.animated {
-webkit-transition: background-color 2s;
-ms-transition: background-color 2s;
-o-transition: background-color 2s;
-moz-transition: background-color 2s;
transition: background-color 2s; /* без префикса - на будущее */
border: 1px solid black;
}
</style>
<div class="animated" onclick="this.style.backgroundColor='red'">
<span style="font-size:150%">Кликни меня</span>
</div>
```
<style>
.animated {
transition: background-color 2s;
-webkit-transition: background-color 2s;
-o-transition: background-color 2s;
-ms-transition: background-color 2s;
-moz-transition: background-color 2s;
border: 1px solid black;
}
</style>
<div class="animated" onclick="this.style.backgroundColor='red'">
<span style="font-size:150%">Кликни меня</span>
</div>
CSS-анимации особенно рекомендуются на мобильных устройствах. Они отрисовываются плавнее, чем JavaScript, и меньше нагружают процессор, так как используют графическую акселерацию.
## Полный синтаксис CSS
## Полный синтаксис
Свойства для CSS-анимаций:
<dl>
@ -76,41 +36,27 @@ CSS-анимации особенно рекомендуются на мобил
<dt>`transition-duration`</dt>
<dd>Продолжительность анимации. Если указано одно значение -- оно применится ко всем свойствам, можно указать несколько значений для разных `transition-property`.</dd>
<dt>`transition-timing-function`</dt>
<dd>[Кривая Безье](/bezier) по 4-м точкам, используемая в качестве временной функциии.</dd>
<dd>Кривая Безье по 4-м точкам, используемая в качестве временной функциии. Их мы изучим [чуть позже](/bezier)</dd>
<dt>`transition-delay`</dt>
<dd>Указывает задержку от изменения свойства до начала CSS-анимации.</dd>
</dl>
Свойство **`transition`** может содержать их все, в порядке: `property duration timing-function delay, ...`.
### Пример
## Пример
Анимируем одновременно цвет и размер шрифта:
```html
<!--+ run -->
<style>
.growing {
-webkit-transition: font-size 3s, color 2s;
-ms-transition: font-size 3s, color 2s;
-o-transition: font-size 3s, color 2s;
-moz-transition: font-size 3s, color 2s;
transition: font-size 3s, color 2s;
}
</style>
<button class="growing" onclick="this.style.fontSize='36px';this.style.color='red'">Кликни меня</button>
```
<style>
.growing {
-webkit-transition: font-size 3s, color 2s;
-ms-transition: font-size 3s, color 2s;
-o-transition: font-size 3s, color 2s;
-moz-transition: font-size 3s, color 2s;
transition: font-size 3s, color 2s;
}
</style>
<button class="growing" onclick="this.style.fontSize='36px';this.style.color='red'">Кликни меня</button>
## Временнáя функция
В качестве временной функции можно выбрать любую [кривую Безье](/bezier), удовлетворяющую условиям:

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

@ -0,0 +1,3 @@
# Анимация [todo]
CSS анимации. Контроль над ними из JavaScript. Анимации на чистом JavaScript.

View file

@ -13,53 +13,15 @@
## Коллекция утечек в IE
### Утечка DOM ↔ JS в IE7-
### Утечка DOM ↔ JS в IE8-
IE до версии 8 не умел очищать циклические ссылки, появляющиеся между DOM-объектами и объектами JavaScript. В результате и DOM и JS оставались в памяти навсегда.
[warn header="Пропустите эту секцию, если IE8- не нужен"]
Проблема была особенно серьезна в IE6 до SP3 (или до обновления июня 2007 года), там память не освобождалась даже при переходе на другую страницу.
В браузере IE8 была проведена серьёзная работа над ошибками, но утечка в IE8- появляется, если круговая ссылка возникает "через объект".
Сейчас она существует в IE6, 7, а также, в облегчённом варианте, в IE8 (см. далее). Если вы не поддерживаете IE8-, то можете пропустить эту секцию.
[/warn]
Чтобы было понятнее, о чём речь, посмотрите на следующий код. Он вызывает утечку памяти в IE8-:
Функция `setHandler` в примере ниже ведёт к утечке памяти в IE6,7:
```js
function setHandler() {
var elem = document.getElementById('id');
elem.onclick = function() {
/* может быть пустая функция, не важно */
};
}
```
Элемент `elem` здесь ссылается на JavaScript-функцию через ссылку `onclick` напрямую, через свойство, а функция ссылается на `elem` через замыкание.
<img src="ie1.png">
Здесь вместо DOM-элемента в IE может быть `XMLHttpRequest`, `ActiveX`, любой другой COM-объект. Круговая ссылка гарантирует утечку.
**Обойти утечки памяти в IE можно, разорвав циклические ссылки.**
Например, можно удалить ссылку на `elem` из замыкания, присвоив `elem = null`. Таким образом обработчик больше не ссылается на DOM-элемент. Циклическая ссылка разорвана:
<img src="ie2.png">
Больше информации об этой утечке вы можете почерпнуть из статей: <a href="http://msdn.microsoft.com/en-us/library/ms976398.aspx">Understanding and Solving Internet Explorer Leak Patterns</a> и <a href="http://msdn.microsoft.com/en-us/library/dd361842%28v=vs.85%29.aspx">Circular Memory Leak Mitigation</a>.
### Утечка DOM ↔ JS, вариант для IE8-
В браузере IE8 была проведена серьёзная работа над ошибками, и пример, описанный выше, больше не приводит к утечке.
**Но ситуация исправлена не до конца. Утечка в IE8- появляется, если круговая ссылка возникает "через объект".**
Чтобы было понятнее, о чём речь, посмотрите на следующий код. Он вызывает утечку памяти:
```js
function leak() {
// Создаём новый DIV, добавляем к BODY
var elem = document.createElement('div');
@ -80,11 +42,12 @@ function leak() {
}
```
<a href="/files/tutorial/leak/leak_ie8.html" target="_blank">Открыть в новом окне (в IE)</a>
Полный пример (только для IE8-, а также IE9 в режиме совместимости с IE8):
Этот пример, течёт в IE6,7,8, а также в IE9 в режиме совместимости с IE8. Проблема -- в круговой ссылке `elem.__expando__elem = elem`.
[codetabs src="leak-ie8"]
**Утечка может возникать и неявным образом, через замыкание:**
Круговая ссылка и, как следствие, утечка может возникать и неявным образом, через замыкание:
```js
function leak() {
@ -103,32 +66,17 @@ function leak() {
}
```
<a href="/files/tutorial/leak/leak_ie8_2.html" target="_blank">Открыть в новом окне (в IE)</a>
Полный пример (IE8-, IE9 в режиме совместимости с IE8):
**Без метода `method` здесь утечки не возникнет.**
[codetabs src="leak-ie8-2"]
Без привязки метода `method` к элементу здесь утечки не возникнет.
Бывает ли такая ситуация в реальной жизни? Или это -- целиком синтетический пример, для заумных программистов?
Да, конечно бывает. Например, при разработке графических компонент.
Во-первых, сам объект компонента хранит ссылку на его DOM-элемент, чтобы работать с ним. Например:
```js
function Menu(elem) {
elem.onclick = function() {
// ссылка на elem осталась в замыкании
};
}
var menu = new Menu(elem);
```
То есть, компонент всегда знает свой элемент.
Но бывают ситуации, когда нужно пойти в обратном направлении, а именно -- по элементу определить, какой на нём компонент. Например, при делегировании, чтобы передать обработку события на элементе соответствующему компоненту. Или при Drag'n'Drop, чтобы получить компонент, соответствующий элементу, на который произведён перенос.
Сама задача не является чем-то из ряда вон выходящим. Вполне естественно, что JS-компонент привязан к элементу, а элемент знает о компоненте на нём. Но в IE8- прямая привязка ведёт к утечке памяти!
Да, конечно бывает. Например, при разработке графических компонент -- бывает удобно присвоить DOM-элементу ссылку на JavaScript-объект, который представляет собой компонент. Это упрощает делегирование и, в общем-то, логично, что DOM-элемент знает о компоненте на себе. Но в IE8- прямая привязка ведёт к утечке памяти!
Примерно так:
```js
function Menu(elem) {
elem.onclick = function() {};
@ -136,12 +84,13 @@ function Menu(elem) {
var menu = new Menu(elem); // Menu содержит ссылку на elem
*!*
elem.menu = menu; // вот такая привязка или что-то подобное ведёт к утечке
elem.menu = menu; // такая привязка или что-то подобное ведёт к утечке в IE8
*/!*
```
<a href="/files/tutorial/leak/leak_ie8_widget.html" target="_blank">Открыть в новом окне (в IE)</a>
Полный пример (IE8-, IE9 в режиме совместимости с IE8):
Такая привязка удобна, т.к. мы по DOM-элементу можем получить JS-компонент, который к нему привязан. Но, как видим, ведёт к утечке в IE8-.
[codetabs src="leak-ie8-widget"]
### Утечка IE8 при обращении к коллекциям таблицы
@ -168,7 +117,9 @@ function leak() {
}
```
<a href="/files/tutorial/leak/leak_ie8_table.html" target="_blank">Открыть в новом окне (в IE)</a>
Полный пример (IE8):
[codetabs src="leak-ie8-table"]
Особенности:
<ul>
@ -217,10 +168,12 @@ function leak() {
Когда запускается асинхронный запрос `xhr`, браузер создаёт специальную внутреннюю ссылку (internal reference) на этот объект. находится в процессе коммуникации. Именно поэтому объект `xhr` будет жив после окончания работы функции.
**Когда запрос завершен, браузер удаляет внутреннюю ссылку, `xhr` становится недостижимым и память очищается... Везде, кроме IE8-.**
Когда запрос завершен, браузер удаляет внутреннюю ссылку, `xhr` становится недостижимым и память очищается... Везде, кроме IE8-.
<a href="/files/tutorial/leak/leak_ie8_xhr.html" target="_blank">Открыть в новом окне (в IE8-)</a> (откройте страницу и пусть поработает минут 20 - съест всю память, включая виртуальную).
Полный пример (IE8):
[codetabs src="leak-ie8-xhr"]
Чтобы это исправить, нам нужно разорвать круговую ссылку `XMLHttpRequest ↔ JS`. Например, можно удалить `xhr` из замыкания:
@ -248,9 +201,7 @@ function leak() {
<img src="xhr2.png">
Теперь циклической ссылки нет -- мы устранили утечку.
<a href="/files/tutorial/leak/leak_ie8_xhr_fixed.html" target="_blank">Посмотреть исправленный пример для IE в отдельном окне</a>.
Теперь циклической ссылки нет -- и не будет утечки.
## Объемы утечек памяти
@ -273,13 +224,11 @@ function f() {
}
```
**Пока функция `inner` остается в памяти, `LexicalEnvironment` с переменной большого объема внутри висит в памяти.**
Пока функция `inner` остается в памяти, `LexicalEnvironment` с переменной большого объема внутри висит в памяти.
Висит до тех пор, пока функция `inner` жива.
**Как правило, JavaScript не знает, какие из переменных функции `inner` будут использованы, поэтому оставляет их все.**
Исключение -- виртуальная машина V8 (Chrome, Opera, Node.JS), она обычно видит, что переменная не используется во внутренних функциях, и очистит память.
Как правило, JavaScript не знает, какие из переменных функции `inner` будут использованы, поэтому оставляет их все. Исключение -- виртуальная машина V8 (Chrome, Opera, Node.JS), она часто (не всегда) видит, что переменная не используется во внутренних функциях, и очистит память.
В других же интерпретаторах, даже если код спроектирован так, что никакой утечки нет, по вполне разумной причине может создаваться множество функций, а память будет расти потому, что функция тянет за собой своё замыкание.
@ -303,113 +252,6 @@ function f() {
}
```
## jQuery: утечки и борьба с утечками
[warn header="Изменения в jQuery 2"]
В jQuery 2 объект с данными элемента, о котором идёт речь далее, более недоступен извне. Он стал локальной переменной внутри jQuery с именем `data_priv`, явный доступ к внутренним данным более невозможен (или deprecated).
Но в остальном всё работает точно так, как описано, и с теми же последствиями.
[/warn]
**В jQuery для борьбы с утечками памяти в IE6-7 используется <a href="http://api.jquery.com/jQuery.data/">$.data</a> API. Однако, это может стать причиной новых(!) утечек, характерных для jQuery.**
Основной принцип `$.data` — это для любого JavaScript объекта сохранить/получить значение для элемента с помощью jQuery вызова:
```js
//+ run
$(document.body).data('prop', { anything: "любой объект" }) // set
alert( $(document.body).data('prop') ) // get
```
jQuery `elem.data(prop, val)` делает следующее:
<ol>
<li>Элемент получает уникальный идентификатор, если у него такого еще нет:
```js
elem[ jQuery.expando ] = id = ++jQuery.uuid; // средствами jQuery
```
`jQuery.expando` -- это случайная строка, сгенерированная jQuery один раз при входе на страницу. Уникальное свойство, чтобы ничего важного не перезаписать.</li>
<li>...А сами данные сохраняются в специальном объекте `jQuery.cache`:
```js
jQuery.cache[id]['prop'] = { anything: "любой объект" };
```
</li>
</ol>
Когда данные считываются из элемента:
<ol>
<li>Уникальный идентификатор элемента извлекается из `id = elem[ jQuery.expando]`.
<li>Данные считываются из `jQuery.cache[id]`.</li>
</ol>
Смысл этого API в том, что DOM-элемент никогда не ссылается на JavaScript объект напрямую. Задействуется идентификатор, а сами данные хранятся в `jQuery.cache`. Утечек в IE не будет.
К тому же все данные известны библиотеке, так что можно клонировать с ними и т.п.
**Как побочный эффект -- возникает утечка памяти, если элемент удален из DOM без дополнительной очистки.**
### Примеры утечек в jQuery
Следующий код создает jQuery-утечку во всех браузерах:
```js
$('<div/>')
.html(new Array(1000).join('text')) // div с текстом, возможна AJAX-загрузка
.click(function() { })
.appendTo('#data')
document.getElementById('data').innerHTML = ''; // (*)
```
<a href="/files/tutorial/leak/jquery2.html" target="_blank">Показать в отдельном окне</a>
Утечка происходит потому, что обработчик события в jQuery хранится в данных элемента. В строке `(*)` элемент удален очисткой родительского `innerHTML`, но в `jQuery.cache` данные остались.
Более того, система обработки событий в jQuery устроена так, что вместе с обработчиком в данных хранится и ссылка на элемент, так что в итоге оба -- и обработчик и элемент -- остаются в памяти вместе со всем замыканием!
**Ещё более простой пример утечки:**
Этот код создает утечку:
```js
function go() {
$('<div/>')
.html(new Array(1000).join('text'))
.click(function() { })
}
```
<a href="/files/tutorial/leak/jquery1.html" target="_blank">Показать в отдельном окне</a>
Причина здесь в том, что элемент `<div>` создан, но нигде не размещен :). После выполнения функции ссылка на него теряется. Но обработчик события `click` уже сохранил данные в `jQuery.cache`, которые застревают там навсегда.
### Используем jQuery без утечек
Чтобы избежать утечек, описанных выше, для удаления элементов используйте функции jQuery API, а не чистый JavaScript.
Методы <a href="http://api.jquery.com/remove/">remove()</a>, <a href="http://api.jquery.com/empty">empty()</a> и <a href="http://api.jquery.com/html">html()</a> проверяют дочерние элементы на наличие данных и очищают их. Это несколько замедляет процедуру удаления, но зато освобождается память.
**К счастью обнаружить такие утечки легко. Проверьте размер `$.cache.`** Если он большой и растет, то изучите кэш, посмотрите, какие записи остаются и почему.
### Улучшение производительности jQuery
У способа борьбы с утечками IE, применённого в jQuery, есть побочный эффект.
**Функции, удаляющие элементы, бегают по всему дереву DOM и очищают подэлементы.**
Представьте себе, что вы получили с сервера большую таблицу (в виде текста), вставили её в документ и хотите обновить. Вызов `$('table').remove()` будет бегать по всем ячейкам и искать в них данные. Но мы-то знаем, что обработчики назначены через делегирование, и тратить на это время ни к чему!
Чтобы "грязно" удалить элемент, без чистки, можно воспользоваться методом <a href="http://api.jquery.com/detach">detach()</a>. Его официальное назначение -- в том, чтобы убрать элемент из DOM, но сохранить возможность для вставки (и, соответственно, оставить на нём все данные). А неофициальное -- быстро убрать элемент из DOM. Если на нём нет данных и обработчиков, то всё хорошо.
В принципе, если хочется всё сделать чисто, но быстро -- никто не мешает сделать `elem.detach()` и поместить вызов `elem.remove()` в `setTimeout`. В результате очистка будет происходить ассинхронно и незаметно.
Итак, будем надеяться, что эта тема для вас теперь прозрачна и ясна и в следующих разделах мы не будем больше говорить об утечках в jQuery.
## Поиск и устранение утечек памяти
### Проверка на утечки
@ -472,9 +314,4 @@ firefox --profilemanager
Утечки памяти штука довольно сложная. В борьбе с ними вам определенно понадобится одна вещь: *Удача!*
<div style="text-align:center">
<img src="goodluck.png">
</div>
<p style="text-align:right;font-style:italic">Перевести с английского варианта<br> учебника помог Марат Шагиев</p>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=8">
</head>
<body>
<script>
// Утечка в IE8 standards mode, а также в IE9 в режиме IE8
// См. http://blog.j15r.com/2009/07/memory-leaks-in-ie8.html
function leak() {
// Создаём новый DIV, добавляем к BODY
var elem = document.createElement('div');
document.body.appendChild(elem);
elem.__expando = {
bigAss: new Array(1000000).join('lalala'),
method: function() {} // создаётся круговая ссылка через замыкание
};
// Удалить элемент из DOM. Браузер должен очистить память.
elem.parentElement.removeChild(elem);
}
</script>
<p>Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.</p>
<button onclick="leak()">Создать утечку!</button>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
var elem = document.createElement('div'); // любой элемент
// Течёт в настоящем IE8, Standards Mode
// Не течёт в других IE. Не течёт в IE9 в режиме совместимости с IE8
function leak() {
for (var i = 0; i < 2000; i++) {
elem.innerHTML = '<table><tr><td>1</td></tr></table>';
elem.firstChild.rows[0]; // просто доступ через rows[] приводит к утечке
// при том, что мы даже без сохраненяем значение в переменную
elem.removeChild(elem.firstChild); // удалить таблицу
// elem.innerHTML = '' очистил бы память, он работает по-другому, см. главу "управление памятью"
}
}
</script>
<p>Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.</p>
<button onclick="leak()">Создать утечку!</button>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=8">
</head>
<body>
<script>
// Утечка в IE8 standards mode, а также в IE9 в режиме IE8
// См. http://blog.j15r.com/2009/07/memory-leaks-in-ie8.html
function leak() {
// Создаём новый DIV, добавляем к BODY
var elem = document.createElement('div');
document.body.appendChild(elem);
// Записываем в свойство ссылку на объект
var menu = new Menu(elem);
elem.menu = menu;
// Удалить элемент из DOM. Браузер должен очистить память.
elem.parentElement.removeChild(elem);
}
function Menu(elem) {
elem.onclick = function() {};
this.bigAss = new Array(1000000).join('lalala');
}
</script>
<p>Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.</p>
<button onclick="leak()">Создать утечку!</button>
</body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>Страница создаёт объект <code>XMLHttpRequest</code> каждые 50мс.</p>
<p>Смотрите на память, она течёт в IE&lt;9.</p>
<script>
function leak() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'something.js?'+Math.random(), true);
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
document.getElementById('test').innerHTML++
}
}
xhr.send(null);
}
onload = function() {
setInterval(leak, 50);
}
</script>
<div>Количество запросов: <span id="test">0</span></div>
</body>
</html>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=8">
</head>
<body>
<script>
// Утечка в IE8 standards mode, а также в IE9 в режиме IE8
// См. http://blog.j15r.com/2009/07/memory-leaks-in-ie8.html
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);
}
</script>
<p>Нажимайте на кнопку и наблюдайте, как увеличивается количество занимаемой браузером памяти.</p>
<button onclick="leak()">Создать утечку!</button>
</body>
</html>

View file

@ -0,0 +1,125 @@
# Утечки памяти при использовании jQuery
В jQuery для хранения обработчиков событий и других вспомогательных данных, связанных с DOM-элементами, используется внутренний объект, который в jQuery 1 доступен через <a href="http://api.jquery.com/jQuery.data/">$.data</a>.
В jQuery 2 доступ к нему закрыт через замыкание, он стал локальной переменной внутри jQuery с именем `data_priv`, но в остальном всё работает точно так, как описано, и с теми же последствиями.
## $.data
Встроенная функция `$.data` позволяет хранить привязывать произвольные значения к DOM-узлам.
Например:
```js
// присвоить
$(document).data('prop', { anything: "любой объект" })
// прочитать
alert( $(document).data('prop').anything ) // любой объект
```
Реализована она хитрым образом. Данные не хранятся в самом элементе, а во внутреннем объекте jQuery.
jQuery-вызов `elem.data(prop, val)` делает следующее:
<ol>
<li>Элемент получает уникальный идентификатор, если у него такого еще нет:
```js
elem[ jQuery.expando ] = id = ++jQuery.uuid; // средствами jQuery
```
`jQuery.expando` -- это случайная строка, сгенерированная jQuery один раз при входе на страницу. Уникальное свойство, чтобы ничего важного не перезаписать.</li>
<li>...А сами данные сохраняются в специальном объекте `jQuery.cache`:
```js
jQuery.cache[id]['prop'] = { anything: "любой объект" };
```
</li>
</ol>
Когда данные считываются из элемента:
<ol>
<li>Уникальный идентификатор элемента извлекается из `id = elem[ jQuery.expando]`.
<li>Данные считываются из `jQuery.cache[id]`.</li>
</ol>
Смысл этого API в том, что DOM-элемент никогда не ссылается на JavaScript объект напрямую. Задействуется идентификатор, а сами данные хранятся в `jQuery.cache`. Утечек в IE не будет.
К тому же все данные известны библиотеке, так что можно клонировать с ними и т.п.
Как побочный эффект -- возникает утечка памяти, если элемент удален из DOM без дополнительной очистки.
## Примеры утечек в jQuery
Следующий код создает jQuery-утечку во всех браузерах:
```js
$('<div/>')
.html(new Array(1000).join('text')) // div с текстом, возможна AJAX-загрузка
.click(function() { })
.appendTo('#data')
document.getElementById('data').innerHTML = ''; // (*)
```
Полный пример:
[codetabs src="jquery-leak"]
Утечка происходит потому, что обработчик события в jQuery хранится в данных элемента. В строке `(*)` элемент удален очисткой родительского `innerHTML`, но в `jQuery.cache` данные остались.
Более того, система обработки событий в jQuery устроена так, что вместе с обработчиком в данных хранится и ссылка на элемент, так что в итоге оба -- и обработчик и элемент -- остаются в памяти вместе со всем замыканием!
Ещё более простой пример утечки:
Этот код также создает утечку:
```js
function go() {
$('<div/>')
.click(function() { })
}
```
...То есть, мы создаём элемент, вешаем на него обработчик... И всё.
Такой код ведёт к утечке памяти как раз потому, что элемент `<div>` создан, но нигде не размещен :). После выполнения функции ссылка на него теряется. Но обработчик события `click` уже сохранил данные в `jQuery.cache`, которые застревают там навсегда.
## Используем jQuery без утечек
Чтобы избежать утечек, описанных выше, для удаления элементов используйте функции jQuery API, а не чистый JavaScript.
Методы <a href="http://api.jquery.com/remove/">remove()</a>, <a href="http://api.jquery.com/empty">empty()</a> и <a href="http://api.jquery.com/html">html()</a> проверяют дочерние элементы на наличие данных и очищают их. Это несколько замедляет процедуру удаления, но зато освобождается память.
К счастью обнаружить такие утечки легко. Проверьте размер `$.cache`. Если он большой и растет, то изучите кэш, посмотрите, какие записи остаются и почему.
## Улучшение производительности jQuery
У способа организации внутренних данных, применённого в jQuery, есть важный побочный эффект.
Функции, удаляющие элементы, также должны удалить и связанные с ними внутренние данные. Для этого нужно для каждого удаляемого элемента проверить -- а нет ли чего во внутреннем хранилище? И, если есть -- удалить.
Представим, что у нас есть большая таблица `<table>`, и мы хотим обновить её содержимое на новое. Вызов `$('table').html(новые данные)` перед вставкой новых данных аккуратно удалит старые: пробежит по всем ячейкам и проверит внутреннее хранилище.
Если это большая таблица, то обработчики, скорее всего, стоят не на ячейках, а на самом элементе `<table>`, то есть используется делегирование. А, значит, тратить время на проверку всех подэлементов ни к чему.
Но jQuery-то об этом не знает!
Чтобы "грязно" удалить элемент, без чистки, мы можем сделать это через "обычные" DOM-вызовы или воспользоваться методом <a href="http://api.jquery.com/detach">detach()</a>. Его официальное назначение -- в том, чтобы убрать элемент из DOM, но сохранить возможность для вставки (и, соответственно, оставить на нём все данные). А неофициальное -- быстро убрать элемент из DOM, без чистки.
Возможен и промежуточный вариант: никто не мешает сделать `elem.detach()` и поместить вызов `elem.remove()` в `setTimeout`. В результате очистка будет происходить асинхронно и незаметно.
## Итого
<ul>
<li>Утечки памяти при использовании jQuery возможны, если через DOM-методы удалять элементы, к которым привязаны данные или обработчики.</li>
<li>Чтобы утечки не было, достаточно убедиться, что элемент удаляется при помощи методов jQuery.</li>
<li>Побочный эффект -- при удалении элементов jQuery должна проверить наличие данных для них. Это сильно замедляет процесс удаления большого поддерева DOM.</li>
<li>Если мы значем, что обработчиков и данных нет -- гораздо быстрее удалять элементы при помощи вызова `detach` или обычного DOM.</li>
</ul>

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
<div id="data"></div>
<script>
function go() {
$('<div/>')
.html(new Array(1000).join('text'))
.click(function() { })
.appendTo('#data');
document.getElementById('data').innerHTML = '';
}
var interval = setInterval(go, 10)
</script>
Утечка идёт...
<input type="button" onclick="clearInterval(interval)" value="stop"/>
</body>
</html>

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

View file

@ -132,20 +132,27 @@ window.isIE = window.isIE || getBrowserVersion();
## Убираем вызовы console.*
Минификатор имеет в своём распоряжении дерево кода и может удалить ненужные вызовы. Хотя по умолчанию в UglifyJS и GCC такого флага нет, код минификатора можно легко расширить, добавив эту возможность.
Минификатор имеет в своём распоряжении дерево кода и может удалить ненужные вызовы.
Для UglifyJS это делают опции компилятора:
<ul>
<li>Для UglifyJS функция обхода дерева:
<li>`drop_debugger` -- убирает вызовы `debugger`.</li>
<li>`drop_console` -- убирает вызовы `console.*`.</li>
</ul>
Можно написать и дополнительную функцию преобразования, которая убирает другие вызовы, например для `log.*`:
```js
//+ hide="Раскрыть функцию"
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] == "console") {
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)];
@ -161,22 +168,5 @@ function ast_squeeze_console(ast) {
};
```
Полный код, использующий эту функцию и модуль uglify-js для сжатия с убиранием вызова: [myuglify.js](/files/tutorial/compress/myuglify.js).
Вы можете добавить свои любимые опции и использовать его вместо поставляемого "из коробки".
</li>
<li>В GCC соответствующие опции называются `stripNameSuffixes` и `stripTypePrefixes`, но они скрыты при запуске минификатора из командной строки или через веб-сервис.
Чтобы их использовать, нужно либо поставить утилиту [plovr](http://plovr.com/), что эквивалентно стрельбе из пушки по воробьям, либо написать свой Java-код для вызова компилятора, который будет ставить опции.
Выглядеть этот код может [примерно так](https://groups.google.com/d/msg/closure-compiler-discuss/qqdnzikzpWA/8cPHKIR1svgJ).
</li>
</ul>
Аналогичным образом можно убрать и любой другой код, например вызовы вашего собственного логгера.
Эту функцию следует вызвать на результате `parse`, и она пройдётся по дереву и удалит все вызовы `log.*`.

View file

@ -1,2 +1,4 @@
# Оптимизация
Утечки памяти, увеличение скорости выполнения и загрузки скриптов.

View file

@ -1,2 +0,0 @@
# Сжатие JavaScript