闭包和高阶函数
闭包的形成与变量的作用于和变量的生命周期密切相关。

  1. 变量作用域
    var fnc = function () {
        var a = 1;
        console.log(a);     //输出    1
    }
    fnc();
    console.log(a);         //输出    Uncaught ReferenceError: a is not defined

在javaScript中, 函数可以用来创造函数作用域,但是只能是函数内部可以访问当前函数外部的变量,而外部的变量是不能访问到函数内部的变量的。这是因为在函数执行时,发现函数内部并没有声明这个变量,那么就会随着代码执行环境创建的作用域链往外层逐层寻找,直到找到为止,变量的搜索是从内到外。

	var a = 1;
	var fnc1 = function() {
	    var b = 2;
	    var fnc2 = function() {
	        var c = 3;
	        console.log(a);         //输出    1
	        console.log(b);         //输出    2
	    }
	    fnc2();
	    console.log(c);             //输出    Uncaught ReferenceError: c is not defined
	}
	fnc1();
  1. 变量的生存周期
    除了变量的作用域外,另外一个和闭包有关系的概念是变量的生存周期。
    对于全局变量,它的生存周期必然是永久的,除非我们主动的去销毁它,而对于函数内部用var 关键字 声明的局部变量来说,当退出函数时,它们都会随着函数的调用结束而被销毁。
    var fnc  = function () {
        var a  = 1;				//退出函数时后局部变量a将被销毁
        console.log(a);
    };
    fnc();

继续看这一段代码:

    var fnc  = function () {
        var a  = 1;
        return function() {
            a++;
            console.log(a);
        }
    };
    var f = fnc();

    f();           //输出    2
    f();           //输出    3
    f();           //输出    4            
    f();           //输出    5
    f();           //输出    6

跟我们之前的推论相反,当退出函数时,局部变量a并没有被销毁,而是在某个地方一直存活着,这是因为执行var f = fnc();时, f返回了一个匿名函数的引用,他可以访问到fnc()被调用时产生的环境,而局部变量a 就处于该环境内部,既然函数内部的变量可以被外界访问到,那么这个变量就有了不被销毁的理由, 由此就产生了闭包结构,局部变量的生命就得到了延续。

下面介绍一个闭包的经典应用:

<body>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        
        <script>
            var nodes = document.getElementsByTagName('div');

            for(var i = 0; i < nodes.length; i++){
                nodes[i].onclick = (function() {
                    console.log(i);
                })
            };
        </script>

 </body>

这样的话,无论点击的是谁,最后的结果都是5,这是因为当前dom绑定的点击事件,是异步触发的。当事件触发的时候,for 循环早就已经执行完毕了,最后一次执行循环完毕加1之后 ,i 一直等于5,。

闭包解决方式:

<body>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        
        <script>
			var nodes = document.getElementsByTagName('div');
			
			for(var i = 0; i < nodes.length; i++){
				(function(i){
				   nodes[i].onclick = (function() {
				       console.log(i);
				   })
			     })(i)
			};
        </script>

 </body>

通过使用闭包,把每次循环的i的值都封闭起来,当在事件函数中顺着作用域链中从内向外查找变量i 时,回先找到被封闭在闭包环境中的i,如果有5 个div,这里的i 就分别是0,1,2,3,4。其实也可以理解为,通过函数自调用,把每次循环执行时,把对应的i传入到匿名函数中,其实就是隐性的在函数内部执行了 var i = 传进来的i的值, 当事件触发时,通过作用域链向外搜索时,便能找到对应的i的值了。

喜欢的点个赞,一直持续更新

Q.E.D.