种常见的 Javascript 内存泄露
1: 意外的全局变量
function foo(arg) { bar = "this is a hidden global variable"; }
|
实际上是:
JavaScript
1 2 3 4 | function foo(arg) { window.bar = "this is an explicit global variable"; }
|
2: 被遗漏的定时器和回调函数
在 JavaScript 中 setInterval 的使用十分常见。其他的库也经常会提供观察者和其他需要回调的功能。这些库中的绝大部分都会关注一点,就是当它们本身的实例被销毁之前销毁所有指向回调的引用。
3 4 5 6 7 8 9 10 11 12 13 14 | var element = document.getElementById('button');
function onClick(event) { element.innerHtml = 'text'; }
element.addEventListener('click', onClick); // Do stuff element.removeEventListener('click', onClick); element.parentNode.removeChild(element); |
在 IE 浏览器中,每当一个观察者被添加到一个节点上时,就会发生一次内存泄漏。这也就是开发者在节点或者空的引用被添加到观察者中之前显式移除处理方法的原因。目前,现代的浏览器(包括 IE 和 Microsoft Edge)都使用了可以发现这些循环引用并正确的处理它们的现代化垃圾回收算法。换言之,严格地讲,在废弃一个节点之前调用 removeEventListener 不再是必要的操作。
3: DOM 之外的引用
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); // Much more logic }
function removeButton() { // The button is a direct child of body. document.body.removeChild(document.getElementById('button'));
// At this point, we still have a reference to #button in the global // elements dictionary. In other words, the button element is still in // memory and cannot be collected by the GC. } |
JavaScript 代码中对单元格的引用导致整个表格被保留在内存中。所以当你想要保留 DOM 元素的引用时,要仔细的考虑清除这一点。
堆、栈、队列之间的区别是?
①堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。
②栈就是一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来。(后进先出)
③队列只能在队头做删除操作,在队尾做插入操作.而栈只能在栈顶做插入和删除操作。(先进先出)
消费二进制数据
何谓消费?最常见的方式也许就是通过XHR2直接把二进制数据以文件方式POST到服务端去。这里我比较推荐使用FormData来构造POST数据。因为在服务端收的时候会比较容易一些,具体有兴趣可以去找找别人的例子。
例子以分片上传:
let i=0;
let $this = this;
let file = $("#postFolder")[0].files[0];
let str = file.name;
let name = file.name, //文件名
size = file.size, //总大小shardSize = 2 * 1024 * 1024,
shardSize = 2 * 1024 * 1024, //以2MB为一个分片
shardCount = Math.ceil(size / shardSize); //总片数
if (i >= shardCount) {
return;
}
//计算每一片的起始与结束位置
let start = i * shardSize,
end = Math.min(size, start + shardSize);
//构造一个表单,FormData是HTML5新增的
let formData = new FormData();
formData.append("data", file.slice(start, end)); //slice方法用于切出文件的一部分
formData.append("lastModified", file.lastModified);
formData.append("fileName", name); //文件名
formData.append("total", shardCount); //总片数
formData.append("index", i + 1); //当前是第几片
formData.append("size", size); //文件夹大小
formData.append("file", file);
formData.append("token", localStorage.loginToken);
if (str.substr(str.length - 3, 3) == "zip" ||str.substr(str.length - 3, 3) == "rar" || str.substr(str.length - 3, 3) == ".7z") {
if ($("#postFolder")[0].files[0].size / Math.pow(1024, 2) < 200) {
document.querySelector("#loadingWork").style.display = "block";
$.ajax({
url: `http://112.74.125.109:8007/api/Works/UploadWorksSourceFile`,
type: "POST",
data: formData,
contentType: false,
processData: false,
async: true, //异步
success: function(result) {
console.log(result);
if (result.resultCode == 1000) {
i = result.index++;
let num = Math.ceil(i * 100 / shardCount);
let progressWidth = 359 * num * 0.01;
$(".progressDiv").width(progressWidth);
console.log(progressWidth);
$this.uploadNumber = num + "%";
console.log(num + "%", $this.uploadNumber);
// $("#output").text(num + "%");
$this.postFolder(i);
if (result.complete) {
document.querySelector("#postFolderHtml").innerHTML =$("#postFolder")[0].files[0].name + ",上传成功";
document.querySelector("#loadingWork").style.display = "none";
WorksSourceFile = result.data;
}
} else {
alert(result.message);
}
},
error: function(result) {
document.querySelector("#postFolderHtml").innerHTML = "上传失败,重新上传";
// console.log(JSON.stringify(result));
}
});
} else {
this.dNotify("文件大小不能大于200M");
}
} else {
this.dNotify("请上传zip、rar、7z格式文件");
}