
本文揭示了因在模块中误用全局变量(如共享数组)导致导入函数在循环中失效的根本原因,并提供基于局部作用域和纯函数原则的修复方案。
本文揭示了因在模块中误用全局变量(如共享数组)导致导入函数在循环中失效的根本原因,并提供基于局部作用域和纯函数原则的修复方案。
在使用埃拉托斯特尼筛法(Sieve of Eratosthenes)实现素数筛选时,一个看似逻辑正确的代码却始终返回空数组或全 false 结果——问题往往不在于算法本身,而在于模块间状态污染。核心症结在于:factorList.js 中将 factors 声明为模块级顶层变量(即全局于该模块),导致每次调用 factorList() 都向同一数组持续追加因子,而非为每个数字生成独立的结果。
具体表现为:
- 第一次调用
isPrime(1)→factors = [1] - 第二次调用
isPrime(2)→factors = [1, 1, 2](因未清空,重复压入1和2) - 后续调用中
factors.length持续增长,永远 ≠ 2 →isPrime()恒返回false
✅ 正确做法:消除共享状态,拥抱纯函数
应将 factors 变量移入 factorList 函数作用域内,并显式返回结果数组:
// ./factorList.js
const factorList = (num) => {
const factors = []; // ✅ 局部变量,每次调用均新建
for (let i = 1; i <= num; i++) { // ⚠️ 注意:i 从 1 开始(i=0 会导致除零错误!)
if (num % i === 0) {
factors.push(i);
}
}
return factors;
};
module.exports.factorList = factorList;相应地,更新 isPrime 的实现,直接使用返回值而非依赖外部可变状态:
立即学习“Java免费学习笔记(深入)”;
// ./numPropFuncs.js
const resources = require('./factorList.js');
const isPrime = (num) => {
// ✅ 纯函数:无副作用,仅依赖输入参数
const factors = resources.factorList(num);
return factors.length === 2;
};
module.exports.isPrime = isPrime;最后,优化主筛法逻辑(注意边界与效率):
// main.js
const { isPrime } = require('./numPropFuncs.js');
const limit = 30;
const primes = [];
const eratosthenesSieve = (num) => {
// ✅ 优化:素数 ≥ 2,跳过 0 和 1
for (let i = 2; i <= num; i++) {
if (isPrime(i)) {
primes.push(i);
}
}
};
eratosthenesSieve(limit);
console.log(primes.join(', ')); // 输出:2, 3, 5, 7, 11, 13, 17, 19, 23, 29⚠️ 关键注意事项
-
永远避免模块级可变状态:Node.js 中
require()缓存模块导出,顶层变量在多次require后仍指向同一内存地址,极易引发隐蔽的跨调用污染。 -
修正除零风险:原
factorList中i从0开始会导致num % 0报错(NaN或RangeError),必须从1起始。 -
性能提醒:当前
factorList时间复杂度为 O(n),对大数不友好;生产环境建议改用 O(√n) 算法(遍历至Math.floor(Math.sqrt(num)))并合并因子对。
遵循“局部变量 + 显式返回 + 无副作用”的函数式设计原则,不仅能解决本例问题,更是构建可维护、可测试 JavaScript 模块的基石。


















