关于闭包的几点认识
什么是闭包?
闭包一般人都说, 是函数中定义的一个函数,有的人也说函数中套函数。
其实准确点说,闭包是一个能够访问到其他函数内部变量的函数,
当然这个其他函数不是父子关系的函数, 而是兄弟关系的函数。
举个最简单的例子引导一下
function alwaysLinger(){ var num = 0;
// 一堆一堆逻辑 }
console.log( num ); // num is not defined;
在外面访问 num 肯定是访问不到,还会报错:num is not defined;
但是你又想访问到 alwaysLinger 函数内部的局部变量。
这是时候你就需要闭包了。
function alwaysLinger(){ var num = 0; // 一堆一堆逻辑 return function(){ return num; } } console.log( alwaysLinger()() ); // 0;
看起来,好像也没有什么神奇之处,当你调用alwaysLinger函数后,返回一个函数,执行返回的函数,再次返回 num 变量。
这样就取了函数内部的值。看到这里你可能你会迷惑, 为什么要返回一个函数,然后再返回num呢,直接返回num不好吗?
我建议你带着这个疑问看下去。
闭包其实是一种现象:
父函数内的子函数,被父函数之外的变量引用,父函数里的变量永远不会销毁(也说明了js的垃圾回收机制不会生效,父函数中的变量永远存在)
下面举几个例子,简单的说明闭包常见的运用场景。
1、使变量始终存在内存中
function alwaysLinger(){ var a = 0; return function(){ return a++; } } console.log( alwaysLinger()() ); // 0 console.log( alwaysLinger()() ); // 0 console.log( alwaysLinger()() ); // 0 console.log( alwaysLinger()() ); // 0 var func = alwaysLinger(); console.log( func() ); // 1 console.log( func() ); // 2 console.log( func() ); // 3 console.log( func() ); // 4 console.log( func() ); // 5
前几个console,输出同一个值,是因为每次执行都返回一个新值,
后几个console,输出递增的值,是因为每次执行后操作的都是一个值,就是函数中的局部变量num。我们使用闭包不单单只是为了取到局部变量,还要去操纵它。
2、模拟块级作用域
比如,我们现在要实现点击列表,实现弹出列表的索引值。
// 先获取到列表项 li var list = document.querySelectorAll("li"); // 遍历 li for (var i = 0; i < list.length; i++) { // 给每一个列表项绑定点击事件,打印索引。 list[i].onclick = function(){ alert(i); } }
一起看起来,好像并没有什么毛病,但是运行的结果却是不管点击那个都是返回最后一个索引值。这是因为在es5中没有块级作用域的原因导致的, 解决的办法很多种举几种常用的解决办法。
1、闭包方式 var list = document.querySelectorAll("li"); for (var i = 0; i < list.length; i++) { (function(i){ list[i].onclick = function(){ console.log(i); } })(i) } // 循环内部加一个自执行函数,利用函数作用域,模拟块级作用域。 2、利用let代替var声明, 告诉浏览器使用es6语法 var list = document.querySelectorAll("li"); for (let i = 0; i < list.length; i++) { list[i].onclick = function(){ console.log(i); } } 3、 使用forEach 循环 var list = document.querySelectorAll("li"); list.forEach(function(item,i){ item.onclick = function(){ console.log(i); } }) 2,3两种方法跟今天没有关系,这里就不多做讨论。
来一个比较坑的面试题。
function createFunction () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function(){ return i; } } return result; } var aFunction = createFunction (); aFunction.forEach(function(obj){ console.log( obj() ); })
大家可以自己尝试着猜猜,结果是什么?
再来一个差不多的面试题。
for (var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 0); }
细心的小伙伴或许已经发现了,这种比较迷惑人的面试题,都有一个共同的特征,就是在循环中包含了一个函数。只要遇到这种问题,都可以用闭包来解决。
3、代替全局变量
我们都知道如果一个程序全局变量太多的话,会影响性能, 那么怎么解决呢,其实最根本的就是把全局变量变成局部变量;
(function(){ // 对于小范围使用的变量,都可以利用这种方法变成局部变量。 var name = "HoChine"; console.log(name); function test (){ console.log(name); } test(); /*..... 一堆堆逻辑 .....*/ })() console.log(name) // name not defined;
// 这样的话,在外界就访问不到变量了,避免了全局变量的增多。<p>---恢复内容结束---</p># 闭包
以上就是我对闭包的理解,如果有错误的地方请提出,欢迎批评指正。一起学习一起进步。