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://www.cnblogs.com/pureshee/p/14030038.html