for (var i = 0; i < 10; i++) {
}
console.log(i) //10
1
2
3
2
3
# 闭包形成
function test() {
var arr = [];
for(var i = 0; i < 10; i++){
arr[i]=function () {
document.write(i + " ");
//前面的 arr[i]从 0 到 10 一直在循环
//后面的函数只是一个引用,没执行。
//到后面执行
}
}
return arr;
}
var myArr = test();
//在下面执行十个函数体
for(var j = 0; j < 10; j++){
myArr[j]();//10个函数都执行
//执行的时候上面的arr[i]也就是说函数保存到了外部已经变成了 10
//理想状态下执行函数值就是 0123456789
//但是输出 10 个 10
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 函数执行的过程
把十个函数都保存到数组里面返回,数组里面的10个函数都与test形成闭包(因为test被保存到了外面),访问test里面产生的变量 访问的就是同一套 test的AO已经成为了10 所以for循环会输出10个10
# 闭包解决
function test() {
var arr = [];
for(var i = 0; i < 10; i++){
(function (j) {
arr[j] = function() {
document.write(j + " ");
//此时的 j 从 0-9 开始变化
}
}(i));
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
解决思路
把i当做参数传到j里面,让每次i打印的结果立即变现,打印一次为1,打印两次为2。第一次j返回1第二次 j 返回 2,有多少次立即执行函数就有多少个j,立即执行函数进行for循环,打印10次彼此独立的立即执行函数,因此 for 循环会循环立即执行函数里面 j,形成 10 对 10,立即套现。
# 里面的立即执行函数第二次执行如下
(function (j) {
arr[j] = function() {
document.write(j + " ");
}
}(1);
//将1传入j 最后数组的第2个下标位打印1
1
2
3
4
5
6
2
3
4
5
6
# 闭包例子
function test() {
var temp = 100;
function a() {
console.log(temp);
}
return a;
}
var demo = test();
demo(); //在外部执行a函数 打印的是100
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
会出现闭包的情况:两个函数(或者多个函数互相嵌套),把里面的函数保存到了外面函数的外部(把里面的函数保存到了外部,保存到了全局,这样一定生成闭包,然后里面的函数在外部执行的话一定会调用的了原来它在的函数里面的变量
function a() {
function b() {
var bbb = 234;
document.write(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo(); //输出123
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
里面的函数被保存到了外面函数的外部。一定会形成闭包。因为里面的函数拿的是外部函数的劳动成果(0:自己的AO 1:外部的AO 2:GO),如果外部的函数执行后,AO剪断了。内部的函数还没执行完,内部的函数会映射(0:自己的AO 1:外部的AO 2:GO),在自己的内部找不到执行期的AO(变量),会依次向外,找外部函数的AO 也就是var aaa=123;
# 闭包demo
使用原生js,addEventListener,给每个li元素绑定一个click事件,输出他们的下标顺序,每个li加一个事件,点击每个li的时候要弹出每个li的索引。
<ul>
<li>a</li>
<li>a</li>
<li>a</li>
<li>a</li>
</ul>
1
2
3
4
5
6
2
3
4
5
6
function test() {
var licollection =document.getElementsByTagName('li');
//获取页面上的四个li元素。此时成为了数组。
//给第一个li绑定事件就是licollection[0].onclick = function()
//{consloe.log(this.innerText)}; 点一下会弹出页面上的文本节点 a。
for (var i = 0; i < licollection.length; i++) {
//给这一组li加事件就得把这一组的li全部遍历出来给每个li加事件。
licollection[i].onclick = function () {
console.log(i); //理应输出每个 i 的下标
}
}
}
test();
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
控制台结果:
分析
此种写法的弊端是for循环把里面的点击函数保存到了外部,那么一定形成了闭包。onclick实际上把每个点击后的动作保存到了li的后面。所以说保存到了外部形成了闭包,要想解决此类问题必须使用立即执行函数。
(function(j) {
licollection[j].onclick = function () {
console.log(j);
}
}(i)) //把下标传到j 里面,每次执行立即输出下标位并销毁,循环传递
1
2
3
4
5
2
3
4
5
控制台结果: