JS中Object.defineProperty 详解

574 阅读2分钟

Object.defineProperty 详解

Object.defineProperty() 是 JavaScript 中一个强大的方法,用于直接在对象上定义新属性或修改现有属性,并返回该对象。它提供了对属性行为的精细控制。

基本语法

Object.defineProperty(obj, prop, descriptor)
  • obj:要在其上定义属性的对象
  • prop:要定义或修改的属性的名称
  • descriptor:将被定义或修改的属性描述符

属性描述符

属性描述符有两种主要类型:​数据描述符存取描述符

数据描述符

数据描述符是一个具有以下可选键的对象:

  • value:属性的值,默认为 undefined
  • writable:是否可写,true 表示可修改,默认为 false
  • enumerable:是否可枚举,true 表示会出现在对象的枚举属性中,默认为 false
  • configurable:是否可配置,true 表示该属性的类型可以改变,且属性可以从对象中删除,默认为 false

存取描述符

存取描述符是一个具有以下可选键的对象:

  • get:作为该属性的 getter 函数,默认为 undefined
  • set:作为该属性的 setter 函数,默认为 undefined
  • enumerable:同数据描述符
  • configurable:同数据描述符

注意:描述符不能同时是数据描述符和存取描述符(即不能同时有 value/writableget/set)。

示例

1. 基本使用

const obj = {};

// 添加数据属性
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: true,
  enumerable: true,
  configurable: true
});

console.log(obj.name); // "John"

2. 不可写属性

const obj = {};

Object.defineProperty(obj, 'readOnly', {
  value: 42,
  writable: false
});

obj.readOnly = 100; // 静默失败,严格模式下会报错
console.log(obj.readOnly); // 42

3. 不可枚举属性

const obj = {};

Object.defineProperty(obj, 'hidden', {
  value: 'secret',
  enumerable: false
});

console.log(obj.hidden); // "secret"
console.log(Object.keys(obj)); // []

4. 使用 getter 和 setter

const obj = {};
let internalValue = '';

Object.defineProperty(obj, 'greeting', {
  get: function() {
    return internalValue;
  },
  set: function(value) {
    internalValue = 'Hello, ' + value;
  },
  enumerable: true,
  configurable: true
});

obj.greeting = 'World';
console.log(obj.greeting); // "Hello, World"

5. 不可配置属性

const obj = {};

Object.defineProperty(obj, 'fixed', {
  value: 'cannot change',
  configurable: false
});

// 尝试删除或修改属性描述符会失败
delete obj.fixed; // false
console.log(obj.fixed); // "cannot change"

// 严格模式下会抛出错误
Object.defineProperty(obj, 'fixed', { configurable: true }); // 抛出 TypeError

注意事项

  1. 默认值​:如果不显式指定,writableenumerableconfigurable 的默认值都是 false
  2. 严格模式​:在非严格模式下,违反属性描述符限制的操作会静默失败;在严格模式下会抛出错误。
  3. 继承​:通过 Object.defineProperty() 定义的属性默认是不可枚举的,因此不会出现在 for...in 循环中(除非显式设置 enumerable: true)。
  4. 性能​:与普通属性相比,访问器属性(getter/setter)可能会有轻微的性能开销。

实际应用

Object.defineProperty() 常用于:

  • 创建不可变属性
  • 实现数据绑定和观察(如 Vue 2.x 的响应式系统)
  • 定义隐藏属性(不可枚举)
  • 实现高级属性行为(如计算属性、验证等)

浏览器兼容性

Object.defineProperty() 在 IE9+ 和所有现代浏览器中都支持,但在 IE8 中仅能用于 DOM 对象。