闭包使用不当会导致什么问题,如何才能去避免

2021-04-07 10:43发布

8条回答
kitidog2016
2楼 · 2021-04-08 09:52

 同事在初始化redis配置的时候,给Dial函数赋值时用了闭包,导致程序上线后,数据怎么都加载不到redis中去,排查了半个多小时,总算找到了罪魁祸首。虽然自己之前对闭包也算了解,但是看到他的那段代码的时候,乍一看竟也没发现出问题来,所以决定写篇文章加深印象,避免自己以后也犯类似的问题。


        先上代码:


func InitRedis() error {

    GRedis = make(map[string]*Cache)

    for _, gConfig := range config.GConfigs.RedisList {  //遍历配置文件,redisList存放了各个redis服务器的ip、端口号以及密码等信息。

        c := new(Cache)

        c.pool = &redis.Pool{

            MaxIdle:     gConfig.Conf.MaxConn,

            MaxActive:   gConfig.Conf.MaxConn,

            IdleTimeout: 240 * time.Second,

            Dial: func() (redis.Conn, error) {   //连接实例的Dial函数,这里用了闭包,其中gConfig为自由变量

                c, err := redis.Dial("tcp", gConfig.Conf.IP+":"+gConfig.Conf.Port, redis.DialConnectTimeout(60*time.Second), redis.DialReadTimeout(60*time.Second), redis.DialWriteTimeout(60*time.Second))  

                if err != nil { 

                    return nil, err

                }

                if gConfig.Conf.Password != "" {

                    if _, err := c.Do("AUTH", gConfig.Conf.Password); err != nil {

                        c.Close()

                        return nil, err

                    }

                }

                return c, err

            },

        }


        c.prefix = gConfig.Conf.Prefix

        GRedis[gConfig.Name] = c

    }


    return nil

}

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

26

27

28

29

        上述代码已对关键部分(涉及公司机密)进行了删除和处理。

        乍一看是不是很难发现问题到底出在哪,我们当时碰到的问题是,数据没有从db加载到redis服务器1中,经过排查,发现db的数据全加载到了redis服务器3中了。恰巧redis服务器3是配置文件中的最后一个,也就是for循环中遍历的最后一个。进而我们发现Dial函数对应的闭包函数里引用了gConfig这个自由变量。

        这会导致什么问题呢?我们先来温习一下闭包的特性:有自由变量的匿名函数是闭包,闭包的特性,就是内层函数引用了外层函数中的变量,注意是引用哦。

        所以,这会导致redis连接池中每个连接去做Dial的时候,拿到的都是for循环中最后一个gConfig,即连接的都是redis服务器3,去redis服务器1里面拿数据当然就什么都拿不到了。


yjh
3楼 · 2021-04-08 11:06

一个是外部可以读取内部函数的变量,二是变量会始终在内存中,不会被回收机制回收,三是避免全局变量被污染,方便调用上下文的局部变量,加强封装性。

大量使用闭包会导致内存泄露,所以不用的变量应该手动设置为null。


嘿呦嘿呦拔萝卜
4楼 · 2021-04-08 11:10

在初始化redis配置的时候,给Dial函数赋值时用了闭包,导致程序上线后,数据怎么都加载不到redis中去,排查了半个多小时,总算找到了罪魁祸首。虽然自己之前对闭包也算了解,但是看到他的那段代码的时候,乍一看竟也没发现出问题来,所以决定写篇文章加深印象,避免自己以后也犯类似的问题。



三岁奶猫
5楼 · 2021-04-08 13:23

闭包(closure)是 JavasSript 的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。


变量作用域

要理解闭包,首先要理解 JavasSript 的特殊的变量作用域。


变量的作用域无非就两种:全局变量和局部变量。


JavasSript 语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。


注意点:在函数内部声明变量的时候,一定要使用 var 命令。如果不用的话,你实际上声明的是一个全局变量!


是开心果呀 - 热爱生活
6楼 · 2021-04-08 14:37

闭包的功能强大,但如果没有正确理解闭包的概念,其结果往往出乎人的意料。例如,下面是一个较常见的问题:


    
第一个

    
第二个

    
第三个

    
第四个


[removed]
    function test()
    {
            var els = document.getElementById("test").getElementsByTagName("div");
            for (var i = 0; i < els>
        {
             var div = els[i];
              div.onclick = function()
          {
              alert(div[removed]);
              return false;
           }
          }
    }
    test();
[removed]
无论我们点击哪个div,反馈的都是第4个div的内容。究其原因,在于每个div的点击事件都与test方法形成了闭包,且每个div的点击事件都共享同一个闭包作用域链。当事件被触发时,变量i所代表的下标已经指向第4个div。可以采用以下几种方式避免由于闭包引起的问题。
(1)使用this转换闭包的作用域链上下文,上例的闭包可以改写为:
for (var i = 0; i < els>
{
        var div = els[i];
        div.onclick = function()
    {
        alert(this[removed]);
        return false;
       }
}
当点击div的事件被触发时,查找的作用域已经是“this”所指定的上下文。尽管该事件仍然处于“test”闭包内,但由于不访问或不使用闭包的上下文环境,也就不存在由于闭包作用域内变量被引用所引发的问题。
(2)使点击div的事件与for循环形成闭包,而使得for循环内的变量div不被回收。如:
//for循环内定义闭包方法
for (var i = 0; i < els>
{
         var div = els[i];
         a(div);
    function a(o)
    {
       o.onclick = function()
       {
          alert(o[removed]);
       }
    }
}
//for循环外定义闭包方法
for (var i = 0; i < els>
{
       var div = els[i];
       a(div);    
}
function a(o)
{
    o.onclick = function()
    {
          alert(o[removed]);
    }
}
//使用匿名方法,其原理与for循环内定义类似
for (var i = 0; i < els>
{
       var div = els[i];
       (function(o)
    {        
          o.onclick = function()
          {
              alert(o[removed]);
          }
    })(div);
}
通过中间方法a或者匿名方法,使for循环体与onclick事情产生闭包。
(3)控制变量的作用域,使点击div的事件所需变量与外层作用域无关。如:
for (var i = 0; i < els>
{      
      (function()
      {    
        var div = els[i];    
        div.onclick = function()
        {
            alert(div[removed]);
        }
      })();
}
内部函数自身也可能有内部函数。每次作用域链嵌套,都会增加由创建内部函数对象的执行环境所引发的新活动对象。ECMA262规范要求作用域链是临时性的,但对作用域链的长度却没有加以限制。闭包的潜规则即Function与内部定义的Function之间的相互作用域链上下文环境的关系。

灰机带翅膀
7楼 · 2021-04-08 16:10

闭包的特性:

有自由变量的匿名函数是闭包,闭包的特性,就是内层函数引用了外层函数中的变量,注意是引用哦。

        

所以,这会导致redis连接池中每个连接去做Dial的时候,拿到的都是for循环中最后一个gConfig,即连接的都是

redis服务器3,去redis服务器1里面拿数据当然就什么都拿不到了。



哈哈哈哈哈哈嗝
8楼 · 2021-04-09 09:06

闭包解决了什么问题

由于变量的作用域的原因-----(函数内部能读取全局变量,函数外部无法读取函数内部的变量【局部变量】),为了在函数外部读取局部变量,所以就有了闭包。


闭包的作用

1.访问其他函数内部变量

2.保护变量不被内存回收机制回收

3.避免全局变量被污染 方便调用上下文的局部变量 加强封装性


闭包的缺点

闭包长期占用内存,内存消耗很大,可能导致内存泄露



相关问题推荐

  • 回答 120

    相对前几年来说,要高上不少了,毕竟入行的人也是越来越多了,基础的工作对应想要参与的人群基数越来越大,但是对于高端人才的需求还是很多,人才还是相对稀缺性的。所以,想要学web或者其他技术也一样,别等,别观望。web前端就业方向特别多包括web前端开发...

  • 回答 25

    相对定位和绝对定位是定位的两种表现形式,区别如下:一、主体不同1、相对定位:是设置为相对定位的元素框会偏移某个距离。2、绝对定位:absolute 脱离文档流,通过 top,bottom,left,right 定位。二、特点不同1、相对定位:在使用相对定位时,无论是否进行移...

  • 抓包是什么意思?2020-04-01 17:36
    回答 7
    已采纳

    抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。抓包可以通过抓包工具来查看网络数据包内容。通过对抓获的数据包进行分析,可以得到有用的信息。目前流行的...

  • 回答 89

    常用的前端框架有Bootstrap框架、React框架、Vue框架、Angular框架、Foundation框架等等

  • 回答 65
    已采纳

    前端是目的就业前景非常不错的一个计算机技术,但是自学的话还是有一定难度的,网络上自学是碎片化的,同时互联网技术跟新换代快,自己的话比较吃力也学习不到最新的技术。

  • SSR 是什么意思?2020-03-20 18:56
    回答 6

    SSR就是一台服务器,可以利用 SSR 在远程的服务器上配置 SSR,使其能够成为 SSR 节点,这样本地电脑或者其它设备利用 SSR 节点实现 VPN 或者远程上网及游戏加速等方面。ShadowsocksR(简称 SSR)是 Shadowsocks 分支,在 Shadowsocks 的基础上增加了一些数据...

  • 回答 11

    1、代码判断xAxis: {type: &#39;time&#39;,splitLine: {show: false},interval: 3600, // 设置x轴时间间隔axisLabel: {formatter: function(value, index) {return liangTools.unix2hm(value)}}},首先要把xAxis 显示类型设置成time,然后设置对应X轴......

  • 回答 52
    已采纳

    计算机培训方向比较多,建议找适合自己的方向选择培训编程类:JAVA、WEB、Python、C/C++、C#等测试类:软件测试运维类:云计算、网络安全设计类:UI设计、3D建模等

  • 回答 8

    HTML5 + CSS + JavaScript 开发 跨平台重用代码 

  • 回答 4

    采用rem单位自动响应,并提供独有栅格化系统快速定义宽高、边距节省css代码量,同时总结各大型移动端网页,提供一套ui颜色搭配规范,尺寸规范,字体规范等。

  • 回答 10

    iView UI、ioni、SUI

  • 回答 6

     jQTouch 

  • 回答 4

    如果只是普通的移动端用vue react 或者dva 如果是要编译成小程序什么的或者混生 就用uni-app(对应vue语法)taro(对应react) 或者纯原生 这个没有限制的,自己怎么舒服怎么来

  • 回答 4

    因为可以运用在网页和小程序的开饭中,而且开源,用着便宜,企业都很喜欢

  • 回答 10

    一、Visual Studio Code下载地址:https://code.visualstudio.com/微软在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。Visual Stud...

  • 回答 9

    jQuery自带淡入淡出效果 https://www.w3school.com.cn/jquery/jquery_fade.asp 看看这个 

没有解决我的问题,去提问