文档开发者中心
我的

【前台知识】JavaScript中的闭包

昨日在看JavaScript语言安全编码规范中有这么一条

问题来了,看不很懂。所以来捋一捋。

首先理清楚几个基本概念:

变量作用域

变量可分为全局变量和局部变量。全局变量的作用域就是全局性的,在 js 的任何地方都可以使用全局变量。在函数中使用 var 关键字声明变量,这时的变量即是局部变量,它的作用域只在声明该变量的函数内,在函数外面是访问不到该变量的。

var func = function(){

   var a = 'test';

   console.log(a);         // test

}

func();

console.log(a);             // Uncaught ReferenceError: a is not defined


变量生存周期

全局变量,生命周期是永久的。局部变量,当定义该变量的函数调用结束时,该变量就会被垃圾回收机制回收而销毁。再次调用该函数时又会重新定义了一个新变量。

正如上面那个例子,a为局部变量,func调用完之后,a就被销毁了。


看下面这个例子

var func = function(){

   var a = test;

   var func1 = function(){

       a += ' a';

       console.log(a);

   }

   return func1;

}

var func2 = func();

func2();                    // test a

func2();                    // test a a

func2();                    // test a a a


可以看出,在第一次调用完 func2 之后,func 中的变量 a 变成 'test a',而没有被销毁。因为此时 func1 形成了一个闭包,导致了 a 的生命周期延续了。

所以什么是闭包,闭包就是能够读取其他函数内部变量的函数。如上面这个例子,func1就是一个闭包,闭包使用了其他函数定义的变量使其不能销毁。


再来看一下存在闭包情况下的内存管理


在闭包中调用局部变量,会导致这个局部变量无法及时被销毁,相当于全局变量一样会一直占用着内存。如果需要回收这些变量占用的内存,可以手动将变量设置为null。

然而在使用闭包的过程中,比较容易形成 JavaScript 对象和 DOM 对象的循环引用,就有可能造成内存泄露。这是因为浏览器的垃圾回收机制中,如果两个对象之间形成了循环引用,那么它们都无法被回收。

function func() {

   var test = document.getElementById('test');

   test.onclick = function () {

       console.log('hello world');

   }

}

在上面例子中,func 函数中用匿名函数创建了一个闭包。变量 test 是 JavaScript 对象,引用了 id 为 test 的DOM 对象,DOM 对象的 onclick 属性又引用了闭包,而闭包又可以调用 test ,因而形成了循环引用,导致两个对象都无法被回收。要解决这个问题,只需要把循环引用中的变量设为 null 即可。

function func() {

   var test = document.getElementById('test');

   test.onclick = function () {

       console.log('hello world');

   }

   test = null;

}

如果在 func 函数中不使用匿名函数创建闭包,而是通过引用一个外部函数,也不会出现循环引用的问题。

function func() {

   var test = document.getElementById('test');

   test.onclick = funcTest;

}

function funcTest(){

   console.log('hello world');

}


那么现在回过头来理解安全编码规范中的那个问题




因此,互相持有,类似死锁一样,都不能释放掉,导致这部分内存不能释放,解决方式就是打开环,如方法二中所说的。 调用外部函数则断开了函数对element对象的引用。从而避免内存泄露。

使用闭包要注意如下问题


1.内存消耗

通常来说,函数的活动对象会随着执行期上下文一起销毁,但是,由于闭包引用另外一个函数的活动对象,因此这个活动对象无法被销毁,这意味着,闭包比一般的函数需要更多的内存消耗。尤其在IE浏览器中需要关注。由于IE使用非原生javascript对象实现DOM对象,因此闭包会导致内存泄露问题

2.性能问题

使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。

因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。


By HW WL

点赞
收藏
回复
分享
举报
浏览932 编辑于2020-07-31 07:39未知归属地
全部评论
最多点赞
最新发布
最早发布
暂无评论
暂无评论
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。