
本文揭示了因在模块中误用全局数组变量(如 factors = [])引发的跨调用状态污染问题:当 isPrime() 在 for 循环中反复调用时,共享的 factors 数组持续累积,导致质数判断始终失败。核心解法是将状态本地化,确保每次调用独立无副作用。
本文揭示了因在模块中误用全局数组变量(如 `factors = []`)引发的跨调用状态污染问题:当 `isprime()` 在 for 循环中反复调用时,共享的 `factors` 数组持续累积,导致质数判断始终失败。核心解法是将状态本地化,确保每次调用独立无副作用。
在 JavaScript 模块化开发中,一个看似微小的设计选择——将数据结构声明为模块顶层变量而非函数局部变量——可能引发难以察觉却极具破坏性的副作用。您遇到的“isPrime() 在 for 循环中始终返回 false”问题,正是典型的状态污染(state pollution)案例。
根本原因在于 factorList.js 中的 factors 数组被定义为模块级全局变量:
// ❌ 错误:模块顶层声明,所有调用共享同一数组
const factors = [];
const factorList = (num) => {
for (let i = 0; i <= num; i++) {
if (num % i === 0) {
factors.push(i); // 每次调用都向同一个数组追加
}
}
};当 eratosthenesSieve 循环执行 isPrime(1)、isPrime(2)、isPrime(3)……时,resources.factorList() 不断向同一个 factors 数组中 push 新因子。例如:
-
factorList(1)→factors = [1] -
factorList(2)→factors = [1, 1, 2] -
factorList(3)→factors = [1, 1, 2, 1, 3]
因此 isPrime(n) 中的 resources.factors.length === 2 判断永远为 false——因为 factors 数组从未清空,长度只增不减。
立即学习“Java免费学习笔记(深入)”;
✅ 正确做法是将 factors 移入函数作用域,使其成为每次调用的局部变量,并显式返回结果:
// ✅ 正确: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;相应地,更新 numPropFuncs.js 中的 isPrime 实现,避免依赖外部可变状态:
// ✅ 正确:numPropFuncs.js
const resources = require('./factorList.js');
const isPrime = (num) => {
// 直接接收并使用返回值,不依赖任何共享状态
const factors = resources.factorList(num);
return factors.length === 2;
};
module.exports.isPrime = isPrime;此外,还需注意两个关键细节:
-
边界安全:原代码
for (let i = 0; ...)在num % 0时会抛出RangeError: modulo by zero。务必从i = 1开始迭代。 -
算法效率:当前
factorList时间复杂度为 O(n),对大数不友好。生产环境建议优化为 O(√n),但本例聚焦于状态隔离问题,暂不展开。
最后,修复后的主程序可保持简洁:
const { isPrime } = require('./numPropFuncs.js');
const limit = 30;
const primes = [];
const eratosthenesSieve = (num) => {
for (let i = 2; i <= num; i++) { // 从 2 开始(最小质数)
if (isPrime(i)) {
primes.push(i);
}
}
};
eratosthenesSieve(limit);
console.log(primes.join(', ')); // 输出:2, 3, 5, 7, 11, 13, 17, 19, 23, 29总结:在 Node.js 模块中,避免在模块顶层声明可变状态(如数组、对象);优先使用纯函数设计——输入确定、输出唯一、无副作用。这不仅解决当前问题,更是构建可测试、可复用、线程安全(在并发场景下)代码的基础原则。


















