JavaScript如何批量将对象所有属性设为只读

作者:技术拾光时间:2026-07-26 08:25:13浏览:368

使用Object.freeze()可冻结对象顶层属性,但无法递归处理嵌套对象。深层冻结需手动递归遍历子对象。Object.defineProperty配合Object.keys()或getOwnPropertyNames()实现更细粒度的只读设置。注意不可枚举属性、Proxy及Map等内置集合的内部状态无法被冻结。

先说结论:要将对象的所有自有属性快速设置为只读,直接使用 Object.freeze() 即可。但需注意,该方法仅冻结当前层级,不会递归处理嵌套对象——即只能冻结一层。

JavaScript如何批量将对象所有属性设为只读

下面拆解这几种方案,同时指出常见的易错点。

基础用法:冻结顶层对象

Object.freeze() 是最直接的方法,适用于简单扁平的对象。调用后,对象属性的 writable、configurable、enumerable 描述符均被锁定。尝试修改值、添加新属性或删除现有属性,在非严格模式下会静默失败,严格模式下直接抛出错误。

但需注意,它只处理对象自身的属性,不会影响原型链上的属性。示例如下:

const obj = { a: 1, b: 2 };
Object.freeze(obj);
obj.a = 99;      // 静默失败
obj.c = 3;       // 无法添加
delete obj.b;    // 无法删除
console.log(obj); // { a: 1, b: 2 }

深层冻结:递归处理嵌套对象

当对象包含嵌套子对象或数组时,需手动递归实现深层冻结。大致步骤:先检查当前值是否为普通对象或数组(排除 null、基本类型、Date、RegExp 等),然后对每个可遍历的自有属性递归调用冻结函数,最后冻结当前层。

一个简单实现如下:

function deepFreeze(obj) {
  if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
    Object.getOwnPropertyNames(obj).forEach(prop => {
      if (obj[prop] !== null && typeof obj[prop] === 'object') {
        deepFreeze(obj[prop]);
      }
    });
    Object.freeze(obj);
  }
  return obj;
}

使用时直接调用 deepFreeze({ a: 1, nested: { x: 2 } }),此时 nested.x 也无法修改。

替代方案:用 Object.defineProperty 批量设置

如需更精细的控制——例如仅将 writable 设为 false 而保留 configurable 为 true——则 Object.freeze() 不够灵活。此时可借助 Object.defineProperty 配合 Object.keys() 逐属性设置。

注意 Object.keys() 仅处理可枚举属性,若要覆盖不可枚举属性,需使用 Object.getOwnPropertyNames()。以下函数可实现:

function makeAllReadOnly(obj) {
  Object.getOwnPropertyNames(obj).forEach(key => {
    Object.defineProperty(obj, key, {
      writable: false,
      configurable: false,
      enumerable: true // 保持原有枚举性
    });
  });
  return obj;
}

注意事项与限制

Object.freeze() 及相关操作虽方便,但需了解以下边界:

  • 不能冻结不可枚举属性(symbol 属性例外,但 getOwnPropertyNames 可覆盖到,因此不算坑);
  • 无法冻结 Proxy、Map、Set 等内置集合的内部状态——只能冻结其对外暴露的引用,内部结构仍可修改;
  • 冻结后,属性值仍可读取,方法也可调用。但若方法内部修改了 this 或外部变量,则无法控制;
  • 在 ES6 模块中,常将默认导出对象配合 Object.freeze() 使用,以确保 API 稳定,避免意外修改。

这些细节虽不复杂,但容易忽略。稍加留意即可避免隐藏的错误。