JavaScript属性描述符详解
1. 什么是 Property Descriptor?
每个对象属性(包括方法)都有一个对应的属性描述符,它是一个普通对象,包含以下属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value |
any |
undefined |
属性的值(如果是方法,这里存储函数本身)。 |
writable |
boolean |
false |
是否可修改属性的值。 |
enumerable |
boolean |
false |
是否在 for...in 或 Object.keys() 中显示。 |
configurable |
boolean |
false |
是否可删除属性或修改描述符。 |
get |
Function |
undefined |
属性的 getter 函数(与 value 互斥)。 |
set |
Function |
undefined |
属性的 setter 函数(与 value 互斥)。 |
注意:get/set 和 value/writable 不能同时存在。
2. 如何获取和修改 Property Descriptor?
(1) 获取属性描述符
使用 Object.getOwnPropertyDescriptor():
const obj = { name: "Alice", greet() { console.log("Hello!"); } };
// 获取普通属性的描述符
const nameDesc = Object.getOwnPropertyDescriptor(obj, "name");
console.log(nameDesc);
// { value: "Alice", writable: true, enumerable: true, configurable: true }
// 获取方法的描述符
const greetDesc = Object.getOwnPropertyDescriptor(obj, "greet");
console.log(greetDesc);
// { value: [Function: greet], writable: true, enumerable: true, configurable: true }
(2) 定义或修改属性描述符
使用 Object.defineProperty():
const obj = {}; // 定义一个不可写、不可枚举的属性 Object.defineProperty(obj, "id", { value: 123, writable: false, // 不可修改 enumerable: false, // 不会出现在 for...in 中 configurable: false // 不可删除或重新配置 }); console.log(obj.id); // 123 obj.id = 456; // 静默失败(严格模式报错) console.log(obj.id); // 123 // 尝试删除(失败) delete obj.id; // 静默失败 console.log(obj.id); // 123
3. 方法描述符的应用场景
(1) 防止方法被修改
class Calculator { add(a, b) { return a + b; } } // 锁定 add 方法,防止被重写 Object.defineProperty(Calculator.prototype, "add", { writable: false, configurable: false }); const calc = new Calculator(); calc.add = () => "Hacked!"; // 静默失败(严格模式报错) console.log(calc.add(2, 3)); // 5
(2) 控制方法的可见性
const user = { _password: "secret", // 内部属性 getPassword() { return this._password; } }; // 让 getPassword 不可枚举(不出现在 JSON.stringify 或 for...in 中) Object.defineProperty(user, "getPassword", { enumerable: false }); console.log(Object.keys(user)); // ["_password"] console.log(JSON.stringify(user)); // {"_password":"secret"}
(3) 实现计算属性(Getter/Setter)
const circle = { _radius: 5, get diameter() { return this._radius * 2; }, set diameter(value) { this._radius = value / 2; } }; // 等价于: Object.defineProperty(circle, "diameter", { get: function() { return this._radius * 2; }, set: function(value) { this._radius = value / 2; }, enumerable: true, configurable: true }); console.log(circle.diameter); // 10 circle.diameter = 20; console.log(circle._radius); // 10
4. 方法描述符与装饰器的关系
在 TypeScript/JavaScript 的方法装饰器中,装饰器函数接收的 descriptor 参数就是方法的属性描述符。通过修改它,可以拦截或增强方法行为:
function logMethod(target: Object, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; // 获取原方法 descriptor.value = function (...args: any[]) { console.log(`Calling ${propertyKey} with args:`, args); const result = originalMethod.apply(this, args); console.log(`Result:`, result); return result; }; return descriptor; // 必须返回修改后的描述符 } class Calculator { @logMethod add(a: number, b: number) { return a + b; } } const calc = new Calculator(); calc.add(2, 3); // 输出: // Calling add with args: [2, 3] // Result: 5
5. 注意事项
-
默认描述符值
- 直接赋值(如
obj.x = 1)创建的属性是writable: true、enumerable: true、configurable: true。 - 通过
Object.defineProperty()定义的属性默认是false。
- 直接赋值(如
-
严格模式
在非严格模式下,修改不可写属性或删除不可配置属性会静默失败;在严格模式下会抛出错误。 -
不可变对象
结合Object.freeze()或Object.seal()可以进一步限制对象修改。
总结
- 属性描述符 控制属性的
value、writable、enumerable、configurable等行为。 - 方法描述符 是属性描述符的一种,用于方法(函数)的元编程。
- 装饰器 通过修改
descriptor来增强方法(如日志、权限校验)。 - 应用场景:防止方法被重写、控制方法可见性、实现计算属性等。
通过理解 PropertyDescriptor,你可以更灵活地控制 JavaScript 对象的属性行为!

浙公网安备 33010602011771号