GIS数据共享:官方网站

JavaScript

当前位置:首页 > language > H5 > JavaScript

闭包的认识

一、什么叫闭包 1、问题引出: 不准用全局变量,也不准在调用代码块使用变量,实现计数的增加。 一般来说,用变量或对象,是可以记住上一次的状态...

一、什么叫闭包

    1、问题引出:

        不准用全局变量,也不准在调用代码块使用变量,实现计数的增加。

        一般来说,用变量或对象,是可以记住上一次的状态或值,但用方法或函数是不能的。

        下面的编写是不允许的,因为在a与b处定义使用了变量。

        private int n = 5;//a
        private static void Main(string[] args)
        {
            int i = 5;//b
            i++;
            Console.WriteLine(i);
            i++;
            Console.WriteLine(i);
            Console.ReadKey();
        }

  改成下面:

        private static void Main(string[] args)
        {
            Func<Func<int>> f = () =>
             {
                 int i = 5;
                 return () =>
                         {
                             i++;
                             return i;
                         };
             };
             
            var counter = f();//a
            
            Console.WriteLine(counter());//6
            Console.WriteLine(counter());//7
            Console.WriteLine(f()());//6
            Console.WriteLine(f()());//6
            Console.ReadKey();
        }

   上面没有用变量,只是用一个方法,每一次调用counter()值就自动增加,实现了题目的要求。

        问:上面输出的最后两次为什么一直是6?

        答:反编译上面,编译器会将上面:

            构造一个类,字段为i=5,后面的是方法。每创建一次就会实例化这个类即创建一个对象,于是这个方法它有了保存“状态”的能力。

            在a处,创建这个类的对象并将对象传递给counter,所以每一次count,里面的对象就会用方法把i增加1,这就是第一次6,第二次再在原来i=6的基础上(因为这里一直引用的是counter=f()创建过来的对象),成为7.

            而后面的两次都为6,因为都是在原来的i=5的基础上,重新创建一个各自不同的新对象,尽管都是新的对象,但它们的基础都是一样的i=5,所以再用方法自增是都是6.

    2、什么是闭包?

        闭包(closure)是指一个函数(或方法)及其相关的引用环境(包括函数内部定义的变量)的组合。简单来说,闭包是一个函数加上它能访问的自由变量(即不是全局变量,也不是函数的参数)的集合。(正如前面例子一样,i与后面方法)

        在编程中,当一个函数引用了外部的变量,并且该函数可以在其定义的作用域之外被调用时,就形成了一个闭包。闭包使函数可以“记住”其创建时的上下文环境,包括外部变量的值。

        闭包的一个常见应用场景是在异步编程中,特别是在使用回调函数或任务的情况下。在这种情况下,闭包可以用来捕获异步操作中的状态或上下文,并在回调函数或任务中使用。        

    3、为什么闭包不会污染全局变量?

        答:闭包通过捕获变量的引用来工作,不会污染全局变量,并且不会浪费内存空间。闭包只是在调用时使用外部变量的当前值,而不会创建新的变量。

            闭包不会污染全局变量,这是因为闭包的工作原理是通过捕获变量的引用,而不是将变量的值复制到闭包中。这意味着闭包只是引用了外部变量,并没有创建一个新的变量。

            当一个闭包被创建时,它会捕获其所在作用域中的变量引用。当闭包被调用时,它可以访问和修改这些变量的值。这是因为闭包引用的是变量本身,而不是变量的副本。

            由于闭包只是引用了外部变量,而不是复制变量的值,所以它不会污染全局变量。全局变量仍然保持其原来的值,而闭包只是在调用时使用了外部变量的当前值。

            此外,关于内存的浪费问题,闭包并不会额外占用内存。当闭包被创建时,它只是引用了外部变量,而不会创建新的变量。因此,闭包不会浪费额外的内存空间。

    4、闭包的条件:

        闭包内部的函数必须引用外部函数的变量。这样,当外部函数执行完毕后,闭包仍然可以访问和修改外部变量。

        private static void Main(string[] args)
        {
            Action a = CreateClosure();
            a();//21
            a();//22
            Console.ReadKey();
        }
 
        private static Action CreateClosure()
        {
            int intInner = 20;
            Action action = () =>
            {
                Console.WriteLine(++intInner);
            };
            return action;
        }

        上面定义了一个函数 CreateClosure,它返回一个闭包。闭包内部的函数 action 引用了外部变量 intInner。在 Main 方法中调用闭包并输出外部变量的值。可以看到,即使 CreateClosure 函数已经执行完毕,闭包仍然可以访问和使用外部变量。这是因为闭包将内部函数和外部变量封装在一起,形成了一个封闭的环境。闭包的存在使得内部函数可以继续访问和操作外部变量,即使外部函数已经执行完毕。

        问:下面是闭包吗?

        static void Main()  
        {  
            int outerVariable = 10;  
            Action closure = () =>  
            {  
                outerVariable++;  
                ConsoleWriteLine("Outer Variable: " + outerVariable);  
            };  
              
            ConsoleWriteLine("Before closure execution: " + outerVariable);  
            closure(); // 调用闭包  
            ConsoleWriteLine("After closure execution: " + outerVariable);  
        }  
          
        static void ConsoleWriteLine(string message)  
        {  
            Console.WriteLine(message);  
        }

     尽管上面没有函数嵌套,但仍然是闭包。

        闭包的特点是它可以持久化对外部变量的引用,并且在闭包调用之后仍然可以访问和修改这些变量的值。代码中,闭包通过引用外部变量 outerVariable,在闭包内部对其进行了修改,并输出了修改后的值。

        尽管闭包中的外部变量 outerVariable 存储在外部作用域中,但这并不影响闭包的定义。闭包是通过引用外部变量来捕获其值的,而不是将其值存储在闭包内部。因此,即使外部变量 outerVariable 存储在外部作用域中,闭包仍然可以持久化对其值的引用,并在闭包内部进行访问和修改。

        函数嵌套并不是闭包的必要条件。

        当 lambda 表达式或 LINQ 查询表达式使用了外部变量时,它们可以被认为是闭包。闭包的使用可以提高代码的可读性和可维护性,并且使得我们能够更方便地处理复杂的逻辑和数据操作。

        闭包是一个函数及其相关的引用的组合。在 lambda 表达式或 LINQ 查询表达式中,我们可以引用和使用外部的变量。这些外部变量可以是在外部作用域中声明的变量,也可以是在外部方法或类中声明的变量。当 lambda 表达式或 LINQ 查询表达式捕获了外部变量时,它们会持久化对这些变量的引用,从而形成闭包。

        闭包的使用使得我们可以在 lambda 表达式或 LINQ 查询表达式内部访问和修改外部变量,这为我们提供了一种更灵活和方便的编程方式。闭包使得我们可以在函数内部使用外部变量,避免了传递参数的麻烦,并且可以在函数内部继续使用外部变量,而不受外部作用域的限制。


扫码查看

上一篇:如何通过图片懒加载加快网站显示时间

下一篇:已经是最后一篇

相关内容

热门标签