关于JavaScript闭包自己在工作和学习中的总结
1、闭包的概念:函数对象可以通过作用域链相互关联起来函数体内部的变量就可以保存在函数作用域内。也就是说闭包就是能够读取其他函数内部变量的函数。
2、闭包的用途:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
先看一道关于闭包的题目,这道题目也是无意间在社交群里看到的,感觉很经典,如果这道题懂了,我想对于闭包也没有多大问题了。
//答案:
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
一、想弄明白这个问题就要先弄明白js中函数作用域和作用域链的问题
1、对象内部的函数表达式:
var o={
fn:function (){
console.log(fn);
}
};
o.fn();//ERROR报错
2、非对象内部的函数表达式:
var fn=function (){ console.log(fn); }; fn();//function (){console.log(fn);};正确
结论是:使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的不能访问到。
原因也非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
再回过头看这题:
最内层的return出去的fun
函数不是第二层fun
函数,是最外层的fun
函数。
所以,三个fun
函数的关系也理清楚了,第一个等于第三个,他们都不等于第二个。
1、第一行a
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
可以得知,第一个fun(0)
是在调用第一层fun
函数。第二个fun(1)
是在调用前一个fun
的返回值的fun
函数,所以:
第后面几个fun(1)
,fun(2)
,fun(3)
,函数都是在调用第二层fun
函数。
遂:
在第一次调用fun(0)
时,o
为undefined
;
第二次调用fun(1)
时m
为1
,此时fun
闭包了外层函数的n
,也就是第一次调用的n=0
,即m=1
,n=0
,并在内部调用第一层fun
函数fun(1,0);
所以o
为0
;
第三次调用fun(2)
时m
为2
,但依然是调用a.fun
,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);
所以o
为0
第四次同理;
即:最终答案为undefined,0,0,0
2、第二行b
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
先从fun(0)
开始看,肯定是调用的第一层fun
函数;而他的返回值是一个对象,所以第二个fun(1)
调用的是第二层fun
函数,后面几个也是调用的第二层fun
函数。
遂:
在第一次调用第一层fun(0)
时,o
为undefined
;
第二次调用 .fun(1)
时m
为1
,此时fun
闭包了外层函数的n
,也就是第一次调用的n=0
,即m=1
,n=0
,并在内部调用第一层fun
函数fun(1,0);
所以o
为0
;
第三次调用 .fun(2)
时m
为2
,此时当前的fun
函数不是第一次执行的返回对象,而是第二次执行的返回对象。而在第二次执行第一层fun
函数时时(1,0)
所以n=1
,o=0
,返回时闭包了第二次的n
,遂在第三次调用第三层fun
函数时m=2
,n=1
,即调用第一层fun
函数fun(2,1)
,所以o
为1
;
第四次调用 .fun(3)
时m
为3
,闭包了第三次调用的n
,同理,最终调用第一层fun
函数为fun(3,2)
;所以o
为2
;
即最终答案:undefined,0,1,2
3、第三行c
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
根据前面两个例子,可以得知:
fun(0)
为执行第一层fun
函数,.fun(1)
执行的是fun(0)
返回的第二层fun
函数,这里语句结束,遂c
存放的是fun(1)
的返回值,而不是fun(0)
的返回值,所以c
中闭包的也是fun(1)
第二次执行的n
的值。c.fun(2)
执行的是fun(1)
返回的第二层fun函数
,c.fun(3)
执行的也是fun(1)
返回的第二层fun
函数。
遂:
在第一次调用第一层fun(0)
时,o
为undefined
;
第二次调用 .fun(1)
时m
为1
,此时fun
闭包了外层函数的n
,也就是第一次调用的n=0
,即m=1
,n=0
,并在内部调用第一层fun
函数fun(1,0);
所以o
为0
;
第三次调用.fun(2)
时m
为2
,此时fun
闭包的是第二次调用的n=1
,即m=2
,n=1
,并在内部调用第一层fun
函数fun(2,1);
所以o
为1
;
第四次.fun(3)
时同理,但依然是调用的第二次的返回值,遂最终调用第一层fun
函数fun(3,1)
,所以o
还为1
即最终答案:undefined,0,1,1
二、 闭包的应用
应用1:
这个是我在用js模拟排序算法过程遇到的问题。我要输出每一次插入排序后的数组,如果在循环中写成
setTimeout(function() { $("proc").innerHTML += arr + "<br/>"; }, i * 500);
会发现每次输出的都是最终排好序的数组,因为arr数组不会为你保留每次排序的状态值。为了保存会不断发生变化的数组值,我们用外面包裹一层函数来实现闭包,用闭包存储这个动态数据。下面用了2种方式实现闭包,一种是用参数存储数组的值,一种是用临时变量存储,后者必须要深拷贝。所有要通过闭包存储非持久型变量,均可以用临时变量或参数两种方式实现。
<script type="text/javascript"><!--
var arr = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];
var $ = function(id) { return document.getElementById(id); }
var Sort = {
Insert: function() {
for (var i = 1; i < arr.length; i++) {
for (var j = 0; j < i; j++) {
if (arr[i] < arr[j]) {
arr[i] = [arr[j], arr[j] = arr[i]][0];
}
}
setTimeout((function() {
var m = [];
for (var j = 0; j < arr.length; j++) {
m[j] = arr[j];
}
return function() {
$("proc").innerHTML += m + "<br>";
}
})(), i * 500);
//or 写成下面这样也可以
/*
setTimeout((function(m) {
return function() {
$("proc").innerHTML += m + "<br>";
}
})(arr.join(",")), i * 500);
*/
}
return arr;
}
}
// --></script>
</head>
<body>
<div>
var a = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];
</div>
<div>
<input type="button" value="插入排序" onclick="Sort.Insert();" />
</div>
Proc:
<div id="proc">
</div>
</body>
如果想查看更加详细的应用例子可以查看http://www.jb51.net/article/24156.htm