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. 基于zepto的手机焦点图touchstart touchmove

    基于zepto的手机焦点图,查看地址:demo (建议使用手机浏览器查看)代码如下: <!DOCTYPE HTML> <html> <head> <title ...

  2. winform使用xml作为数据源

    1.新建窗体应用程序 2.拖放DataGridView 3.在bin\Debug中放入XML文件 using System; using System.Collections.Generic; usi ...

  3. ES6笔记① var 和 let的区别

    let 和 var的区别    答:不同点在于作用域 1.(全局下)首先  let关键字声明的变量是这样写会导致错误. let声明的变量类似于”本地变量“,函数内如何不重新声明,还是会被改变 var ...

  4. POJ 3279(Fliptile)题解

    以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定长宽的黑白棋棋盘摆满棋子,每次操作可以反转一个位置和其上下左右共五个位置的棋子的颜色,求要使用最少翻转次数将所有棋子反转为黑 ...

  5. data stage走起

    如题,希望以后可以找到相应的工作.(已经工作3年以上了)

  6. C语言中内存分配 (转)

    在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要介绍内存管理基本概念,重 ...

  7. jquery.post方法回调函数

    1 function(data){} 此post请求成功后调用之,data是请求成功后服务器返回的东西.如果在servlet中有response.getWriter().println("s ...

  8. 安装 Archlinux 小记

    故事的背景 开始的时候装的 win8 + ubuntu 双系统,但是慢慢感觉只要有 windows 存在,在 Linux 上遇到问题了就想逃回去. 在一次 GDG 的演讲中听到的: 趁现在还年轻,还有 ...

  9. WPS Office手机版调用接口代码指导帖之一(Android)

    经常会有一些喜欢开发鼓捣的童鞋问我们,WPS Office手机版是否提供调用接口,希望在android中使用一个调用命令,直接调用WPS手机版来打开指定的DOC文件,而不用弹出一个程序可选列表(如果用 ...

  10. Flex中如何通过设置GridLines对象的horizontalAlternateFill样式交错显示LineSeries图表背景颜色的例子

    原文 http://blog.minidx.com/2008/11/27/1652.html 接下来的例子演示了Flex中如何通过设置GridLines对象的horizontalAlternateFi ...