You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在工厂函数中创建并返回外部不可修改的私有对象?

问题示例与需求

示例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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 13:54:21