js闭包陷阱问题
JavaScript是一种非常强大的函数式编程语言,可以动态创建函数对象。
由于JavaScript还支持闭包(Closure),因此,函数可以引用其作用域外的变量,非常强大。
来看看在JavaScript中使用闭包的陷阱:
var tasks = [];
for (var i=0; i<3; i++) {
tasks.push(function() {
console.log('>>> ' + i);
});
}
console.log('end for.');
for (var j=0; j<tasks.length; j++) {
tasks[j]();
}
如果在循环中创建函数,并引用循环变量,原意是打印出0,1,2,但结果却是一样的:
end for.
>>> 3
>>> 3
>>> 3
这个问题的原因在于,函数创建时并未执行,所以先打印end for.,然后才执行函数。
由于函数引用了循环变量i,在函数执行时,由于i的值已经变成了3,所以,打印出的结果不对。
如果我们用一个变量n来复制一份i传入呢?
var tasks = [];
for (var i=0; i<3; i++) {
var n = i;
tasks.push(function() {
console.log('>>> ' + n);
});
}
console.log('end for.');
for (var j=0; j<tasks.length; j++) {
tasks[j]();
}
结果还是不对:
end for.
>>> 2
>>> 2
>>> 2
注意到i会比n多加一次,所以n的最终值是2。
这个问题的原因是JavaScript虽然有局部变量,但局部变量只有函数作用域,而没有其他语言通常有的块作用域(循环内部,if内部),所以,无论在函数内部的哪个地方定义变量,变量作用域都是整个函数。
把上述代码的变量提取到函数开头,其实是这样的:
var tasks, i, n, j;
tasks = [];
for (i=0; i<3; i++) {
n = i;
tasks.push(function() {
console.log('>>> ' + n);
});
}
console.log('end for.');
for (j=0; j<tasks.length; j++) {
tasks[j]();
}
完全等价。
根据道爷的说法,JavaScript缺少块作用域是语言设计的一个缺陷。所以,上面的代码有问题,就是因为n的作用域是整个函数,而不是循环。
创建闭包的一条原则就是:
不要引用循环变量!
这条原则对有没有块作用域的函数式编程语言都适用。
如果一定要在闭包中引用循环变量怎么办???
方法是再创建一个函数,将循环变量作为函数参数传入:
var tasks = [];
for (var i=0; i<3; i++) {
var fn = function(n) {
tasks.push(function() {
console.log('>>> ' + n);
});
};
fn(i);
}
这段代码是正确的,是因为循环内部,fn()函数立即被执行,因此,闭包拿到的参数n就是当前循环变量的值的副本。
最后,简化语法,直接用匿名函数的立即执行模式(function() { ... })()改写如下:
var tasks = [];
for (var i=0; i<3; i++) {
(function(n) {
tasks.push(function() {
console.log('>>> ' + n);
});
})(i);
}
来自:http://www.liaoxuefeng.com/article/001398146224424978bbe1df8594901b2d477d1619cd7ca000
js闭包陷阱问题的更多相关文章
- js闭包的作用
js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文 我们来看 ...
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- 大部分人都会做错的经典JS闭包面试题
由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- js闭包之初步理解( JavaScript closure)
闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...
- (原创)JS闭包看代码理解
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- js闭包理解
js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- Js闭包的用途
本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...
随机推荐
- Banner 切换
在线项目 : Banner 切换 时间 : 2个小时 (15:00 - 17:00)满分 : 100分------------------------------------------------ ...
- VS2012JSON自动生成对应的类
一.复制JSON数据如图 {Key:"aaaa",Value:"bbbb"} 二.点击以下操作
- git管理修改
为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件. 新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改 ...
- ubuntu 12.04 安装谷歌浏览器
http://hi.baidu.com/kevin276/item/29bc1c96a208fabc82d29542 sudo dpkg -i google-chrome-stable_current ...
- js第四章作用域
一.动态的属性 //创建了一个变量并且保存在了变量person中 var person = new Object(); //为该对象添加了一个名为name的属性,将字符串值‘NiCholas’赋值给n ...
- Ghost win7 系统安装(虚拟机)
1.将Ghost.iso添加到数据存储iso文件中,启动虚拟机,根据相关提示,文件格式化系统硬盘,完成操作后关机: 2.进入BIOS,设置从CDROM启动系统(否则系统会提示找到引导文件): 3.将系 ...
- python 学习day6(面向对象)
博客部分内容转自:http://www.cnblogs.com/wupeiqi/p/4493506.html 面向对象编程介绍 为什么要用面向对象进行开发? 面向对象的特性:封装.继承.多态 类.方法 ...
- JNDI(转载)
转自:http://javacrazyer.iteye.com/blog/759485 原理: 在DataSource中事先建立多个数据库连接,保存在数据库连接池中.当程序访问数据库时 ...
- Qt5.3.0 for android windows平台下搭建及demo(虫子的博客)
----我的生活,我的点点滴滴!! 部门领导突然心血来潮,想在android平台上做应用,但是我们大多产品属于嵌入式(本吊只负责写写应用,苦比的被强行顶过来搞这,由于这还得领导吵了一架,架虽然吵完了, ...
- MFC的规则DLL中资源使用的问题
今天刚刚做了一个静态链接到MFC的规则dll,即“Use MFC in a static library“,该dll用来显示一个窗体.然后在一个mfc可执行程序A.exe中调用这个dll文件,可以正常 ...