一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何用 Object.defineProperty 为对象属性设置只读或不可枚举权限

时间:2026-08-07 07:21:49 编辑:袖梨 来源:一聚教程网

如何用 Object.defineProperty 为对象属性设置只读或不可枚举权限并不只看表面做法,关键还要理解相关条件、限制和后续影响。

正确设置只读属性需同时设 writable: false 和 configurable: false;仅 writable: false 时仍可通过 defineProperty 改回可写;enumerable: false 控制遍历可见性,不影响直接访问;Vue 2 中 defineProperty 无法监听新增属性、数组索引赋值等,存在固有局限。

Object.defineProperty 设置只读属性的正确写法

直接设 writable: false 不够,必须同时确保 configurable: false,否则后续仍可通过 Object.defineProperty 重新打开写权限。只设 writable: false 后,属性值确实无法被赋值(严格模式下报 TypeError: Cannot assign to read only property),但只要 configurable: true,就能再次调用 Object.definePropertywritable 改回 true

实操建议:

  1. 需要真正只读,必须同时设 writable: falseconfigurable: false
  2. 若只希望“当前不可写但未来可配置”,才保留 configurable: true(极少用)
  3. 对已存在的普通属性补设只读时,原属性必须是 configurable: true 才能成功调用 Object.defineProperty
const obj = { a: 1 };Object.defineProperty(obj, 'a', {  writable: false,  configurable: false,  value: 1});obj.a = 2; // 无效(非严格模式)或报错(严格模式)Object.defineProperty(obj, 'a', { writable: true }); // TypeError: Cannot redefine property: a

让属性不可枚举的关键是 enumerable: false

enumerable: false 控制该属性是否出现在 for...inObject.keys()JSON.stringify() 等遍历操作中。它和 writableconfigurable 正交,可以单独设置,不影响读写行为。

常见误区:

  1. enumerable: false 不等于“隐藏”——仍可通过 obj.prop 直接访问,也能用 Object.getOwnPropertyDescriptor 查到
  2. for...in 会跳过 enumerable: false 属性,但会遍历原型链上 enumerable: true 的属性
  3. Object.assign({}, obj) 只拷贝自身且 enumerable: true 的属性,所以 enumerable: false 属性不会被复制
const obj = {};Object.defineProperty(obj, 'hidden', {  value: 'secret',  enumerable: false,  writable: true,  configurable: true});console.log(Object.keys(obj)); // []console.log(obj.hidden); // 'secret'

同时设置只读 + 不可枚举的典型场景

这类组合常用于定义内部状态字段、缓存键、或向库暴露“只读接口但不污染遍历结果”的属性。比如封装一个带缓存的计算属性:

  1. 缓存值本身不应被外部修改(writable: false
  2. 也不应出现在 Object.keys() 或序列化中(enumerable: false
  3. 通常还要禁用重配置(configurable: false),防止被意外覆盖

注意:如果属性初始值是对象或数组,writable: false 只冻结引用,不深冻结内容;需额外用 Object.freeze

const cache = {};Object.defineProperty(obj, '_cache', {  value: cache,  writable: false,  enumerable: false,  configurable: false});// 后续仍可修改 cache 内部:cache.x = 1;// 若需完全冻结,应写成 value: Object.freeze({})

Vue 2 响应式系统里为什么慎用 Object.defineProperty

Vue 2 用 Object.defineProperty 实现数据劫持,但它有硬性限制:无法检测对象新增/删除属性,也无法监听数组索引赋值(如 arr[0] = x)或 length 变更。这些限制不是写法问题,而是 API 本身能力边界。

实际开发中容易踩的坑:

  1. 给响应式对象动态加属性(如 vm.obj.newKey = val),不会触发视图更新
  2. Object.defineProperty 手动加的属性,若没走 Vue 的 set 方法,也不会进入依赖收集
  3. 数组方法如 push 被重写了,但直接写索引或 splice 某些用法仍可能漏掉响应

现代项目优先用 Proxy 替代,但若维护旧代码,就得接受这些约束,并始终用 Vue.setthis.$set 添加响应式属性。

热门栏目