[摘译]js内存走漏罕见的四种状况

本文重要选取了4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them 这篇文章中的一小部分来申明一下js中发作内存走漏的罕见状况. 关于较难明白的第四种状况, 参考了一些文章来举行申明.

不测的全局变量

js中假如不必var声明变量,该变量将被视为window对象(全局对象)的属性,也就是全局变量.

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

// 上面的函数等价于
function foo(arg) {
    window.bar = "this is an explicit global variable";
}

所以,你挪用完了函数今后,变量依然存在,致使走漏.

假如不注重this的话,还可能会这么漏:

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

// 没有对象挪用foo, 也没有给它绑定this, 所以this是window
foo();

你能够经由过程加上'use strict'启用严厉形式来防止这类题目, 严厉形式会构造你建立不测的全局变量.

被忘记的定时器或者回调

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

如许的代码很罕见, 假如idNode的元素从DOM中移除, 该定时器仍会存在, 同时, 由于回调函数中包括对someResource的援用, 定时器表面的someResource也不会被开释.

没有清算的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() {
    document.body.removeChild(document.getElementById('button'));
    
    // 虽然我们用removeChild移除了button, 然则还在elements对象里保留着#button的援用
    // 换言之, DOM元素还在内存内里.
}

闭包

先看如许一段代码:

var theThing = null;
var replaceThing = function () {
  var someMessage = '123'
  theThing = {
    someMethod: function () {
      console.log(someMessage);
    }
  };
};

挪用replaceThing以后, 挪用theThing.someMethod, 会输出123, 基础的闭包, 我想到这里应当不难明白.

解释一下的话, theThing包括一个someMethod要领, 该要领援用了函数中的someMessage变量, 所以函数中的someMessage变量不会被接纳, 挪用someMethod能够拿到它准确的console.log出来.

接下来我这么改一下:

var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var someMessage = '123'
  theThing = {
    longStr: new Array(1000000).join('*'),        // 也许占用1MB内存
    someMethod: function () {
      console.log(someMessage);
    }
  };
};

我们先做一个假定, 假如函数中所有的私有变量, 不论someMethod用不必, 都被放进闭包的话, 那末会发作什么呢.

第一次挪用replaceThing, 闭包中包括originalThing = nullsomeMessage = '123', 我们设函数结束时, theThing的值为theThing_1.

第二次挪用replaceThing, 假如我们的假定建立, originalThing = theThing_1someMessage = '123'.我们设第二次挪用函数结束时, theThing的值为theThing_2.注重, 此时的originalThing保留着theThing_1, theThing_1包括着和theThing_2判然不同的someMethod, theThing_1someMethod中包括一个someMessage, 一样假如我们的假定建立, 第一次的originalThing = null应当也在.

所以, 假如我们的假定建立, 第二次挪用今后, 内存中有theThing_1theThing_2, 由于他们都是靠longStr把占用内存撑起来, 所以第二次挪用今后, 内存斲丧比第一次多1MB.

假如你亲身试了(运用Chrome的Profiles检察每次挪用后的内存快照), 会发明我们的假定是不建立的, 浏览器很智慧, 它只会把someMethod用到的变量保留下来, 用不到的就不保留了, 这为我们节省了内存.

但假如我们这么写:

var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
    if (originalThing)
      console.log("hi");
  };
  var someMessage = '123'
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log(someMessage);
    }
  };
};

unused这个函数我们没有用到, 然则它用了originalThing变量, 接下来, 假如你一次次挪用replaceThing, 你会看到内存1MB 1MB的涨.

也就是说, 虽然我们没有运用unused, 然则由于它运用了originalThing, 使得它也被放进闭包了, 内存漏了.

强烈建议读者亲身尝尝在这几种状况下发作的内存变化.

这类状况发作的缘由, 浅显讲, 是由于不管someMethod照样unused, 他们个中所需要用到的在replaceThing中定义的变量是保留在一起的, 所以就漏了.

假如我没有申明第四种状况, 能够参考以下链接, 或是在批评区批评.

参考链接

    原文作者:LeuisKen
    原文地址: https://segmentfault.com/a/1190000004896090
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞