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 中的回调函数一直是一个实例,第二个则是用了闭包,就创建三个实例,感觉不是很明白,麻烦解答下


9 回复

当然可以。让我们来详细分析这两个代码片段,并解释它们之间的区别。

代码片段 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:

  • 在循环中直接定义了一个回调函数。
  • 这个回调函数引用了外部变量 filesi
  • 因为 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绑定到当前的索引。因此,每个回调函数都能正确地引用对应的文件。

通过这种方式,第二个示例能够正确地输出每个文件的内容。

回到顶部