Прочитав ее, вы станете на один шаг ближе к написанию высокопроизводительного JavaScript.

внешний интерфейс JavaScript HTML CSS DOM

субтитры:Распространенные утечки памяти JavaScript

Это одна из серии статей, посвященных утечкам памяти в JavaScript. Из-за ограниченного времени процесс обновления будет немного медленным, но он будет продолжать обновляться. Я тоже учусь, и неизбежно, что мое понимание некоторых пунктов знаний не очень правильное, поэтому я ставлю статью именно на это.githubПо первому я хочу с вами поделиться, по второму — облегчить непрерывные обновления, а по третьему — облегчить исправление ошибок в реальном времени. Я также надеюсь, что студенты, прочитавшие эту статью, смогут поднять больше вопросов, и я продолжу улучшать статью на основе комментариев. Наконец, я надеюсь, что вы можете извлечь что-то из статьи----->🎉 приятного чтения, наслаждайтесь жизнью 🐳

✏️Пожалуйста, смотрите последний контент на github❗️

Ссылки на статьи о последовательности

Что такое утечка памяти

утечка памятиОтносится к отказу программы освободить память, которая больше не используется из-за недосмотра или ошибки. Утечка памяти относится не к физическому исчезновению памяти, а к тому, что после того, как приложение выделяет определенный сегмент памяти из-за ошибки проектирования, оно теряет контроль над сегментом памяти до освобождения сегмента памяти, что приводит к пустой трате времени. Память. Утечки памяти обычно могут быть проанализированы только программистами, имеющими доступ к исходному коду программы. Тем не менее, довольно много людей имеют привычку описывать любое нежелательное увеличение использования памяти как утечку памяти, хотя это и не совсем точно. ————wikipedia

⚠️Примечание: CG, отмеченный ниже, является аббревиатурой кнопки [Собрать мусор] Devtools в браузере Chrome, обозначающей операцию сборки мусора.

cg

неожиданная глобальная переменная

Как JavaScript обрабатывает необъявленные переменные: создайте ссылку на переменную глобального объекта (т. е. свойство глобального объекта, а не переменную, потому что к нему можно получить доступ черезdeleteудалять). Если бы в браузере глобальный объект был быwindowобъект.

Если необъявленная переменная кэширует большой объем данных, это приведет к освобождению данных только при закрытии окна или обновлении страницы. Это может привести к неожиданным утечкам памяти.

function foo(arg) {
    bar = "this is a hidden global variable with a large of data";
}

Эквивалентно:

function foo(arg) {
    window.bar = "this is an explicit global variable with a large of data";
}

Кроме того, поthisСоздайте неожиданные глобальные переменные:

function foo() {
    this.variable = "potential accidental global";
}

// 当在全局作用域中调用foo函数,此时this指向的是全局对象(window),而不是'undefined'
foo();

Решение:

добавить в файл javascript'use strict', включите строгий режим, который может эффективно избежать вышеуказанных проблем.

function foo(arg) {
    "use strict" // 在foo函数作用域内开启严格模式
    bar = "this is an explicit global variable with a large of data";// 报错:因为bar还没有被声明
}

Если вам нужно использовать глобальные переменные в функции, вы можете использовать следующий код вwindowВыше четко сказано:

function foo(arg) {
    window.bar = "this is a explicit global variable with a large of data";
}

Это не только хорошо читается, но и удобно для последующего обслуживания.

Когда дело доходит до глобальных переменных, вам нужно обратить внимание на те, которые используются для временного хранения больших объемов данных, обязательно установите для них значение null или переназначьте их после обработки данных. В качестве кешей также часто используются глобальные переменные.Как правило, кеши используются для оптимизации производительности.Для производительности лучше установить верхний предел размера кеша. Поскольку кеш нельзя восстановить, чем больше размер кеша, тем выше потребление памяти.

console.log

console.log: вывод сообщения в консоль веб-разработки, которая часто используется для отладки анализа во время разработки. Иногда при разработке нужно напечатать какую-то информацию об объекте, но забыть ее убрать при публикацииconsole.logОператор, который может вызвать утечку памяти.

переходя кconsole.logОбъект не может быть удален сборщиком мусора ♻️, потому что информацию об объекте необходимо просмотреть в средстве разработки после запуска кода. Так что лучше не в производственной средеconsole.logлюбой объект.

Пример------>demos/log.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Leaker</title>
</head>

<body>
  <input type="button" value="click">
  <script>
    !function () {
      function Leaker() {
        this.init();
      };
      Leaker.prototype = {
        init: function () {
          this.name = (Array(100000)).join('*');
          console.log("Leaking an object %o: %o", (new Date()), this);// this对象不能被回收
        },

        destroy: function () {
          // do something....
        }
      };
      document.querySelector('input').addEventListener('click', function () {
        new Leaker();
      }, false);
    }()
  </script>
</body>

</html>

Вот некоторый анализ в сочетании с Chrome Devtools-> Performance, Шаги операции следующие:

⚠️Примечание. Анализ лучше всего выполнять в скрытом окне, чтобы плагины браузера не влияли на результаты анализа.

  1. Включить запись пункта [Производительность]
  2. Выполните компьютерную графику, чтобы создать опорную линию
  3. Нажмите кнопку [click] три раза подряд, чтобы создать три новых объекта Leaker.
  4. Выполнить компьютерную графику
  5. остановить запись

Видно, что строка [JS Heap] не опустилась обратно на позицию контрольной строки в конце, очевидно, есть память, которая не была переработана. Если вы измените код на:

    !function () {
      function Leaker() {
        this.init();
      };
      Leaker.prototype = {
        init: function () {
          this.name = (Array(100000)).join('*');
        },

        destroy: function () {
          // do something....
        }
      };
      document.querySelector('input').addEventListener('click', function () {
        new Leaker();
      }, false);
    }()

Удалитьconsole.log("Leaking an object %o: %o", (new Date()), this);утверждение. Повторите вышеуказанные действия, результаты анализа следующие:

По результатам сравнительного анализа видно, чтоconsole.logНапечатанный объект не будет собран сборщиком мусора. Так что лучше не быть на страницеconsole.logЛюбые крупные объекты, которые могут повлиять на общую производительность страницы, особенно в рабочей среде. Кромеconsole.logКроме того, существуют такжеconsole.dir,console.error,console.warnи т.д. имеют схожие проблемы, и эти детали требуют особого внимания.

закрытия

Когда функция A возвращает встроенную функцию B, даже после выполнения функции A функция B может получить доступ к переменным в области действия функции A, которая является замыканием — по сути, замыкание — это функция внутри и снаружи функция, связанная мостом.

function foo(message) {
    function closure() {
        console.log(message)
    };
    return closure;
}

// 使用
var bar = foo("hello closure!");
bar()// 返回 'hello closure!'

Объект закрытия функции, созданный в функции foo, не может быть переработан, поскольку на него ссылается глобальная переменная bar, и он всегда доступен. выполнивbar()можно распечататьhello closure!. Если вы хотите выпустить его, вы можетеbar = nullВот и все.

Поскольку замыкание содержит область действия функции, которая его содержит, оно занимает больше памяти, чем другие функции. Чрезмерное использование замыканий может привести к чрезмерному использованию памяти.

Пример------>demos/closures.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Closure</title>
</head>

<body>
  <p>不断单击【click】按钮</p>
  <button id="click_button">Click</button>
  <script>
    function f() {
      var str = Array(10000).join('#');
      var foo = {
        name: 'foo'
      }
      function unused() {
        var message = 'it is only a test message';
        str = 'unused: ' + str;
      }
      function getData() {
        return 'data';
      }
      return getData;
    }

    var list = [];
    
    document.querySelector('#click_button').addEventListener('click', function () {
      list.push(f());
    }, false);
  </script>
</body>

</html>

Это сочетается с инструментом Chrome Devtools-> Memory для анализа.Этапы операции следующие:

⚠️Примечание. Анализ лучше всего выполнять в скрытом окне, чтобы плагины браузера не влияли на результаты анализа.

  1. Установите флажок [Временная шкала записи]
  2. Выполнить компьютерную графику
  3. Нажмите кнопку [Пуск], чтобы начать запись анализа кучи.
  4. Нажмите кнопку [щелчок] более десяти раз подряд
  5. Остановить анализ кучи

closure

Синие полосы на изображении выше представляют новую выделенную память с течением времени. Выберите одну из синих полос, чтобы отфильтровать вновь выделенные объекты:

closure

Посмотреть детали объекта:

closure

Как видно из рисунка, возвращаемая область замыкания (Scopes) несет в себе область видимости функции, в которой она находится, а область видимости также содержит поле str. Поле str не используется в возврате getData(). Почему он существует в области видимости, он должен быть переработан сборщиком мусора, почему:вопрос:

Причина в том, что несколько внутренних объектов-функций, созданных в одной и той же области видимости, совместно используют одни и те жепеременный объект. Если на созданную внутреннюю функцию не ссылаются другие объекты, независимо от того, ссылается ли внутренняя функция на переменные и функции внешней функции, соответствующий переменный объект будет уничтожен после выполнения внешней функции. И наоборот, если есть доступ к переменной внешней функции или функции во внутренней функции (которая может не быть внутренней функцией, на которую ссылаются), и есть одна или несколько внутренних функций, на которые ссылаются другие объекты, то будет сформировано замыкание, а объект переменной внешней функции будет существовать в цепочке областей видимости функции закрытия. Это гарантирует, что функция замыкания имеет доступ ко всем переменным и функциям внешней функции. Как только вы поймете причину проблемы, вы сможете лечить ее соответствующим образом. Измените код следующим образом:

    function f() {
      var str = Array(10000).join('#');
      var foo = {
        name: 'foo'
      }
      function unused() {
        var message = 'it is only a test message';
        // str = 'unused: ' + str; //删除该条语句
      }
      function getData() {
        return 'data';
      }
      return getData;
    }

    var list = [];
    
    document.querySelector('#click_button').addEventListener('click', function () {
      list.push(f());
    }, false);

Внутренние функции getData() и unused() совместно используют объект переменной, соответствующий функции f. Поскольку внутренняя функция unused() обращается к переменной str в области действия f, поле str существует в объекте переменной f. Кроме того, внутренняя функция getData() возвращается и на нее ссылаются другие объекты, образуя замыкание, поэтому соответствующий объект переменной f существует в цепочке областей видимости функции замыкания. Здесь просто поместите неиспользуемую функцию вstr = 'unused: ' + str;Удаление заявления решит проблему.

closure

Ознакомьтесь с информацией о закрытии:

closure

Утечка DOM

В JavaScript манипулирование DOM занимает очень много времени. Поскольку движок JavaScript/ECMAScript не зависит от движка рендеринга, а DOM находится в движке рендеринга, взаимный доступ требует определенного количества ресурсов. Например, в браузере Chrome DOM находится в WebCore, а JavaScript/ECMAScript — в V8. Если представить JavaScript/ECMAScript и DOM как два изолированных острова, эти два острова соединены платным мостом, и за проезд по мосту необходимо платить определенную «плату за мост». Каждый раз, когда JavaScript/ECMAScript обращается к DOM, он должен платить «плату за переход». Таким образом, чем больше раз выполняется доступ к DOM, тем выше стоимость и производительность страницы.Узнать большеℹ️

Чтобы уменьшить количество посещений DOM, как правило, когда к одному и тому же методу или свойству DOM необходимо обращаться несколько раз, ссылка DOM кэшируется в локальной переменной. Однако если вы забудете освободить соответствующую ссылку на DOM в коде после выполнения некоторых операций удаления и обновления, это вызовет утечку памяти DOM.

Пример------>demos/dom.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Dom-Leakage</title>
</head>
<body>
  <input type="button" value="remove" class="remove" style="display:none;">
  <input type="button" value="add" class="add">

  <div class="container">
    <pre class="wrapper"></pre>
  </div>
  <script>
    // 因为要多次用到pre.wrapper、div.container、input.remove、input.add节点,将其缓存到本地变量中,
    var wrapper = document.querySelector('.wrapper');
    var container = document.querySelector('.container');
    var removeBtn = document.querySelector('.remove');
    var addBtn = document.querySelector('.add');
    var counter = 0;
    var once = true;
    // 方法
    var hide = function(target){
      target.style.display = 'none';
    }
    var show = function(target){
      target.style.display = 'inline-block';
    }
    // 回调函数
    var removeCallback = function(){
      removeBtn.removeEventListener('click', removeCallback, false);
      addBtn.removeEventListener('click', addCallback, false);
      hide(addBtn);
      hide(removeBtn);
      container.removeChild(wrapper);
    }
    var addCallback = function(){
      wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n'));
      // 显示删除操作按钮
      if(once){
        show(removeBtn);
        once = false;
      }
    }
    // 绑定事件
    removeBtn.addEventListener('click', removeCallback, false);
    addBtn.addEventListener('click', addCallback, false);
  </script>
</body>
</html>

Вот некоторый анализ в сочетании с Devtools-> Производительность браузера Chrome, Шаги операции следующие:

⚠️Примечание. Анализ лучше всего выполнять в скрытом окне, чтобы плагины браузера не влияли на результаты анализа.

  1. Включить запись пункта [Производительность]
  2. Выполните компьютерную графику, чтобы создать опорную линию
  3. Нажмите кнопку [добавить] 6 раз подряд, чтобы добавить 6 текстовых узлов к элементу pre.
  4. Нажмите кнопку [удалить], чтобы удалить 6 текстовых узлов и элемент pre, которые были только что добавлены.
  5. Выполнить компьютерную графику
  6. Остановить анализ кучи

dom

Из результата анализа видно, что хотя 6 операций добавления увеличивают 6 Узлов, операция удаления не уменьшает количество Узлов, то есть операция удаления завершается неудачей. Кривая узлов не упала, несмотря на активную операцию компьютерной графики. Так что можно сделать вывод, что память утекла! Вопрос в том, как найти причину проблемы? Здесь вы можете выполнить диагностический анализ через Devtools->Память браузера Chrome и выполнить следующие шаги:

⚠️Примечание. Анализ лучше всего выполнять в скрытом окне, чтобы плагины браузера не влияли на результаты анализа.

  1. Выберите параметр [Сделать моментальный снимок кучи]
  2. Нажмите кнопку [добавить] 6 раз подряд, чтобы добавить 6 текстовых узлов к элементу pre.
  3. Нажмите кнопку [Сделать снимок], чтобы сделать снимок кучи
  4. Нажмите кнопку [удалить], чтобы удалить 6 текстовых узлов и элемент pre, которые были только что добавлены.
  5. Нажмите кнопку [Сделать снимок], чтобы сделать снимок кучи
  6. Выберите второй сгенерированный отчет о моментальных снимках, переключите представление с «Сводка» на режим сравнения «Сравнение» и введите ключевые слова в поле ввода фильтра [фильтр класса]:Detached

dom

Из графика результатов анализа видно, что причина, по которой весь элемент pre и 6 текстовых узлов не могут быть переработаны, заключается в том, что в коде есть глобальные переменные.wrapperСсылка на элемент pre. Как только вы узнаете причину проблемы, вы сможете лечить ее соответствующим образом. Измените код следующим образом:

    // 因为要多次用到pre.wrapper、div.container、input.remove、input.add节点,将其缓存到本地变量中,
    var wrapper = document.querySelector('.wrapper');
    var container = document.querySelector('.container');
    var removeBtn = document.querySelector('.remove');
    var addBtn = document.querySelector('.add');
    var counter = 0;
    var once = true;
    // 方法
    var hide = function(target){
      target.style.display = 'none';
    }
    var show = function(target){
      target.style.display = 'inline-block';
    }
    // 回调函数
    var removeCallback = function(){
      removeBtn.removeEventListener('click', removeCallback, false);
      addBtn.removeEventListener('click', addCallback, false);
      hide(addBtn);
      hide(removeBtn);
      container.removeChild(wrapper);
     
      wrapper = null;//在执行删除操作时,将wrapper对pre节点的引用释放掉
    }
    var addCallback = function(){
      wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n'));
      // 显示删除操作按钮
      if(once){
        show(removeBtn);
        once = false;
      }
    }
    // 绑定事件
    removeBtn.addEventListener('click', removeCallback, false);
    addBtn.addEventListener('click', addCallback, false);

При выполнении операции удаления освободить ссылку обертки на пре-узел, то есть добавить его в логику удаленияwrapper = null;утверждение. Повторите вышеуказанную операцию еще раз в Devtools->Performance:

dom

Маленький испытательный нож ------>demos/dom_practice.html

Давайте посмотрим на пример в Интернете, код выглядит следующим образом:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Practice</title>
</head>
<body>
  <div id="refA"><ul><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#" id="refB"></a></li></ul></div>
  <div></div>
  <div></div>

  <script>
    var refA = document.getElementById('refA');
    var refB = document.getElementById('refB');
    document.body.removeChild(refA);

    // #refA不能GC回收,因为存在变量refA对它的引用。将其对#refA引用释放,但还是无法回收#refA。
    refA = null;

    // 还存在变量refB对#refA的间接引用(refB引用了#refB,而#refB属于#refA)。将变量refB对#refB的引用释放,#refA就可以被GC回收。
    refB = null;
  </script>
</body>
</html>

Весь процесс показан на следующем рисунке:

Заинтересованные студенты могут использовать инструмент Chrome Devtools для проверки результатов анализа. Практика очень важна~~~:high_brightness:

timers

Обычно используется в JavaScriptsetInterval()для достижения некоторых анимационных эффектов. Конечно, вы также можете использовать цепочкуsetTimeout()Вызовите шаблон для достижения:

setTimeout(function() {
  // do something. . . .
  setTimeout(arguments.callee, interval);
}, interval);

если не нуженsetInterval()не прошелclearInterval()метод удален, затемsetInterval()будет продолжать вызывать функцию до тех пор, пока вызовclearInterval()или окно закрывается. Если прикованsetTimeout()Режим вызова не дает логики завершения и будет продолжать работать. Поэтому, когда больше нет необходимости повторять таймер, обязательно очистите таймер, чтобы не занимать системные ресурсы. Кроме того, при использованииsetInterval()а такжеsetTimeout()При реализации анимации невозможно гарантировать, что таймер выполняет анимацию с заданным интервалом. Для создания плавных и плавных анимаций в JavaScript браузеры добавили новый API для анимаций JavaScript — requestAnimationFrame().О разнице между setInterval, setTimeout и requestAnimationFrame для достижения анимации ➹ Slam 😊

Пример------>demos/timers.html

пройти следующим образомsetInterval()Реализуйте небольшой пример часов, но если есть проблема с кодом, заинтересованные студенты могут сначала попытаться найти проблему~~~~~~😎 работать:

  • Нажмите кнопку [пуск], чтобы запустить часы, и консоль веб-разработки распечатает информацию в реальном времени.
  • Нажмите кнопку [стоп], чтобы остановить часы, и консоль веб-разработки выведет информацию об остановке.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>setInterval</title>
</head>
<body>
  <input type="button" value="start" class="start">
  <input type="button" value="stop" class="stop">

  <script>
    var counter = 0;
    var clock = {
      start: function () {
        setInterval(this.step.bind(null, ++counter), 1000);
      },
      step: function (flag) {
        var date = new Date();
        var h = date.getHours();
        var m = date.getMinutes();
        var s = date.getSeconds();
        console.log("%d-----> %d:%d:%d", flag, h, m, s);
      }
    }
    document.querySelector('.start').addEventListener('click', clock.start.bind(clock), false);
    document.querySelector('.stop').addEventListener('click', function () {
      console.log('----> stop <----');
      clock = null;
    }, false);
  </script>
</body>
</html>

В приведенном выше коде есть две проблемы:

  1. Если вы постоянно нажимаете кнопку [Пуск], будут сгенерированы новые часы.

  2. Нажмите кнопку [стоп], чтобы остановить часы.

Выходной результат:

В ответ на обнаруженные проблемы внесите следующие изменения в код:

    var counter = 0;
    var clock = {
      timer: null,
      start: function () {
        // 解决第一个问题
        if (this.timer) {
          clearInterval(this.timer);
        }
        this.timer = setInterval(this.step.bind(null, ++counter), 1000);
      },
      step: function (flag) {
        var date = new Date();
        var h = date.getHours();
        var m = date.getMinutes();
        var s = date.getSeconds();
        console.log("%d-----> %d:%d:%d", flag, h, m, s);
      },
      // 解决第二个问题
      destroy: function () {
        console.log('----> stop <----');
        clearInterval(this.timer);
        node = null;
        counter = void(0);
      }
    }
    document.querySelector('.start').addEventListener('click', clock.start.bind(clock), false);
    document.querySelector('.stop').addEventListener('click', clock.destroy.bind(clock), false);

EventListener

При разработке мобильных приложений вам необходимо адаптироваться к разным размерам устройств. Например, при разработке компонентов иногда необходимо учитывать адаптацию горизонтальных и вертикальных экранов. Как правило, при изменении горизонтального и вертикального экранов компоненты необходимо разрушать, а затем восстанавливать. В компоненте оно будет привязано к связанным событиям.Если событие компонента не отвязано при уничтожении компонента, то при смене горизонтального и вертикального экранов событие будет привязано к компоненту непрерывно. Это вызовет некоторые исключения и может даже привести к сбою страницы.

Пример------>demos/callbacks.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>callbacks</title>
</head>
<body>
  <div class="container"></div>
  <script>
    var container = document.querySelector('.container');
    var counter = 0;
    var createHtml = function (n, counter) {
      var template = `${(new Array(n)).join(`<div>${counter}: this is a new data <input type="button" value="remove"></div>`)}`
      container.innerHTML = template;
    }
   
    var resizeCallback = function (init) {
      createHtml(10, ++counter);
      // 事件委托
      container.addEventListener('click', function (event){
        var target = event.target;
          if(target.tagName === 'INPUT'){
              container.removeChild(target.parentElement)
          }
      }, false);   
    }
    window.addEventListener('resize', resizeCallback, false);
    resizeCallback(true);
  </script>
</body>
</html>

Проблема со страницей. Вот комбинация Devtools->Performance для анализа проблемы. Этапы операции следующие:

⚠️Примечание. Анализ лучше всего выполнять в скрытом окне, чтобы плагины браузера не влияли на результаты анализа.

  1. Включить запись элемента производительности
  2. Выполните компьютерную графику, чтобы создать опорную линию
  3. изменить размер окна
  4. Выполнить компьютерную графику
  5. остановить запись

callbacks

Как показывают результаты анализа, при изменении размера окнаcontainerДобавьте делегированные события.

Если для одного и того же узла элемента зарегистрировано несколько идентичных EventListener, повторяющиеся экземпляры будут отброшены. Это не приведет к повторному вызову EventListener, и нет необходимости вручную удалять избыточный EventListener с помощью removeEventListener, поскольку дубликаты автоматически отбрасываются. И это правило применяется только к именованным функциям.Для анонимных функций браузер увидит их как другой EventListener., так что просто назовите анонимный EventListener, чтобы решить проблему:

    var container = document.querySelector('.container');
    var counter = 0;
    var createHtml = function (n, counter) {
      var template = `${(new Array(n)).join(`<div>${counter}: this is a new data <input type="button" value="remove"></div>`)}`
      container.innerHTML = template;
    }
    // 
    var clickCallback = function (event) {
      var target = event.target;
      if (target.tagName === 'INPUT') {
        container.removeChild(target.parentElement)
      }
    }
    var resizeCallback = function (init) {
      createHtml(10, ++counter);
      // 事件委托
      container.addEventListener('click', clickCallback, false);
    }
    window.addEventListener('resize', resizeCallback, false);
    resizeCallback(true);

Повторите описанную выше операцию в Devtools->Performance, и результаты анализа будут следующими:

callback

В разработке разработчики редко обращают внимание на отвязку событий, потому что браузер уже обрабатывает это за нас. Однако при использовании сторонних библиотек нужно уделять особое внимание, так как обычно сторонние библиотеки реализуют свои собственные привязки событий.Если несвязанный метод не вызывается, когда привязку события необходимо уничтожить во время использования, это может количество привязок событий продолжает расти. Следующая ссылка — это когда я использую jquery в своем проекте и сталкиваюсь с похожими проблемами:Забыл отвязать зарегистрированные события в jQuery, что привело к утечке памяти➹Bash😊

Суммировать

В этой статье в основном представлены несколько распространенных утечек памяти. В процессе разработки нам нужно обратить особое внимание на несколько утечек памяти, о которых идет речь в этой статье. Поскольку они могут происходить в нашем повседневном развитии в любое время, трудно обнаружить их существование, если мы их не понимаем. Возможно, они привлекают наше внимание только тогда, когда преувеличивают влияние проблемы. Но тогда может быть слишком поздно, потому что продукт может быть запущен, и тогда это серьезно повлияет на качество продукта и пользовательский опыт, и даже может привести к потере большого количества пользователей. Как разработчики, мы должны хорошо воспринимать этот уровень и позволить продуктам, которые мы разрабатываем, приносить пользователям наилучшие впечатления.

Справочная статья: