首先我们来了解几个概念:

立即执行函数:形如 (function(){})();的一类函数;

闭包:闭包是指有权访问另一函数作用域中的变量的函数。

作用域链:当代码执行的时候,会创建变量对象的一个作用域链....(具体百度)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>闭包经典例子详解——huansky</title>
</head>
<body>
    <div id="ttt">
        <p>000000000000000000</p>
            <br>
        <p>111111111111111111</p>
        <br>
        <p>222222222222222222</p>
    </div>
</body>
<script>
var dom=document.getElementsByTagName("p");
    for(var i=0;i<dom.length;i++){
        dom[i].onclick=function(){    
            console.log(i);//3
        };
    }
   
</script>
</html>

首先,代码中的匿名函数没有变量 i,所以它必须向上查找,在全局环境中找到了 i。

当for循环运行后,全局变量中的 i 变成了3。此时当你点击文字的时候,会调用其绑定的函数,而该函数运行的时候,发现自己没有 i,就会取得全局环境中的 i。

所以,最后的结果是,不管你点击那段文字,最后结果都是3。

那怎么办呢?你可以用立即执行函数,看代码:


1
2
3
4
5
6
7
   for(var i=0;i<dom.length;i++){
                dom[i].onclick=function(i){
                    return function( ){
                    console.log(i);
                    };
                }(i);
            }

我们把参数 i 作为传给立即执行函数,这样,i 的值就传给了立即执行函数的局部变量 i 了。立即执行函数会直接执行,但是其活动不会销毁,因为里面有个匿名函数。执行后局部变量 i 与全局变量 i 联系就切断了,也就是执行的时候,传进去的变量 i 是多少,立即执行函数的局部变量 i  就是多少,并且该局部变量 i 仍然没有消失,因为匿名函数的存在。

这时候,return中的匿名函数的作用域链中会有两个变量 i。当点击文本的时候,它向上搜索 i 的时候,它找到立即执行函数的局部变量 i ,就停止向上查找了,因此最后的结果就不会是全局变量 i 的值3了。

再看一个变体,代码:


1
2
3
4
5
6
7
8
   for(var i=0;i<dom.length;i++){
                dom[i].onclick=function(t){
                    return function( ){
                    console.log(t);//1
                    console.log(i);//3
                    };
                }(i);
            }

其实return中的匿名函数中的 t 就是立即执行函数的局部变量 i,而 i 就是 指全局变量 i,因为立即执行函数中没有变量i,只能继续向上搜索,然后就找到全局变量的 i 了。

最后,在ES6以后,提供了局部变量定义 let , 所以在这个案例中,使用


1
2
3
4
5
6
var dom=document.getElementsByTagName("p");
    for(let i=0;i<dom.length;i++){
        dom[i].onclick=function(){    
            console.log(i);// 1 2 3
        };
    }

也可以达到想要的效果!