模块模式
JavaScript 模块模式的实现与应用
问题
什么是模块模式?如何用 JavaScript 实现模块模式?
解答
模块模式利用闭包创建私有作用域,实现变量和方法的封装。
基本模块模式
const Counter = (function() {
// 私有变量
let count = 0;
// 私有方法
function log(msg) {
console.log(msg);
}
// 返回公共接口
return {
increment() {
count++;
log(`Count: ${count}`);
},
decrement() {
count--;
log(`Count: ${count}`);
},
getCount() {
return count;
}
};
})();
Counter.increment(); // Count: 1
Counter.increment(); // Count: 2
console.log(Counter.getCount()); // 2
console.log(Counter.count); // undefined(无法访问私有变量)
揭示模块模式
const Calculator = (function() {
let result = 0;
function add(x) {
result += x;
return this;
}
function subtract(x) {
result -= x;
return this;
}
function multiply(x) {
result *= x;
return this;
}
function getResult() {
return result;
}
function reset() {
result = 0;
return this;
}
// 揭示公共方法,映射到私有函数
return {
add,
subtract,
multiply,
getResult,
reset
};
})();
Calculator.add(10).subtract(3).multiply(2);
console.log(Calculator.getResult()); // 14
带参数的模块
const UserModule = (function(initialName) {
let name = initialName;
const history = [];
function recordChange(oldName, newName) {
history.push({ oldName, newName, time: Date.now() });
}
return {
getName() {
return name;
},
setName(newName) {
recordChange(name, newName);
name = newName;
},
getHistory() {
return [...history]; // 返回副本,防止外部修改
}
};
})('Guest');
console.log(UserModule.getName()); // Guest
UserModule.setName('Alice');
console.log(UserModule.getName()); // Alice
console.log(UserModule.getHistory()); // [{ oldName: 'Guest', newName: 'Alice', time: ... }]
模块扩展
const BaseModule = (function() {
let data = [];
return {
add(item) {
data.push(item);
},
getAll() {
return [...data];
}
};
})();
// 扩展模块
const ExtendedModule = (function(base) {
return {
...base,
remove(item) {
const all = base.getAll();
const index = all.indexOf(item);
if (index > -1) {
// 注意:这里无法直接操作原模块的私有 data
// 需要原模块提供 remove 方法
}
},
count() {
return base.getAll().length;
}
};
})(BaseModule);
ExtendedModule.add('a');
ExtendedModule.add('b');
console.log(ExtendedModule.count()); // 2
关键点
- IIFE(立即执行函数)创建独立作用域,防止污染全局
- 闭包保持对私有变量的引用,实现真正的私有性
- 返回对象作为公共 API,控制暴露的接口
- 揭示模块模式让公共方法命名更清晰,便于维护
- ES6 模块(import/export)是现代替代方案,但模块模式在理解闭包和封装上仍有价值
目录