• 理解javascript闭包


    1.闭包是什么

    官方解释:闭包是一个拥有很多变量和绑定了这些变量的环境的表达式(其实就是函数),因而这些变量也是该表达式的一部分。这个定义虽然太学术,但是告诉我们两个信息:

    1)闭包是一个函数

    2)函数中有很多变量

    上面两个是构成闭包的两个主要条件。

    下面我们用通俗的话来解释一下:js中的所有函数都是闭包(因为函数中的局部变量只能函数内部访问),但是嵌套函数产生的闭包更加强大,也是我们现在所探讨的闭包。

    如果上面的解释还不够通俗,下面的终极解释我想你一定能够看懂:

    有一个函数a,函数a中嵌套了一个函数b,如果函数b被函数a外部的一个变量引用,就创建了一个闭包。

    下面我们来看看具体如何通过代码来创建闭包,以加深上面概念的理解。

    2.创建闭包

    在创建闭包之前,首先要明白两个概念,一个是变量的作用域,一个事js中的作用域链,第一点我们简单说一下,第二点自己去查资料。

    在Js中变量根据作用域的不同可以分为全局变量和局部变量(事实上很多语言都是这样),在js中,如果一个变量没有定义在任何函数中,则为全局变量;相对应的,定义在函数中的变量就是局部变量,但是如果函数中变量在声明时没有使用var关键字,则其仍然会称为全局变量。我们来看例子。

    function f() {
                a = 1;//没有使用var,所以在函数外部也可以访问
            }
            f();
            alert(a);
    

      

    下面我们看看如何创建闭包,看下面的函数

         function f1() {
                var a = 10;
                a++;
                alert(a);
            }
            var func1 = f1();
            func1;
            func1;
    

    希望你能猜对上面代码的运行结果,只输出一个11。在函数f1的外部创建了变量func1,然后指向由函数f1的返回值。当执行完代码func1之后,这个对象就没有引用了,所以会被垃圾回收,对象中的变量a同样也会被回收;所以当再次执行func1时就不会有输出了。

    从上面的代码,希望你能明白这样一个道理:js中是有垃圾回收机制的。当一个对象没有变量引用的时候,这个对象就会被回收。

    再来看下面的代码:

    function f1() {
                var a = 10;
                function f2() {
                    a++;
                    alert(a);
                }
                return f2;
            }
            var func1 = f1();
            func1();
            func1();
    

      

    上面代码的输出结果为11,12。

    执行完第一句func1()之后,对象应该被回收,第二句func1();应该没有输出猜对呢,这是为什么呢?

    我们来分析一下。

    首先看var func1=f1();这行代码执行之后,func1是什么。在函数f1中返回的是函数f2,所以func1的值其实是函数f2。按理说当执行完这行代码之后,函数f1的使命已经完成,应该被垃圾回收才是,你们变量a也会被清除,但是执行代码func1()之后的结果居然为11,这说明函数f1中的变量a没有被清除,那么肯定函数f1也没有被垃圾回收。这是为什么?

    我们前面说过,一个对象如果被垃圾回收的条件是什么,那就是没有变量引用这个对象。我们来看看上面的代码。函数f2中对函数f1中的变量a进行++操作,也就是说在函数f2中引用了函数f1中的变量,也就是函数f2引用了函数f1。而代码var func1=f1();其实是将函数f2返回给变量func1,也就是说变量func1引用了函数f2,而函数f2由引用了函数f1,这种间接引用的结果就是函数f1一直被变量引用着,所以一直无法被垃圾回收。

    上面的情况就是闭包,我们再回顾一下闭包的定义:如果函数a中的嵌套函数b被函数a外部的变量引用,就创建了闭包。

    综合上面的讨论,我们可以看出闭包的作用是什么

    3.闭包的作用

    1)变量的安全性:我们无法在函数f1的外部直接访问其局部变量a,只能通过函数f2来访问,而在函数f2中我们可以写代码进行安全性的控制,这是不是和c#中类的属性很像。所以我们可以将函数f2看成是函数f1的一个属性,这个属性只有setter方法,而将局部变量a看成是函数f1的私有字段,只能通过公共属性f2才能访问f1中的私有字段a。

    2)让变量的值始终保存在内存中。这个已经非常清晰了,通过闭包,函数f1中的变量a没有被回收,而是一直保存在内存中。

  • 相关阅读:
    同步类容器和并发类容器
    T4模板生成自定义的实体类
    C# UrlEncode 编码
    PLSQL快速生成增删改查语句
    Oracle 检查星期只能是1-7的数字不能重复
    PLSQL 插入数据无响应
    C# DataTable 排序
    C# 获取程序集信息
    C# 调用WinRAR解压缩文件
    DataTable 获取一列最大值并修改
  • 原文地址:https://www.cnblogs.com/wenriyao/p/3807614.html
Copyright © 2020-2023  润新知