三分钟理解JS闭包

先一句话概括:
一个函数内(父)放另外一个函数(子),(子)函数拿到(父)函数的局部变量,(父)函数把(子)函数返回出去,此时(子)函数保留着(父)函数的私有变量,也就是占据着空间。这样,其他函数(隔壁老王)调用这个(子)函数的时候,其他函数(隔壁老王)就可以拿到那个(父)函数的局部变量了,调用一次拿到一次,不管那个(父)函数执行完毕没有。

function fn() {
  var i = 1
  return function () {
    console.log(i++)
  }
}

var f = fn()
f() //1
f() //2
f() //3

理解:
看上面的代码,在函数 a() 里面放一个函数 inc() ,inc() 拿到 a() 的局部变量 n 。
然后呢, a() 又把 inc 返回出去。 
这样,c = a() ,就相当于 c = inc() [注意:这时候 inc() 是拿到 a() 里面的 n 并且会保留着这个 n ,这个 n 不会随着 a() 销毁而销毁,也就是不会被垃圾回收机制回收]。
这样,c = a() 就相当于 c = inc() ,所以最后两句代码,是调用了两次 c() ,第一次调用 n++ = 1 , 第二次调用 n++ = 2 。

总结:
由于在JS中,变量的作用域属于函数作用域,在函数执行后作用域就会被清理、内存也随之被收回,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数—也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后,作用域内的值也不会被销毁。

闭包两大用处:

1) 一个是可以读取函数内部的变量;
2) 另一个就是让这些变量的值始终保存在内存中。

闭包的常见场景:
一般回调函数都有闭包的身影,一个 Ajax 请求的成功回调,一个事件绑定的回调方法,一个 setTimeout 的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对某个函数内部局部变量进行传递,当闭包函数(子函数)在别处被调用时,都有闭包的身影。

闭包注意的问题:
由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

内存泄露:
程序的运行需要内存。对于持续运行的服务进程,必须及时释放不再用到的内存,否则占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏。

 

posted @ 2022-04-29 12:19  RHCHIK  阅读(63)  评论(0编辑  收藏  举报