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闭包陷阱问题的更多相关文章

  1. js闭包的作用

    js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文   我们来看 ...

  2. js闭包的作用域以及闭包案列的介绍:

    转载▼ 标签: it   js闭包的作用域以及闭包案列的介绍:   首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...

  3. 大部分人都会做错的经典JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  4. Js闭包常见三种用法

        Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...

  5. js闭包之初步理解( JavaScript closure)

    闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...

  6. (原创)JS闭包看代码理解

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

  7. js闭包理解

    js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...

  8. js闭包理解实例小结

    Js闭包 闭包前要了解的知识  1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...

  9. Js闭包的用途

    本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...

随机推荐

  1. read op case $op in

     read op case $op in

  2. java中的302和sendRedirect的区别

    java中有一个sendRedirect函数这个用于跳转到另外一个页面,这个实际上是一个302跳转,但是不完全等同于302跳转 点击(此处)折叠或打开 response.sendRedirect(&q ...

  3. iOS下的实际网络连接状态检测(转)

    序言 网络连接状态检测对于我们的iOS app开发来说是一个非常通用的需求.为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示.对绝大部分iOS开发者来说,从苹果示例代 ...

  4. Windows下的窗口程序

    再进一步. . .model flat,stdcall option casemap:none include Windows.inc include user32.inc includelib us ...

  5. dialog获取焦点

    弹出层是一个iframe openWindow:function (options) { var url = options.url; url += url.indexOf("?" ...

  6. Android Activity之 setContentView()总结

    从一开始hello world的第一个安卓应用开始,Activity 自动生成,布局自动生成,直接修改布局,在Activity中,findviewById()找到view,然后处理相应的业务逻辑即可, ...

  7. 【学习笔记01】:hover为DIV添加鼠标悬停时改变颜色的效果

    :hover所有主流浏览器都支持(IE6.0以下支持不好,以后再学习用Javascript来实现悬停效果) 这是一个绿色底白色Icon的搜索按钮

  8. 关于PHPExcel

    在学PHPExcel的时候,在网上查了很多资料,花了很多时间,下面是我想要分享给大家的,我找到的并进行了一定修改的亲身实践成功的资料,希望大家对大家有所帮助. 首先,需要下载PhpExcel资料,下载 ...

  9. leetcode算法刷题(三)

    今天在刷了几道简单的动态规划后,又看了看string方面的题 第五题 Longest Palindromic Substring 题目的意思:求一个字符串的最长回文子串 分析:开始,我的想法是,现在字 ...

  10. 如何修改Struts2 FiledError样式

    有时候,使用Struts2的<s:fielderror />标签还是比较好的.但是这个标签本来的样式实在很难看,不但换行,而且有个大大的黑点.一看就知道用了<ui><li ...