闭包

203 阅读4分钟

闭包

了解闭包之前,回顾一下变量的作用域

变量根据作用域的不同,分为全局变变量和局部变量,函数里没有var,直接赋值,也是全局变量

在函数内部可以使用全局变量

在函数外面不能使用局部变量

当函数执行完毕后,本作用域内的局部变量会销毁

函数内部可以访问父级的变量,外部不可以访问内部的变量

闭包(closure)的定义

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

简单理解,就是一个作用域可以访问另外一个函数内部的局部变量

         function fn(){
            var num=10;
            function fun(){
                console.log(num);
            }
            fun();
        }
        fn();//10
        // fun函数可以访问fn函数里的值,被访问的变量所在的函数fn就是一个闭包

闭包:fun这个函数也是一个作用域,访问了另外一个函数里面局部变量num,满足了闭包产的的条件,此时就产生了闭包

闭包就是一个函数,被访问的变量所在的函数就是一个闭包 ,fn就是一个闭包

闭包的作用

闭包是一个作用域访问另一个作用域的变量,是否能在函数外面的作用域访问函数内部的局部变量?

1. 函数外部访问函数内部变量

  // 函数外部访问函数内部变量
        function fn() {
            var num = 10;
            
            function fun() {
                console.log(num);
            }
            // 函数不调用不执行,直接返回
            // 函数不仅可以当参数传递,也可以当做返回值
           
            return fun;
        }
        
        // 用个变量接收函数返回值 
        var f = fn();
        
        f();// 调用fun函数,因为fun本身就是在fn里,所以可以可以使用fn里的变量
      
      /*
        类似于
        var f=  function fun() {
                console.log(num);
            }
        */

2. 外部作用域访问函数内部变量

 // 函数外部访问函数内部变量2
       function fn() {
           var num = 10;
           // 直接返回一个函数,高阶函数
           return function () {
               console.log(num);
           }
       }
       // 用个变量接收函数返回值 
       var f = fn();
       f();//调用fun函数,因为fun本身就是在fn里,所以可以可以使用fn里
       /*
       类似于
       var f=  function() {
               console.log(num);
           }
       */

闭包的作用:延伸了变量的作用范围,解决了变量污染

一般情况下,局部变量使用后就会销毁,但在fn函数里,不会等着fn执行完就销毁,因为还有一个函数没有调用,等到全部函数执行完之后,才会销毁

闭包的应用

点击文字,改变文字的颜色

   <h1>点击我,改变颜色</h1>
   <h1>点击我,改变颜色</h1>
   <h1>点击我,改变颜色</h1>
   <h1>点击我,改变颜色</h1>
   <h1>点击我,改变颜色</h1>
  <script>
     var h1s = document.querySelectorAll('h1');
       for (var i = 0; i < h1s.length; i++) {
             h1s[i].onclick = function () {
              h1s[i].style.color = "red"
           }
      }
  
  </script>

正常来说,都是获取到所有的h1,循环添加点击事件,点击哪个哪个文字就会变颜色,但是此时却有错误,for循环结束后,得到的值是5

这是因为点击事件是是一个异步任务,只有当点击的时候才会执行,而for循环是个同步任务,立刻就执行了,当我们开始点击的时候,循环已经执行完毕,i也变成了5

// 利用闭包的形式
  var h1s = document.querySelectorAll('h1');
        for (var i = 0; i < h1s.length; i++) {
             (function (i) {
                   h1s[i].onclick = function () {
                   // 一个函数使用了另外一个函数的变量
                   h1s[i].style.color = "red"
                }
            })(i)
       }

使用立即执行函数,创建了5个立即执行函数,每个立即执行函数都是一个独立的作用域

向立即执行函数里传入参数,当i为0的时候,传入的参数也为0,i=1的时候,创建了一个新的立即执行函数,传入的参数也为1,以此类推

在onclick函数里,使用了立即执行函数里的变量i,此时立即执行函数就是一个闭包 立即执行函数也称为一个小闭包,因为它里面任何一个函数都可以使用它这个变量

在开发中,合理的使用闭包

var name = "window";

        var obj = {

            name"my obj",

            getnamefunction () {

                return function () {

                    return this.name;

                };

            }

        };

        console.log(obj.getname()());

        var f =obj.getname();// 里面还有一个函数返回,还没有调用

        // 类似于

        var f=function(){

            return this.name;// 函数里的this指向window 在普通函数里 window.那么 

        }

        f();

没有闭包的产生,没有局部变量.

var name = "window";

        var obj = {

            name"my obj",

            getnamefunction () {

                var that=this;// this指向函数的调用者,obj

                return function () {

                    return that.name;// 使用父级函数的变量this,指向的是obj,是 my obj 

                };

            }

        };

        console.log(obj.getname()());

        var f =obj.getname();// 里面还有一个函数返回,还没有调用

        // 类似于
/*
        var f=function(){

            return that.name;

        }
        */

        f();

有闭包的产生,有了局部变量,函数内使用了父级函数内的变量