Сегодня вдруг задумался над вопросом, очень интересна блочная область видимости let и переменная ссылочная функция замыканий (с ней связана эта мозговая дыра, 囧). . Использование замыканий влияет на процесс сборки мусора в браузере. Так:
- Когда будут объекты JSАвтоматическая переработка?
- Как правильно использовать замыкания и избежать утечек памяти?
Давайте сначала взглянем на классический пример, проблему асинхронной печати в цикле (нетерпеливо перейдем сразу к последнему экземпляру (^▽^))
// 想异步打印1到5
for(var i=1; i<=5;i++) {
setTimeout(function(){
console.log("print: " + i);
}, i*1000)
}
// 结果
print: 6
print: 6
print: 6
print: 6
print: 6
Поскольку функция печати вызывается асинхронно, при вызове этой функции цикл завершается и i становится равным 6, поэтому подряд печатается 5 6.
Во втором случае, если для объявления i используется let, то let имеет как минимум следующие характеристики по сравнению с var:
- Переменная, объявленная let, имеетобласть действия блока
- Цикл формы for (let x...) создает новую привязку для x на каждой итерации (глубокая копия)
// 1到5
for(let i=1; i<=5;i++) {
setTimeout(function(){
console.log("print: " + i);
}, i*1000)
}
// 结果
print: 1
print: 2
print: 3
print: 4
print: 5
В этом случае непосредственно через let фактически создается замыкание для каждой регистрации callback-функции, поэтому печать идет нормально.
В третьем случае аналогичного эффекта можно добиться, создав замыкание вручную. В каждом цикле возвращайтеФункция ссылается на переменную в это времяя, это на самом делеперераспределенная памятьчтобы сохранить значение i, а не просто ссылаться на адрес памяти. Память Нимы увеличивается, но не стоит беспокоиться о таком небольшом количестве данных
// 1到5
for(var i=1; i<=5;i++) {
setTimeout((function(){
var b = i; //install timer的时候引用 i 并且return 一个函数
return function(){
console.log("print: " + b);
}
})(), i*1000)
}
Этот пример является хорошим примером того, как замыкание сохраняет адрес памяти внутренней переменной (1w глубоких копий i делается 1w раз в цикле). Однако неправильное использование замыканий и глобальных переменных может привести к утечке памяти, переполнению памяти или даже к прямому зависанию вкладки.
Будет ли переменная JS восстановлена GC в памяти браузера, зависит от жизненного цикла области, в которой находится переменная, и от того, ссылаются ли на эту переменную другие:
- Если это переменная, объявленная внутри функции, и нет внешней ссылки на переменную, функция будет уничтожена после выполнения. Если есть ссылка, то внутренняя переменная всегда будет свободна в памяти
Объекты JS (ссылочные типы) хранятся вкуча памяти, вы можете просмотреть его, создав моментальный снимок кучи с помощью инструмента «Профиль» инструмента отладки Chrome.
Наконец-то посмотрите на живом примере, не случайноИспользование памяти 1G в минуту:
function Test()
{
this.obj= {};
this.index = 1;
this.timer = null;
var cache = []; // 内部变量,内存隐患...
this.timer = window.setInterval(() =>{
this.index += 1;
this.obj = {
val: '_timerxxxxxbbbbxx_' + this.index,
junk: [...cache]
};
cache.push(this.obj);
}, 1);
console.warn("create Test instance..");
}
test = new Test(); // JS对象开启定时器不断分配内存
Несколько слов, ключ к этому примеру в том, что внутренний кеш переменныхвнешняя асинхронная функция(таймер) ссылка. Если вы не очистите таймер и просто вручную установите экземпляр класса Test в null, это не поможет, и кеш продолжит занимать память.
Test.prototype.destroy = function(){
clearInterval(this.timer);
}
function d() {
// 取消定时器并销毁Test 实例
test.destroy();
test = null;
console.warn("destroyed test instance..");
}
Сразу после очистки ссылки на внутренний кэш переменных размер кучи памяти уменьшился на 40 МБ.
Суммировать:
- Локальные переменные, которые не используются внутри функции, вовремя очищаются, и при очистке следует учитывать все ссылочные функции ta.
- Если вам нужно обратиться к локальным переменным, используйте неанонимные функции, иначе ссылку будет сложно уничтожить.
Личное мнение, приветствуются комментарии и исправления, если я не прав :)
Справочная статья: