Node.js递归统计文件数时counter++计数错误问题求助
问题分析与解决方案
你的问题核心在于JavaScript的参数传递机制:原始类型(比如数字totalFiles)是按值传递的,而数组是按引用传递的。
当你把totalFiles作为参数传入递归函数时,每次递归调用都会创建这个数值的副本——你在递归里执行totalFiles++,修改的只是当前函数作用域内的副本,根本不会影响外层的totalFiles变量。而数组totalFilesArray是引用类型,所有递归调用共享同一个数组引用,所以push操作能正确累计所有文件。
解决方法1:用引用类型包裹计数器(最贴近你的现有代码)
把计数器放到一个对象里,因为对象是引用类型,递归时传递的是同一个对象的引用,修改对象的属性会同步到所有作用域:
const fs = require('fs') const path = require('path') const dir = '/Users/Username' // 用对象包裹计数器,确保递归共享同一个引用 const counter = { total: 0 } const totalFilesArray = [] getFilesRecursively(dir, counter, totalFilesArray) console.log(counter.total, 'total_files_count', totalFilesArray.length, 'total_files_array_count') function getFilesRecursively(dir, counter, totalFilesArray){ try { const files = fs.readdirSync(dir) for (const file of files) { // 建议用for...of代替for...in,避免遍历到原型链属性 const name = path.join(dir, file) try { const stats = fs.lstatSync(name) if (stats.isDirectory()) { getFilesRecursively(name, counter, totalFilesArray) } else if (stats.isFile()) { counter.total++ // 直接修改对象属性,所有递归共享 totalFilesArray.push(name) } } catch(err) { console.error(err.message) } } } catch(err) { console.error(err.message) } }
解决方法2:让递归函数返回当前目录的文件数(更函数式)
不需要传递计数器,每次递归返回当前目录下的文件总数(包括子目录的),然后在外层累加:
const fs = require('fs') const path = require('path') const dir = '/Users/Username' const totalFiles = getFilesRecursively(dir) console.log(totalFiles, 'total_files_count') function getFilesRecursively(dir){ let count = 0 try { const files = fs.readdirSync(dir) for (const file of files) { const name = path.join(dir, file) try { const stats = fs.lstatSync(name) if (stats.isDirectory()) { count += getFilesRecursively(name) // 累加子目录的文件数 } else if (stats.isFile()) { count++ } } catch(err) { console.error(err.message) } } } catch(err) { console.error(err.message) } return count }
针对大目录的性能优化
你的目录有26万+文件,同步方法fs.readdirSync会阻塞Node.js的事件循环,导致进程无法处理其他任务。推荐使用异步非阻塞的方式,比如fs.promises的API:
const fs = require('fs').promises const path = require('path') const dir = '/Users/Username' async function getFilesRecursively(dir){ let count = 0 try { const files = await fs.readdir(dir) // 并行处理所有文件,提升速度 const promises = files.map(async file => { const name = path.join(dir, file) try { const stats = await fs.lstat(name) if (stats.isDirectory()) { count += await getFilesRecursively(name) } else if (stats.isFile()) { count++ } } catch(err) { console.error(err.message) } }) await Promise.all(promises) } catch(err) { console.error(err.message) } return count } // 执行并打印结果 getFilesRecursively(dir).then(total => { console.log(total, 'total_files_count') })
这种异步方式不会阻塞进程,而且Promise.all可以并行处理多个文件/目录,比同步递归速度更快。
内容的提问来源于stack exchange,提问作者Systems Rebooter
相关产品推荐
相关产品推荐

