Nodejs 两个的函数的区别谁能说下
Nodejs 两个的函数的区别谁能说下
var fs = require(‘fs’);
var files = [‘a.txt’, ‘b.txt’, ‘c.txt’];
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], ‘utf-8’, function(err, contents) {
console.log(files);
console.log(i);
console.log(files[i]);
});
}
var fs = require(‘fs’);
var files = [‘a.txt’, ‘b.txt’, ‘c.txt’];
for (var i = 0; i < files.length; i++) {
(function(i) {
fs.readFile(files[i], ‘utf-8’, function(err, contents) {
console.log(files[i] + ': ’ + contents);
});
})(i);
}```
书上说第一个是模块的的 readFile
中的回调函数一直是一个实例,第二个则是用了闭包,就创建三个实例,感觉不是很明白,麻烦解答下
当然可以。让我们来详细分析这两个代码片段,并解释它们之间的区别。
代码片段 1
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files);
console.log(i);
console.log(files[i]);
});
}
代码片段 2
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
for (var i = 0; i < files.length; i++) {
(function(i) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files[i] + ': ' + contents);
});
})(i);
}
解析
共同点
两个代码片段都使用了 fs.readFile
方法来异步读取文件。fs.readFile
是 Node.js 的一个内置方法,用于读取文件内容。
不同点
代码片段 1:
- 在循环中直接定义了一个回调函数。
- 这个回调函数引用了外部变量
files
和i
。 - 因为
i
是一个全局变量,在循环结束后它的值会变成files.length
(即3)。 - 当回调函数执行时,
i
已经不再是当前文件的索引,而是循环结束后的值,导致输出不正确。
代码片段 2:
- 使用立即调用函数表达式(IIFE)为每个循环迭代创建一个新的作用域。
- IIFE 接收当前的
i
值并传递给内部的回调函数。 - 每次回调函数执行时,它引用的是该迭代的
i
值,而不是循环结束后的值。 - 因此,每次回调函数都能正确地访问到对应的文件名和内容。
示例输出
假设文件 a.txt
, b.txt
, c.txt
分别包含以下内容:
a.txt
: “Hello”b.txt
: “World”c.txt
: “!”
代码片段 1 输出
由于 i
在所有回调函数执行时都是 3
,输出可能类似于:
['a.txt', 'b.txt', 'c.txt']
3
undefined
['a.txt', 'b.txt', 'c.txt']
3
undefined
['a.txt', 'b.txt', 'c.txt']
3
undefined
代码片段 2 输出
正确的输出应该是:
a.txt: Hello
b.txt: World
c.txt: !
结论
使用闭包(代码片段 2)确保了每次回调函数执行时都能访问到正确的文件名和内容,而直接在循环中定义回调函数(代码片段 1)会导致访问到错误的变量值。
楼主代码没标记没格式化… 帮楼主做了.
两个回调函数区别在对作用域外部的变量的引用上边…
第一个函数里 i
使用了同一个外部的 i
, 第二个回调里每次拷贝一个 i
到函数里.
运行的结果不同…
但不明白为什么这东西被叫做"实例"…
两段代码的运行结果是不一致的,这个在js中是很常见的代码。运用闭包保证当前运行环境,个人是这么理解的,这是解释性语言的原因?代码执行不进行预编译。需要通过闭包来确定当前内部状态? 求大神指教- -
谢谢楼上的解答,关于实例的问题我也是从书上看到的
用面向对象来解释: for (var i = 0; i < files.length; i++) { fs.readFile(…, function(err, contents) { console.log(i); }); } 相当于,在执行环境中,this.i = 0; for 运行了3次循环, this.i = 0; this.i = 1; this.i=2; 当最后异步回调的时候会去取用this.i === 2;
for (var i = 0; i < files.length; i++) { (function(i) { fs.readFile(…, function(err, contents) { console.log(files[i] + ': ’ + contents); }); })(i); } 相当于,在执行环境中,this.i = 0; for 运行了3次循环, this.i = 0; this.i = 1; this.i=2; 但是, 在每次循环, 使用function () {} () 创建一个匿名的“对象”, 并使匿名对象的属性i 赋值为外部的i. 每个匿名“对象”的异步回调函数在匿名“对象”内的执行环境中,访问匿名“对象”的i.
关键是内存占用. for 中的i只是一个内存位置. 当function () {} () 会在内部启动一个内存占用. (每个循环会生成一个内存占用)
关于内存占用的回收: 当所处的函数环境不再对变量有引用的时候, 内存会被回收.
第一种情况你console的都是循环后的结果,因为是异步的,里面的i是无法同步访问的,所以你每次打印的i的值都是最后一次i的值。 第二种情况就是为了解决第一种情况用了function闭包,把i的值保存下来了没有被gc回收,所以可以打印每次i不同的值
提一个另外的话题,建议写法:
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
files.forEach(read);
function read(filename) {
fs.readFile(filename, ‘utf8’, function (err, content) {
if (err) return console.errror(err.stack);
console.log(content);
});
}
很多 callback hell 是可以通过提取函数来解决的。另外,array 的 forEach, map, reduce, filter 这些方法也还是挺好用的。
其实写一句 var item = files[i],再把item传到异步的函数里就行了。
这段代码展示了Node.js中两个处理文件读取的方式。第一个示例使用了一个全局变量i
来访问循环中的索引,而第二个示例则使用了闭包来为每次迭代创建一个独立的上下文。
示例1
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files);
console.log(i);
console.log(files[i]);
});
}
在这个例子中,i
是全局变量,所以在回调函数执行时,它可能已经改变。因此,你可能会看到输出的i
值总是等于files.length
,因为循环已经结束。
示例2
for (var i = 0; i < files.length; i++) {
(function(i) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files[i] + ': ' + contents);
});
})(i);
}
这里使用了一个立即执行函数表达式(IIFE)来创建一个新的作用域,这样每次迭代都会有一个新的i
绑定到当前的索引。因此,每个回调函数都能正确地引用对应的文件。
通过这种方式,第二个示例能够正确地输出每个文件的内容。