如何在工厂函数中创建并返回外部不可修改的私有对象?
问题示例与需求
示例1:私有对象被外部篡改
const module = (function() { let _priv = {a:1}; return { get priv() {return _priv} } })(); let obj = module.priv; obj.b = 2; console.log(module.priv); //{a:1, b:2}
核心问题
在使用工厂函数(或揭示模块模式)时,如何获取私有对象的引用同时保证其不可变?
实用场景:井字棋游戏
const gameBoard = (function() { let _board = (new Array(9)).fill(''); // 希望外部无法直接修改 const add = (index,mark) => { _board[index] = mark; } const getBoard = () => {return _board} return {add, getBoard} })();
需求:仅通过add()方法修改_board,允许外部获取棋盘状态,但禁止外部直接篡改_board。
解决方法
核心逻辑是不直接返回私有对象的原始引用,通过返回副本或冻结对象的方式限制外部修改,以下是两种常用方案:
1. 返回对象/数组的副本
通过创建私有数据的浅拷贝,让外部拿到的是独立的新对象,不会影响原始私有数据。
- 数组可用展开语法
[...arr]或slice()创建副本 - 对象可用展开语法
{...obj}或Object.assign({}, obj)创建副本
修改示例1:
const module = (function() { let _priv = {a:1}; return { get priv() {return {..._priv}} // 返回对象副本 } })(); let obj = module.priv; obj.b = 2; console.log(module.priv); // {a:1} 私有对象未被修改
修改井字棋示例:
const gameBoard = (function() { let _board = (new Array(9)).fill(''); const add = (index,mark) => { _board[index] = mark; } const getBoard = () => {return [..._board]} // 返回数组副本 return {add, getBoard} })(); // 外部尝试篡改无效 const board = gameBoard.getBoard(); board[0] = 'X'; console.log(gameBoard.getBoard()); // 原数组仍保持初始状态
2. 使用Object.freeze()冻结返回对象
Object.freeze()可以禁止对对象进行添加、修改、删除属性的操作,数组同样适用。结合副本使用,既能保护原始数据,也能限制外部对返回值的修改。
修改示例1:
const module = (function() { let _priv = {a:1}; return { get priv() {return Object.freeze({..._priv})} // 先拷贝再冻结 } })(); let obj = module.priv; obj.b = 2; // 严格模式下报错,非严格模式静默失败 console.log(module.priv); // {a:1}
修改井字棋示例:
const gameBoard = (function() { let _board = (new Array(9)).fill(''); const add = (index,mark) => { _board[index] = mark; } const getBoard = () => {return Object.freeze([..._board])} return {add, getBoard} })(); const board = gameBoard.getBoard(); board[0] = 'X'; // 无法修改 console.log(gameBoard.getBoard()); // 原数组不受影响
注意事项
- 如果私有数据是嵌套结构(如对象包含子对象),需要实现深拷贝+深冻结,可通过递归遍历属性完成。
- 返回副本会每次创建新对象,性能敏感场景需权衡,但普通业务场景影响可忽略。
内容的提问来源于stack exchange,提问作者caveman418
相关产品推荐
相关产品推荐

