本文重要选取了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);
如许的代码很罕见, 假如id
为Node
的元素从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 = null
和someMessage = '123'
, 我们设函数结束时, theThing
的值为theThing_1
.
第二次挪用replaceThing
, 假如我们的假定建立, originalThing = theThing_1
和someMessage = '123'
.我们设第二次挪用函数结束时, theThing
的值为theThing_2
.注重, 此时的originalThing
保留着theThing_1
, theThing_1
包括着和theThing_2
判然不同的someMethod
, theThing_1
的someMethod
中包括一个someMessage
, 一样假如我们的假定建立, 第一次的originalThing = null
应当也在.
所以, 假如我们的假定建立, 第二次挪用今后, 内存中有theThing_1
和theThing_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
中定义的变量是保留在一起的, 所以就漏了.
假如我没有申明第四种状况, 能够参考以下链接, 或是在批评区批评.