在写这篇博客之前,其实我对闭包的理解停留在两个函数嵌套内函数可以访问外函数,会产生内存泄露这两个点上,确实也就这两个点,但是如果有人问你使用场景呢?为什么要用内函数去获取外函数的局部变量?有可能你会说返回给全局啊,这样全局可以用了,是的,但是,如果使这种需求,我直接把局部变量return 出去不就行啦,何必要再声明一个函数呢?
有人又会说内存泄露,你又猜对了,但是真的有人问你为什么会造成内存泄露,能举一个简单例子吗?你能写出来吗?能的话说明你不仅懂了闭包,其实你也已经懂了js回收机制。直到我看了阮一峰老师的文章确实简单易懂,在此谢谢并友情链接:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
容我像一个初学者一样慢慢道来:
// 最简单的闭包 function outer() { var a = 1; function inner() { console.log(a) }
// return a }
这是一个闭包,但这不足以造成内存泄露,如果想要返回局部变量a的值,直接在outer里 return a,所以说这么普通的闭包真的是又麻烦又没有任何效果
// 返回一个函数 function outer() { var a = 1; return function inner() { console.log(a) } }
这回高级一点了,返回了一个函数,那么返回一个函数难道就为了让外界获取局部a的值吗?显然有些花里胡哨,还是直接return a不好吗?花里胡哨复杂的代码,别人几行就能搞定,你说哪个牛?而用简单的方法就能实现的更牛。所以函数作为返回值绝不是仅仅用来让外界获取a的值的,而是外界更贪心,还想改变a的值。怎么做呢?请看:
function outer() { var a = 1; return function inner() { a = a + 1; console.log(a) } } // 调用返回的函数,闭包会导致每次调用都初始化,得出来的结果都一样,我们需要的是初始化步骤一次就够剩下的只交给inner() outer()(); // 2 outer()(); // 2
这不是闹着玩吗?outer()就是inner函数,outer()()就是inner() 再来几个outer()()都是2,这是因为每次执行outer()都会把a重新赋值为1,我就要每次调用inner()就要把a自增
// 闭包式调用 --- 这是我自己添加的名词,不是官方的,这其实就是摆脱了每次调用inner还要执行一次outer var fn = outer(); fn() // 2 fn() // 3
其实就是把返回的函数赋给了一个全局变量,简简单单的赋值,这就厉害了,如果用fn去掉用,就不会走var a = 1这条语句,也就是说不会在outer内重新赋值,而a不会走正常局部变量的该走的道路(局部变量执行就回收)。这就体现了第二个特点:局部变量难以回收。(造成内存泄漏) 这功能倒是适应以下情况:想让一个局部变量在原先处理的基础上操作,但是又需要初始化(这很重要),不想每次执行都把局部变量初始化了。有人一看:这简单,直接在全局里面写不就行了(码农);全局里面全局污染怎么办(相当于钱的余额能给别人查看和修改吗)(程序员), 防止全局污染使用函数,而在函数内要满足上述要求使用闭包,所以闭包也能防止变量污染。
斐波那契递归实现(闭包缓存版):
// 使用闭包缓存会减少很多的重复操作 function fbnq(n){ var cache = [] return function handle() { var res; if(n == 1 || n == 2){ return 1; } if (cache[n] == res){ return cache[n] }else{ res = fbnq(n-1) - fbnq(n-2) cache[n] = res; return res; } } return handle }
闭包讲完了,下面有兴趣的大佬们可以看看相关博客(其实是想听听大佬们的指点): js垃圾回收机制( 从这个角度也能了解把函数赋值给全局变量全局变量依赖内函数,那么内函数里面的变量就不能被清除【阮一峰的理解】 )