JavaScript--我所理解的闭包
关于闭包大家肯定不陌生, 刚知道闭包这个特性那会真的被折磨, 现在找个时间把这些都记下来, 希望能帮新人理解和记忆.
-------------------------------------------------------------------------------------------
闭包的格式有很多种, 但基本都是将一个嵌套函数中的内层函数返回到外层函数外面, 使外层函数体外也能访问和操作外层函数中"封住"的局部变量, 与此同时, 外层函数中的变量不会被销毁, 会"长久记忆".
最常见的闭包:
function outer(){ var i = 100; //保存于外层函数体内的局部变量 return function(){ //用于返回内部函数 console.log(i); //内部函数体中对 "它" 自己的上下文中的变量进行了操作 } } var inner = outer(); inner();
闭包的形式知道了, 重点是怎么调用, 关于调用的方式有很多:
第一种:
function outer(){ var i = 100; return function(){ i++; console.log(i); } } var inner1 = outer(); var inner2 = outer(); inner1(); //输出结果为 101; inner2(); //输出结果还是 101;
这是为何呢? 原因是 当 外层函数每执行一次, 就会创建新的闭包, 即每次外层函数的执行都开辟新的内存空间, 虽然 inner1 和 inner2 都是函数的引用, 但每次返回的函数都是"新的", 所以 inner1 是 inner1, inner2 是 inner2.
第二种
function outer(){ var i = 100; return function(){ i++; console.log(i); } } var inner1 = outer(); var inner2 = inner1; inner1(); //输出 101 inner2(); //输出 102
这种调用方式和第一种相比就很好理解了, 因为 inner1 和 inner2 都是"同一个函数"的引用, 所以出现 101 和 102 也就通了.
第三种
function fo(){ var i = 0; return function(n){ return n + i++; } } var f = fo(); //现在变量f是一个函数的引用 var a = f(15); //输出 15, i此时的值是0, 不是undefind, 闭包能够记住自己认识变量i var b = fo()(15); //输出 15, fo()会返回新的内层函数, 也就是说产生了新的闭包(对比第一种理解), i已经被清零 var c = fo()(20); //输出 20, 同理 var d = f(20); //输出 21
第三种函数做了稍稍改变, 内层函数中要传入形式参数, 其实道理是一样的.
第四种
function fo(m){ return function(n){ return m + n; } } var f = fo(1); //变量 f 是一个内层函数的引用, 此时 f 已"记住" f 自己中要调用的 m 的值为 1 var a = f(2); //输出 3, 因为闭包的存在, 所以在 f(2) 执行时, f 内部的 m 是 1 var b = fo(10)(1); //输出 11, 每当外层函数 fo 执行一次, 都会产生新的闭包并返回一个函数 var c = fo(10)(2); //输出 12。同理。 var d = f(3); // 和 f(2) 同理.
第五种
其实第五种来说, 不单单是闭包的影响, 还有就是关于"异步"的一些概念, 就是浏览器给元素添加监听是要消耗时间的, 所以在给一个类数组中的元素通过循环添加监听是不能直接添加的.下面是 html 结构.
<ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul>
下面是对应的 JS 代码:
var lis = document.getElementsByTagName("li");
for(var i = 0, length = lis.length; i < length; i++){
lis[i].onclick = function (){
console.log("我被点击了, 我是第" + i);
};
}
初学者可以试试, 会发现每个 li 触发点击后再控制台输出的都是 "我被点击了, 我是第 5", 原因是 当 浏览器给 元素添加监听时, 不会阻塞程序的执行, 就是说在给 lis[0] 添加事件监听时, for 循环还在走, i 还在 ++.
解决方法最好用的就是 IIFE(即立即执行函数):
var lis = document.getElementsByTagName("li"); for(var i = 0, length = lis.length; i < length; i++){ lis[i].onclick = (function(i){ return function (){ console.log("我被点击了, 我是第" + i); }; })(i); }
外层函数是立即执行的, 并且把 i 当做参数传入函数体, 因此返回的内层函数就会拿到"合适的" i 值.
以上是我对闭包的一些理解, 在此抛砖引玉, 有误导之处还请不吝指点 ^^.