Nodejs 学习笔记之循环与闭包

来源:互联网 发布:施工组织设计编制软件 编辑:程序博客网 时间:2024/05/20 03:08

首先,来看一段代码(来自《nodejs开发指南》)

for( var i = 0;i<files.length;i++){    fs.readFile(files[i],'utf-8',function (err,contents) {        console.log(files[i] + ':' + contents);    })}

结果是

undefined:AAAundefined:BBBundefined:CCC

输出 undefined 是因为 fs.readFile是一个异步方法,它的回调在for循环执行完毕后才开始执行, 而此时变量 i=files.length, 超出了边界。如果我们打印出i,发现i的值一直都是3。那么如何解决这个问题呢?我们手动创建一个闭包,如下:

varfs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
for (vari =0; i < files.length; i++) {
(
function(i) {
fs.readFile(files[i], 'utf-8',
function(err, contents) {
console.log(files[i] + ': ' + contents);
});
})(i);
}

输出如下:

a.txt: AAA
c.txt: CCC
b.txt: BBB


上述方法,解决了undefined的问题,但是显然可读性较差 ,因此建议将for循环写成如下方式:

files.forEach(function (filename) {    fs.readFile(filename,'utf-8',function (err,contents) {        console.log(filename+':'+contents);    })})

上述例子中涉及到了闭包的问题,那么什么是闭包呢?

通俗地讲, JavaScript 中每个的函数都是一个闭包,但通常意义上嵌套的函数更能够体
现出闭包的特性,请看下面这个例子:
var generateClosure = function() {
var count = 0;
var get = function() {
count ++;
return count;
};
return get;
};
var counter = generateClosure();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3
这段代码中, generateClosure() 函数中有一个局部变量count, 初值为 0。还有一
个叫做 get 的函数, get 将其父作用域,也就是 generateClosure() 函数中的 count 变
量增加 1,并返回 count 的值。 generateClosure() 的返回值是 get 函数。在外部我们
通过 counter 变量调用了 generateClosure() 函数并获取了它的返回值,也就是 get 函
数,接下来反复调用几次 counter(),我们发现每次返回的值都递增了 1。
让我们看看上面的例子有什么特点,按照通常命令式编程思维的理解, count 是
generateClosure 函数内部的变量,它的生命周期就是 generateClosure 被调用的时
期,当 generateClosure 从调用栈中返回时, count 变量申请的空间也就被释放。问题
是,在 generateClosure() 调用结束后, counter() 却引用了“已经释放了的” count
变量,而且非但没有出错,反而每次调用 counter() 时还修改并返回了 count。这是怎
么回事呢?
这正是所谓闭包的特性。当一个函数返回它内部定义的一个函数时,就产生了一个闭包,
闭 包 不 但 包 括 被 返 回 的 函 数 , 还 包 括 这 个 函 数 的 定 义 环 境 。 上 面 例 子 中 , 当 函 数
generateClosure() 的内部函数 get 被一个外部变量 counter 引用时, counter 和
generateClosure() 的局部变量就是一个闭包。如果还不够清晰,下面这个例子可以帮助
你理解:
var generateClosure = function() {
var count = 0;
var get = function() {
count ++;
return count;
};
return get;
};
var counter1 = generateClosure();
var counter2 = generateClosure();
console.log(counter1()); // 输出 1
console.log(counter2()); // 输出 1
console.log(counter1()); // 输出 2
console.log(counter1()); // 输出 3
console.log(counter2()); // 输出 2
上面这个例子解释了闭包是如何产生的:counter1 和 counter2 分别调用了 generateClosure() 函数,生成了两个闭包的实例,它们内部引用的 count 变量分别属于各自的
运行环境。我们可以理解为,在 generateClosure() 返回 get 函数时,私下将 get 可
能引用到的 generateClosure() 函数的内部变量(也就是 count 变量)也返回了,并
在内存中生成了一个副本,之后 generateClosure() 返回的函数的两个实例 counter1
和 counter2 就是相互独立的了。


更多解释可以参照: https://wenku.baidu.com/view/f5367cc2bb4cf7ec4afed009.html