常见的 JavaScript 内存泄露

点点圈 提交于 2019-12-20 18:12:16

内存泄漏:由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

1、意外的全局变量

js对未声明变量会在全局最高对象上创建它的引用,(是以属性存在的,而不是变量),如果在游览器上就是window对象,如果在node环境下就是global;如果未声明的变量缓存大量的数据,它可能只有在页面被刷新或者被关闭的时候才会释放内存,这样就造成了内存意外泄漏。如下例子:

function my() {
    name="my name is tokey"
}
my()
console.log(window)

在控制台可以看到

还有通过this创建意外的全局变量

function my() {
    this.name="my name is tokey"
}
my()
console.log(window.name)  //my name is tokey 此时的this指向的并不是undefined而是全局对象window

针对上面类型的内存泄漏我们在平时一定要声明变量,不要有全局直接引用。(在JavaScript文件中添加 'use strict',开启严格模式,可以有效地避免上述问题。)

2、console.log

作为前端平时使用console.log在控制台打出相对应的信息可以说是非常常见。但如果没有去掉console.log可能会存在内存泄漏。因为在代码运行之后需要在开发工具能查看对象信息,所以传递给console.log的对象是不能被垃圾回收。

3、闭包

首先闭包是一个函数A返回一个内联的函数B,及时A函数执行完函数B也可以访问函数A里面的变量,这就是一个简单的闭包。本质上闭包是将函数内部和外部连接起来的一座桥梁。

function my(name) {
    function sendName() {
        console.log(name)
    }
    return sendName
}
var test=my("tokey")
test()  //tokey

在my()内部创建的sendName()函数是不会被回收的,因为它被全局变量test引用,处于随时被调用的状态。如果向释放内存可以设置test=null;由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多。

4、DOM泄漏

游览器中DOM和js采用的是不一样的引擎,DOM采用的是渲染引擎,而js采用的是v8引擎,所以在用js操作DOM时会比较耗费性能,因为他们需要桥来链接他们。为了减少DOM的操作,我们一般将常用的DOM。我们会采用变量引用的方式会将其缓存在当前环境。如果在进行一些删除、更新操作之后,可能会忘记释放已经缓存的DOM,话不多说直接来个例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,initial-scale=1.0, width=device-width">
    <title>Title</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="main">
    <div class="test">天</div>
    <div class="item">天</div>
    <div class="item">向</div>
</div>
<button id="add">点击我增加</button>
<button id="remove">点击我减少</button>
</body>
<script src="js/Zepto.js"></script>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var add=document.querySelector("#add");
var remove=document.querySelector("#remove");
var main=document.querySelector(".main")
var test=document.querySelector(".test")
add.onclick=function () {
    var itemN=document.createElement('div')
    var txt=document.createTextNode('上')
    itemN.appendChild(txt)
    main.appendChild(itemN)
}
remove.onclick=function () {
    main.removeChild(test)
}
</script>
</html>

通过chrome游览器的内存调试工作我们可以看到

在我点击了三次增加之后,可以明显的看到节点(绿线)有三次明显的增加,之后我又删除了一个节点,但绿线没有下降,这是为什么呢?,这也就是内存泄漏。原因就是删除的DOM在js中有全局的引用。也就是我删除的test在文中被引用,所以无法释放内存。所以在删除更新等操作后应该将其设置为null。

5、被遗忘的timers

js中常用的定时器setInterval()、setTimeout().他们都是规定延迟一定的时间执行某个代码,而其中setInterval()和链式setTimeout()在使用完之后如果没有手动关闭,会一直存在执行占用内存,所以在不用的时候我们可以通过clearInterval()、clearTimeout() 来关闭其对应的定时器,释放内存。熟悉朋友都知道这类定时器是有误差的,所以游览器给出了专门的API-requestAnimationFrame();大家可以试一下。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!