renovations
|
@ -1,6 +1,6 @@
|
|||
# Управление памятью в JavaScript
|
||||
|
||||
Управление памятью обычно незаметно. Мы создаём примитивы, объекты, функции.. Всё это занимает память.
|
||||
Управление памятью в JavaScript обычно происходит незаметно. Мы создаём примитивы, объекты, функции... Всё это занимает память.
|
||||
|
||||
Что происходит с объектом, когда он становится "не нужен"? Возможно ли "переполнение" памяти? Для ответа на эти вопросы -- залезем "под капот" интерпретатора.
|
||||
|
||||
|
@ -23,14 +23,20 @@
|
|||
|
||||
Для очистки памяти от недостижимых значений в браузерах используется автоматический <a href="http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)">Сборщик мусора</a> (англ. Garbage collection, GC), встроенный в интерпретатор, который наблюдает за объектами и время от времени удаляет недостижимые.
|
||||
|
||||
Самая простая ситуация здесь с примитивами. При присвоении они копируются целиком, ссылок на них не создаётся, так что если в переменной была одна строка, а её заменили на другую, то предыдущую можно смело выбросить.
|
||||
|
||||
Именно объекты требуют специального "сборщика мусора", который наблюдает за ссылками, так как на один объект может быть много ссылок из разных переменных и, при перезаписи одной из них, объект может быть всё ещё доступен из другой.
|
||||
|
||||
Далее мы посмотрим ряд примеров, которые помогут в этом разобраться.
|
||||
|
||||
### Достижимость и наличие ссылок
|
||||
|
||||
Можно сказать просто: "значение остаётся в памяти, пока на него есть ссылка". Но такое упрощение будет не совсем верным.
|
||||
Есть одно упрощение для работы с памятью: "значение остаётся в памяти, пока на него есть ссылка".
|
||||
|
||||
Но такое упрощение будет верным лишь в одну сторону.
|
||||
|
||||
<ul>
|
||||
<li>**Верно -- в том плане, что если на значение не остаётся ссылок, то память из-под него очищается.**
|
||||
<li>**Верно -- в том плане, что если ссылок на значение нет, то память из-под него очищается.**
|
||||
|
||||
Например, была создана ссылка в переменной, и эту переменную тут же перезаписали:
|
||||
|
||||
|
@ -41,9 +47,9 @@ user = null;
|
|||
|
||||
Теперь объект `{ name: "Вася" }` более недоступен. Память будет освобождена.
|
||||
</li>
|
||||
<li>**Неверно -- может быть так, что ссылка есть, но при этом значение недостижимо и должно быть удалено из памяти.**
|
||||
<li>**Неверно -- в другую сторону: наличие ссылки не гарантирует, что значение останется в памяти.**
|
||||
|
||||
Такая ситуация возникает с объектами, при наличии ссылок друг на друга:
|
||||
Такая ситуация возникает с объектами, которые ссылаются друг на друга:
|
||||
|
||||
```js
|
||||
var vasya = {};
|
||||
|
@ -58,276 +64,96 @@ vasya = petya = null;
|
|||
|
||||
Поэтому они будут удалены из памяти.
|
||||
|
||||
Чтобы отследить такие сложные случаи, придуман [сборщик мусора](http://ru.wikipedia.org/wiki/%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0_%D0%BC%D1%83%D1%81%D0%BE%D1%80%D0%B0), который время от времени перебирает объекты и ищет недоступные, с использованием хитрых алгоритмов и оптимизаций, чтобы это было быстро и незаметно.
|
||||
Здесь как раз и играет роль "достижимость" -- оба этих объекта становятся недостижимы из корней, в первую очередь, из глобальной области, стека.
|
||||
|
||||
[Сборщик мусора](http://ru.wikipedia.org/wiki/%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0_%D0%BC%D1%83%D1%81%D0%BE%D1%80%D0%B0) отслеживает такие ситуации и очищает память.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
## Управление памятью в картинках
|
||||
## Алгоритм сборки мусора
|
||||
|
||||
Рассмотрим пример объекта "семья":
|
||||
Сборщик мусора идёт от корня по ссылкам и запоминает все найденные объекты. По окончанию -- он смотрит, какие объекты в нём отсутствуют и удаляет их.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
Например, рассмотрим пример объекта "семья":
|
||||
|
||||
```js
|
||||
var family = { };
|
||||
function marry(man, woman) {
|
||||
woman.husband = man;
|
||||
man.wife = woman;
|
||||
|
||||
family.father = {
|
||||
name: "Вася"
|
||||
};
|
||||
return {
|
||||
father: man,
|
||||
mother: woman
|
||||
}
|
||||
}
|
||||
|
||||
family.mother = {
|
||||
name: "Маша"
|
||||
};
|
||||
var family = marry({ name: "Василий" }, { name: "Мария"});
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
Функция `marry` принимает два объекта, даёт им ссылки друг на друга и возвращает третий, содержащий ссылки на оба.
|
||||
|
||||
<img src="family.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
Этот код создаёт объект `family` и два дополнительных объекта, доступных по ссылкам `family.father` и `family.mother`.
|
||||
Получившийся объект `family` можно изобразить так:
|
||||
|
||||
### Недостижимый объект
|
||||
<img src="family.svg">
|
||||
|
||||
Теперь посмотрим, что будет, если удалить ссылку `family.father` при помощи `delete`:
|
||||
Здесь стрелочками показаны ссылки, а вот свойство `name` ссылкой не является, там хранится примитив, поэтому оно внутри самого объекта.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
Чтобы запустить сборщик мусора, удалим две ссылки:
|
||||
|
||||
```js
|
||||
var family = { };
|
||||
|
||||
family.father = {
|
||||
name: "Вася"
|
||||
};
|
||||
|
||||
family.mother = {
|
||||
name: "Маша"
|
||||
};
|
||||
|
||||
*!*
|
||||
```
|
||||
delete family.father;
|
||||
*/!*
|
||||
delete family.wife.husband;
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-nofatherlink.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
Обратим внимание, удаление только одной из этих ссылок ни к чему бы не привело. Пока до объекта можно добраться из корня `window`, объект остаётся жив.
|
||||
|
||||
### Пришёл сборщик мусора
|
||||
А если две, то получается, что от бывшего `family.father` ссылки выходят, но в него -- ни одна не идёт:
|
||||
|
||||
Сборщик мусора ищет недоступные объекты. Базовый алгоритм поиска -- это идти от корня (`window`) по ссылкам и помечать все объекты, которые встретит. Тогда после окончания обхода непомеченными останутся как раз недостижимые объекты.
|
||||
<img src="family-no-father.svg">
|
||||
|
||||
В нашем случае таким объектом будет бывший `family.father`. Он стал недостижимым и будет удалён вместе со своим "поддеревом", которое также более недоступно из программы.
|
||||
**Совершенно неважно, что из объекта выходят какие-то ссылки, они не влияют на достижимость этого объекта.**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
Бывший `family.father` стал недостижимым и будет удалён вместе со своми данными, которые также более недоступны из программы.
|
||||
|
||||
<img src="family-no-father-2.svg">
|
||||
|
||||
А теперь -- рассмотрим более сложный случай. Что будет, если удалить главную ссылку `family`?
|
||||
|
||||
Исходный объект -- тот же, что и в начале, а затем:
|
||||
|
||||
```js
|
||||
var family = {
|
||||
father: {
|
||||
name: "Вася"
|
||||
},
|
||||
|
||||
mother: {
|
||||
name: "Маша"
|
||||
}
|
||||
};
|
||||
|
||||
*!*
|
||||
delete family.father;
|
||||
*/!*
|
||||
window.family = null;
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-nofatherlink-junk.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
Результат:
|
||||
|
||||
### После сборщика
|
||||
<img src="family-no-family.svg">
|
||||
|
||||
После того, как сработает сборщик мусора, картина в памяти будет такой:
|
||||
Как видим, объекты в конструкции всё ещё связаны между собой. Однако, поиск от корня их не находит, они не достижимы, и значит сборщик мусора удалит их из памяти.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
[smart header="Оптимизации"]
|
||||
Проблема описанного алгоритма -- в больших задержках. Если объектов много, то на поиск всех достижимых уйдёт довольно много времени. А ведь выполнение скрипта при этом должно быть остановлено, уже просканированные объекты не должны поменяться до окончания процесса. Получатся небольшие, но неприятные паузы-зависания в работе скрипта.
|
||||
|
||||
Поэтому современные интерпретаторы применяют различные оптимизации.
|
||||
|
||||
Самая частая -- это деление объектов на два вида "старые" и "новые". Для каждого типа выделяется своя область памяти. Каждый объект создаётся в "новой" области и, если прожил достаточно долго, мигрирует в старую. "Новая" область обычно небольшая. Она очищается часто. "Старая" -- редко.
|
||||
|
||||
На практике получается эффективно, обычно большинство объектов создаются и умирают почти сразу, к примеру, служа локальными переменными функции:
|
||||
```js
|
||||
var family = {
|
||||
father: {
|
||||
name: "Вася"
|
||||
},
|
||||
|
||||
mother: {
|
||||
name: "Маша"
|
||||
}
|
||||
};
|
||||
|
||||
*!*
|
||||
delete family.father;
|
||||
*/!*
|
||||
function showTime() {
|
||||
alert( new Date() ); // этот объект будет создан и умрёт сразу
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-nofatherlink-junk-cleanup.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
Если вы знаете низкоуровневые языки программирования, то более подробно об организации сборки мусора в V8 можно почитать, например, в статье [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
|
||||
|
||||
|
||||
### Достижимость -- только по входящим ссылкам
|
||||
|
||||
Вернёмся к исходному коду.
|
||||
|
||||
**Пусть внутренние объекты ссылаются друг на друга:**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```js
|
||||
var family = {
|
||||
father: {
|
||||
name: "Вася"
|
||||
},
|
||||
|
||||
mother: {
|
||||
name: "Маша"
|
||||
}
|
||||
};
|
||||
|
||||
// добавим перекрёстных ссылок
|
||||
*!*
|
||||
family.father.wife = family.mother;
|
||||
family.mother.husband = family.father;
|
||||
family.father.we = family;
|
||||
family.mother.we = family;
|
||||
*/!*
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-ext.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Получилась сложная структура, с круговыми ссылками.
|
||||
|
||||
**Если удалить ссылки `family.father` и `family.mother.husband` (см. иллюстрацию ниже), то получится объект, который имеет исходящие ссылки, но не имеет входящих:**
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```js
|
||||
var family = {
|
||||
father: {
|
||||
name: "Вася"
|
||||
},
|
||||
|
||||
mother: {
|
||||
name: "Маша"
|
||||
}
|
||||
};
|
||||
|
||||
family.father.wife = family.mother;
|
||||
family.mother.husband = family.father;
|
||||
family.father.we = family;
|
||||
family.mother.we = family;
|
||||
|
||||
*!*
|
||||
delete family.father;
|
||||
delete family.mother.husband;
|
||||
*/!*
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-ext-nofatherlink-nohusband.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
При стандартном алгоритме очистки памяти, сборщик мусора пойдёт от корня и не сможет достичь объект, помеченный серым. Поэтому он будет удалён.
|
||||
|
||||
**И совершенно неважно, что из объекта выходят какие-то ссылки `wife`, `we`, они не влияют на достижимость этого объекта.**
|
||||
|
||||
### Недостижимый остров
|
||||
|
||||
Всё "семейство" объектов, которое мы рассматривали выше, достижимо исключительно через глобальную переменную `family` или, иными словами, через свойство `window.family`.
|
||||
|
||||
Если записать в `window.family` что-то ещё, то все они, вместе со своими внутренними ссылками станут "недостижимым островом" и будут удалены:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Код</th>
|
||||
<th>Структура в памяти</th>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```js
|
||||
var family = {
|
||||
father: {
|
||||
name: "Вася"
|
||||
},
|
||||
|
||||
mother: {
|
||||
name: "Маша"
|
||||
}
|
||||
};
|
||||
|
||||
family.father.wife = family.mother;
|
||||
family.mother.husband = family.father;
|
||||
family.father.we = family;
|
||||
family.mother.we = family;
|
||||
|
||||
*!*
|
||||
family = null;
|
||||
*/!*
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<img src="family-ext-nolink.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[/smart]
|
||||
|
||||
## Замыкания
|
||||
|
||||
Замыкания следуют тем же правилам, что и обычные объекты.
|
||||
Объекты переменных, о которых шла речь ранее, в главе про замыкания, также подвержены сборке мусора. Они следуют тем же правилам, что и обычные объекты.
|
||||
|
||||
**Объект переменных внешней функции существует в памяти до тех пор, пока существует хоть одна внутренняя функция, ссылающаяся на него через свойство `[[Scope]]`.**
|
||||
Объект переменных внешней функции существует в памяти до тех пор, пока существует хоть одна внутренняя функция, ссылающаяся на него через свойство `[[Scope]]`.
|
||||
|
||||
Например:
|
||||
|
||||
|
@ -336,7 +162,7 @@ family = null;
|
|||
|
||||
```js
|
||||
function f() {
|
||||
var value = Math.random();
|
||||
var value = 123;
|
||||
|
||||
function g() { } // g видна только изнутри
|
||||
}
|
||||
|
@ -344,13 +170,13 @@ function f() {
|
|||
f();
|
||||
```
|
||||
|
||||
В коде выше внутренняя функция объявлена, но она осталась внутри. После окончания работы `f()` она станет недоступной для вызовов, так что будет убрана из памяти вместе с остальными локальными переменными.
|
||||
В коде выше `value` и `g` являются свойствами объекта переменных. Во время выполнения `f()` её объект переменных находится в текущем стеке выполнения, поэтому жив. По окончанию, он станет недостижимым и будет убран из памяти вместе с остальными локальными переменными.
|
||||
</li>
|
||||
<li>...А вот в этом случае лексическое окружение, включая переменную `value`, будет сохранено:
|
||||
|
||||
```js
|
||||
function f() {
|
||||
var value = Math.random();
|
||||
var value = 123;
|
||||
|
||||
function g() { }
|
||||
|
||||
|
@ -362,7 +188,7 @@ function f() {
|
|||
var g = f(); // функция g будет жить и сохранит ссылку на объект переменных
|
||||
```
|
||||
|
||||
Причина сохранения проста: в скрытом свойстве `g.[[Scope]]` находится ссылка на объект переменных, в котором была создана `g`.
|
||||
В скрытом свойстве `g.[[Scope]]` находится ссылка на объект переменных, в котором была создана `g`. Поэтому этот объект переменных останется в памяти, а в нём -- и `value`.
|
||||
</li>
|
||||
<li>
|
||||
Если `f()` будет вызываться много раз, а полученные функции будут сохраняться, например, складываться в массив, то будут сохраняться и объекты `LexicalEnvironment` с соответствующими значениями `value`:
|
||||
|
@ -375,17 +201,16 @@ function f() {
|
|||
}
|
||||
|
||||
// 3 функции, каждая ссылается на свой объект переменных,
|
||||
// со своим значением value
|
||||
// каждый со своим значением value
|
||||
var arr = [f(), f(), f()];
|
||||
```
|
||||
|
||||
При этом совершенно не важно, имеет ли вложенная функция имя или нет.
|
||||
</li>
|
||||
<li>Объект `LexicalEnvironment` живёт ровно до тех пор, пока на него существуют ссылки. В коде ниже замыкание сначала сохраняется в памяти, а после удаления ссылки на `g` умирает:
|
||||
<li>Объект `LexicalEnvironment` живёт ровно до тех пор, пока на него существуют ссылки. В коде ниже после удаления ссылки на `g` умирает:
|
||||
|
||||
```js
|
||||
function f() {
|
||||
var value = Math.random();
|
||||
var value = 123;
|
||||
|
||||
function g() { }
|
||||
|
||||
|
@ -393,7 +218,7 @@ function f() {
|
|||
}
|
||||
|
||||
var g = f(); // функция g жива
|
||||
// а значит в памяти остается соответствующий объект переменных
|
||||
// а значит в памяти остается соответствующий объект переменных f()
|
||||
|
||||
g = null; // ..а вот теперь память будет очищена
|
||||
```
|
||||
|
@ -427,6 +252,8 @@ var g = f();
|
|||
g();
|
||||
```
|
||||
|
||||
Как вы могли увидеть -- нет такой переменной! Недоступна она изнутри `g`. Интерпретатор решил, что она нам не понадобится и удалил.
|
||||
|
||||
Это может привести к забавным казусам при отладке, вплоть до того что вместо этой переменной будет другая, внешняя:
|
||||
|
||||
```js
|
||||
|
@ -434,7 +261,7 @@ g();
|
|||
var value = "Сюрприз";
|
||||
|
||||
function f() {
|
||||
var value = "...";
|
||||
var value = "самое близкое значение";
|
||||
|
||||
function g() {
|
||||
debugger; // выполните в консоли alert(value); Сюрприз!
|
||||
|
@ -488,4 +315,6 @@ alert("Разница в " + ( timeRecursion / timeLoop ) + " раз");
|
|||
|
||||
Различие в скорости на таком примере может составлять, в зависимости от интерпретатора, 2-10 раз.
|
||||
|
||||
В большинстве ситуаций оптимизация по количеству создаваемых объектов несущественна, просто потому что "JavaScript и так достаточно быстр". Но она может быть важной для "узких мест" кода, а также при написании компьютерной графики и сложных вычислений на JS.
|
||||
Вообще, этот пример -- не показателен. Ещё раз обращаю ваше внимание на то, что такие искусственные "микротесты" часто врут. Правильно их делать -- отдельная наука, которая выходит за рамки этой главы. Но и на практике ускорение в 2-10 раз оптимизацией по количеству объектв (и вообще, любых значений) -- отнюдь не миф, а вполне достижимо.
|
||||
|
||||
В реальной жизни в большинстве ситуаций такая оптимизация несущественна, просто потому что "JavaScript и так достаточно быстр". Но она может быть эффективной для "узких мест" кода.
|
||||
|
|
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="120px" height="203px" viewBox="0 0 120 203" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>family-no-father-2</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="family-no-father-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="18" y="18" width="80" height="30"></rect>
|
||||
<text id="window" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="18" y="11">window</tspan>
|
||||
</text>
|
||||
<text id="Корень" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="35" y="37">Корень</tspan>
|
||||
</text>
|
||||
<rect id="Rectangle-2" stroke="#979797" fill="#E8E8E8" sketch:type="MSShapeGroup" x="18" y="85" width="80" height="28"></rect>
|
||||
<text id="Object" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="35" y="102">Object</tspan>
|
||||
</text>
|
||||
<text id="family" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="3" y="69">family</tspan>
|
||||
</text>
|
||||
<rect id="Rectangle-4" stroke="#979797" fill="#E8E8E8" sketch:type="MSShapeGroup" x="0" y="153" width="120" height="50"></rect>
|
||||
<text id="name:-"Мария"" sketch:type="MSTextLayer" font-family="Consolas" font-size="12" font-weight="normal" fill="#000000">
|
||||
<tspan x="23" y="191">name: "Мария"</tspan>
|
||||
</text>
|
||||
<text id="mother" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="5" y="136">mother</tspan>
|
||||
</text>
|
||||
<text id="Object-3" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="37" y="171">Object</tspan>
|
||||
</text>
|
||||
<path d="M59.5,50.5 L59.5,81.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M59.5,81.5 C60.55,77.72 61.45,74.48 62.5,70.7 C60.4,70.7 58.6,70.7 56.5,70.7 C57.55,74.48 58.45,77.72 59.5,81.5 C59.5,81.5 59.5,81.5 59.5,81.5 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
<path d="M58.5,117.5 L58.5,149.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M58.5,149.5 C59.55,145.72 60.45,142.48 61.5,138.7 C59.4,138.7 57.6,138.7 55.5,138.7 C56.55,142.48 57.45,145.72 58.5,149.5 C58.5,149.5 58.5,149.5 58.5,149.5 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 10 KiB |
59
1-js/5-functions-closures/6-memory-management/family.svg
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="329px" height="203px" viewBox="0 0 329 203" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>family</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="family" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<path d="M188.5,196.5 L129.5,196.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M129.5,196.5 C133.28,197.55 136.52,198.45 140.3,199.5 C140.3,197.4 140.3,195.6 140.3,193.5 C136.52,194.55 133.28,195.45 129.5,196.5 C129.5,196.5 129.5,196.5 129.5,196.5 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="118" y="18" width="80" height="30"></rect>
|
||||
<text id="window" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="118" y="11">window</tspan>
|
||||
</text>
|
||||
<text id="Корень" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="135" y="37">Корень</tspan>
|
||||
</text>
|
||||
<rect id="Rectangle-2" stroke="#979797" fill="#E8E8E8" sketch:type="MSShapeGroup" x="118" y="85" width="80" height="28"></rect>
|
||||
<text id="Object" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="135" y="102">Object</tspan>
|
||||
</text>
|
||||
<rect id="Rectangle-3" stroke="#979797" fill="#E8E8E8" sketch:type="MSShapeGroup" x="0" y="153" width="120" height="50"></rect>
|
||||
<text id="Object-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="35" y="171">Object</tspan>
|
||||
</text>
|
||||
<text id="father" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="31" y="133">father</tspan>
|
||||
</text>
|
||||
<text id="wife" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="143" y="154">wife</tspan>
|
||||
</text>
|
||||
<text sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="103" y="69">family</tspan>
|
||||
</text>
|
||||
<text id="name:-"Василий"" sketch:type="MSTextLayer" font-family="Consolas" font-size="12" font-weight="normal" fill="#000000">
|
||||
<tspan x="15" y="191">name: "Василий"</tspan>
|
||||
</text>
|
||||
<rect id="Rectangle-4" stroke="#979797" fill="#E8E8E8" sketch:type="MSShapeGroup" x="209" y="153" width="120" height="50"></rect>
|
||||
<text id="name:-"Мария"" sketch:type="MSTextLayer" font-family="Consolas" font-size="12" font-weight="normal" fill="#000000">
|
||||
<tspan x="226" y="191">name: "Мария"</tspan>
|
||||
</text>
|
||||
<text id="mother" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="238" y="133">mother</tspan>
|
||||
</text>
|
||||
<text id="Object-3" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="240" y="171">Object</tspan>
|
||||
</text>
|
||||
<path d="M159.5,50.5 L159.5,81.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M159.5,81.5 C160.55,77.72 161.45,74.48 162.5,70.7 C160.4,70.7 158.6,70.7 156.5,70.7 C157.55,74.48 158.45,77.72 159.5,81.5 C159.5,81.5 159.5,81.5 159.5,81.5 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
<path d="M110.5,118.5 L64.5,150.5" id="Line-2" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-2-decoration-1" d="M64.5293087,150.479611 C68.2319481,149.182938 71.405639,148.071504 75.1082783,146.77483 C73.9090429,145.05093 72.8811268,143.5733 71.6818914,141.849399 C69.1784875,144.869973 67.0327127,147.459037 64.5293087,150.479611 C64.5293087,150.479611 64.5293087,150.479611 64.5293087,150.479611 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
<path d="M129.5,162.5 L190.5,162.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#9B9B9B" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M190.5,162.5 C186.72,161.45 183.48,160.55 179.7,159.5 C179.7,161.6 179.7,163.4 179.7,165.5 C183.48,164.45 186.72,163.55 190.5,162.5 C190.5,162.5 190.5,162.5 190.5,162.5 Z" stroke="#979797" stroke-linecap="square" fill="#9B9B9B"></path>
|
||||
<text id="husband" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
|
||||
<tspan x="132" y="187">husband</tspan>
|
||||
</text>
|
||||
<path d="M204.5,118.5 L251.5,150.5" id="Line" stroke="#979797" stroke-linecap="square" fill="#979797" sketch:type="MSShapeGroup"></path>
|
||||
<path id="Line-decoration-1" d="M250.789558,150.016295 C248.255943,147.021016 246.084274,144.453634 243.550659,141.458354 C242.368798,143.194213 241.355774,144.682091 240.173913,146.41795 C243.889389,147.677371 247.074082,148.756874 250.789558,150.016295 C250.789558,150.016295 250.789558,150.016295 250.789558,150.016295 Z" stroke="#979797" stroke-linecap="square" fill="#979797"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.7 KiB |