[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之间的相互作用域链上下文环境的关系。
SSR就是一台服务器,可以利用 SSR 在远程的服务器上配置 SSR,使其能够成为 SSR 节点,这样本地电脑或者其它设备利用 SSR 节点实现 VPN 或者远程上网及游戏加速等方面。ShadowsocksR(简称 SSR)是 Shadowsocks 分支,在 Shadowsocks 的基础上增加了一些数据...
一、Visual Studio Code下载地址:https://code.visualstudio.com/微软在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。Visual Stud...
同事在初始化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里面拿数据当然就什么都拿不到了。
一个是外部可以读取内部函数的变量,二是变量会始终在内存中,不会被回收机制回收,三是避免全局变量被污染,方便调用上下文的局部变量,加强封装性。
大量使用闭包会导致内存泄露,所以不用的变量应该手动设置为null。
在初始化redis配置的时候,给Dial函数赋值时用了闭包,导致程序上线后,数据怎么都加载不到redis中去,排查了半个多小时,总算找到了罪魁祸首。虽然自己之前对闭包也算了解,但是看到他的那段代码的时候,乍一看竟也没发现出问题来,所以决定写篇文章加深印象,避免自己以后也犯类似的问题。
闭包(closure)是 JavasSript 的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。
变量作用域
要理解闭包,首先要理解 JavasSript 的特殊的变量作用域。
变量的作用域无非就两种:全局变量和局部变量。
JavasSript 语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
注意点:在函数内部声明变量的时候,一定要使用 var 命令。如果不用的话,你实际上声明的是一个全局变量!
闭包的功能强大,但如果没有正确理解闭包的概念,其结果往往出乎人的意料。例如,下面是一个较常见的问题:
第一个
第二个
第三个
第四个
[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之间的相互作用域链上下文环境的关系。
闭包的特性:
有自由变量的匿名函数是闭包,闭包的特性,就是内层函数引用了外层函数中的变量,注意是引用哦。
所以,这会导致redis连接池中每个连接去做Dial的时候,拿到的都是for循环中最后一个gConfig,即连接的都是
redis服务器3,去redis服务器1里面拿数据当然就什么都拿不到了。
闭包解决了什么问题
由于变量的作用域的原因-----(函数内部能读取全局变量,函数外部无法读取函数内部的变量【局部变量】),为了在函数外部读取局部变量,所以就有了闭包。
闭包的作用
1.访问其他函数内部变量
2.保护变量不被内存回收机制回收
3.避免全局变量被污染 方便调用上下文的局部变量 加强封装性
闭包的缺点
闭包长期占用内存,内存消耗很大,可能导致内存泄露
相关问题推荐
相对前几年来说,要高上不少了,毕竟入行的人也是越来越多了,基础的工作对应想要参与的人群基数越来越大,但是对于高端人才的需求还是很多,人才还是相对稀缺性的。所以,想要学web或者其他技术也一样,别等,别观望。web前端就业方向特别多包括web前端开发...
相对定位和绝对定位是定位的两种表现形式,区别如下:一、主体不同1、相对定位:是设置为相对定位的元素框会偏移某个距离。2、绝对定位:absolute 脱离文档流,通过 top,bottom,left,right 定位。二、特点不同1、相对定位:在使用相对定位时,无论是否进行移...
抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。抓包可以通过抓包工具来查看网络数据包内容。通过对抓获的数据包进行分析,可以得到有用的信息。目前流行的...
常用的前端框架有Bootstrap框架、React框架、Vue框架、Angular框架、Foundation框架等等
前端是目的就业前景非常不错的一个计算机技术,但是自学的话还是有一定难度的,网络上自学是碎片化的,同时互联网技术跟新换代快,自己的话比较吃力也学习不到最新的技术。
SSR就是一台服务器,可以利用 SSR 在远程的服务器上配置 SSR,使其能够成为 SSR 节点,这样本地电脑或者其它设备利用 SSR 节点实现 VPN 或者远程上网及游戏加速等方面。ShadowsocksR(简称 SSR)是 Shadowsocks 分支,在 Shadowsocks 的基础上增加了一些数据...
1、代码判断xAxis: {type: 'time',splitLine: {show: false},interval: 3600, // 设置x轴时间间隔axisLabel: {formatter: function(value, index) {return liangTools.unix2hm(value)}}},首先要把xAxis 显示类型设置成time,然后设置对应X轴......
计算机培训方向比较多,建议找适合自己的方向选择培训编程类:JAVA、WEB、Python、C/C++、C#等测试类:软件测试运维类:云计算、网络安全设计类:UI设计、3D建模等
HTML5 + CSS + JavaScript 开发 跨平台重用代码
采用rem单位自动响应,并提供独有栅格化系统快速定义宽高、边距节省css代码量,同时总结各大型移动端网页,提供一套ui颜色搭配规范,尺寸规范,字体规范等。
iView UI、ioni、SUI
jQTouch
如果只是普通的移动端用vue react 或者dva 如果是要编译成小程序什么的或者混生 就用uni-app(对应vue语法)taro(对应react) 或者纯原生 这个没有限制的,自己怎么舒服怎么来
因为可以运用在网页和小程序的开饭中,而且开源,用着便宜,企业都很喜欢
一、Visual Studio Code下载地址:https://code.visualstudio.com/微软在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。Visual Stud...
jQuery自带淡入淡出效果 https://www.w3school.com.cn/jquery/jquery_fade.asp 看看这个