首页 > 其他 > 详细

调用堆栈(五)-内存泄漏

时间:2020-11-24 18:57:46      阅读:32      评论:0      收藏:0      [点我收藏+]

四种常见的JS内存泄漏

1、意外的全局变量

JavaScript处理未定义变量的方式比较宽松:未定义的变量会在全局对象创建一个新变量。在浏览器中,全局对象是window。

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

真相是:

  function foo(arg) {
        window.bar = "this is a hidden global variable";
  }

函数foo内部忘记使用var,意外创建了一个全局变量。

另外一种意外可能是this创建:

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

在 JavaScript 文件头部加上 ‘use strict‘,可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。

2、被遗忘的计时器或回调函数
在JavaScript中使用setInterval非常平常。一段常见的代码:

  var someResource = getData();
  setInterval(function(){
        var node = document.getElementById(‘Node‘);
        if(node) {
              // 处理node和someResource
              node.innerHTML = JSON.stringify(someResource);
        }
  }, 1000)

此例说明了什么:与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。

对于观察者的例子,一旦它们不再需要(或者关联的对象变成不可达),明确地移除它们非常重要。老的 IE 6 是无法处理循环引用的。如今,即使没有明确移除它们,一旦观察者对象变成不可达,大部分浏览器是可以回收观察者处理函数的。

观察者代码示例:

  var element = document.getElementById(‘button‘);
  function onClick(event) {
      element.innerHTML = ‘text‘;
  }
  element.addEventListener(‘click‘, onClick);

老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,会导致内存泄漏。如今,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。

3、脱离DOM的引用
有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。

  var elements = {
      button: document.getElementById(‘button‘),
      image: document.getElementById(‘image‘),
      text: document.getElementById(‘text‘)
  };
  function doStuff() {
      image.src = ‘http://some.url/image‘;
      button.click();
      console.log(text.innerHTML);
      // 更多逻辑
  }
  function removeButton() {
      // 按钮是 body 的后代元素
      document.body.removeChild(document.getElementById(‘button‘));
      // 此时,仍旧存在一个全局的 #button 的引用
      // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
  }

4、闭包

 function fn () {
      var str = "aaaaa";
      return function () {
          console.log(str);
      };
  }

变量a被fn()函数内的匿名函数所引用,因此这种变量是不会被回收的。

记录一下一开始自己的问题:

  <body>
      <div id="div">fasdf</div>
    <script>
        var list = []
        var divs = document.getElementsByTagName("div")

        list.push(divs[0])
        document.body.removeChild(divs[0])

        console.log(divs)
        console.dir(list)
    </script>
  </body>

技术分享图片

这里我一开始认为数组和变量是对同一个对象的引用,所以他俩应该都为空。
很明显这是错误的。
首先我们应该把div这个标签元素当做对象存在内存中,dom节点对象引用了它,所以在页面中才显示了出来。当我们通过let a = document.getElementById(‘div‘)获取的时候,获取到的直接是对内存中这个对象的引用。然后进行remove删除操作的时候,是删掉了dom节点对象对它的引用,而不会影响a对它的引用。
我们上面的代码var divs = document.getElementsByTagName("div"),获取的divs是对dom节点对象的引用,而这个dom节点对象里又有对div标签元素的引用,当我们push操作时push的是对内存中div标签元素的引用,所以remove之后,dom节点对象失去了对它的引用,结果显示HTMLCollection[], 而数组list并不为空。

参考:
https://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/

调用堆栈(五)-内存泄漏

原文:https://www.cnblogs.com/pureshee/p/14030038.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!