JavaScript中Object.defineProperty的使用详解
在JavaScript开发中,我们经常需要对对象的属性进行更精细的控制,比如让属性不可被修改、不可被枚举,或者在属性被访问、修改时执行自定义逻辑。这时候就可以用到Object.defineProperty方法,它是JavaScript中用于定义或修改对象属性的核心API,允许我们设置属性的各种特性。
Object.defineProperty的基本语法
该方法接收三个参数,语法结构如下:
Object.defineProperty(obj, prop, descriptor)
三个参数的含义分别是:
- obj:要在其上定义属性的目标对象
- prop:要定义或修改的属性的名称,类型为字符串或Symbol
- descriptor:属性描述符对象,用于定义该属性的具体特性
方法执行后会返回修改后的目标对象obj。
属性描述符的分类
属性描述符分为两类:数据描述符和存取描述符,同一时间一个属性只能使用其中一种类型的描述符,不能混合使用。
数据描述符
数据描述符用于定义普通的属性值,包含以下可选字段:
- value:属性的值,默认为undefined
- writable:布尔值,表示属性的值是否可以被修改,默认为false
- enumerable:布尔值,表示属性是否可以通过for...in循环或Object.keys()枚举,默认为false
- configurable:布尔值,表示属性是否可以被删除,以及除value和writable外的其他特性是否可以被修改,默认为false
下面是一个使用数据描述符定义属性的示例:
// 定义一个空对象
const user = {};
// 使用Object.defineProperty给user对象添加name属性
Object.defineProperty(user, 'name', {
value: '张三', // 属性值为张三
writable: false, // 属性不可修改
enumerable: true, // 属性可枚举
configurable: false // 属性不可删除,特性不可修改
});
console.log(user.name); // 输出:张三
// 尝试修改name属性
user.name = '李四';
console.log(user.name); // 输出:张三,修改无效
// 尝试删除name属性
delete user.name;
console.log(user.name); // 输出:张三,删除无效
// 遍历对象的可枚举属性
console.log(Object.keys(user)); // 输出:['name']存取描述符
存取描述符通过getter和setter函数来获取或设置属性的值,包含以下可选字段:
- get:一个函数,当访问该属性时会被调用,返回值作为属性的值,默认为undefined
- set:一个函数,当修改该属性时会被调用,接收修改后的值作为参数,默认为undefined
- enumerable:和数数据描述符的含义一致,控制属性是否可枚举
- configurable:和数据描述符的含义一致,控制属性是否可配置
注意:存取描述符不能和数据描述符的value、writable字段同时存在,否则会报错。
下面是一个使用存取描述符实现属性代理的示例:
const person = {
_age: 18 // 下划线开头的属性通常表示私有属性,不直接访问
};
// 使用存取描述符定义age属性
Object.defineProperty(person, 'age', {
enumerable: true,
configurable: true,
get() {
console.log('正在访问age属性');
return this._age;
},
set(newVal) {
console.log('正在修改age属性,新值为:' + newVal);
if (typeof newVal !== 'number' || newVal < 0 || newVal > 150) {
throw new Error('年龄必须是0到150之间的数字');
}
this._age = newVal;
}
});
console.log(person.age); // 输出:正在访问age属性 18
person.age = 20; // 输出:正在修改age属性,新值为:20
console.log(person.age); // 输出:正在访问age属性 20
// 尝试设置非法值
try {
person.age = '二十';
} catch (e) {
console.log(e.message); // 输出:年龄必须是0到150之间的数字
}批量定义属性:Object.defineProperties
如果需要同时给对象定义多个属性,可以使用Object.defineProperties方法,它接收一个对象和一个属性描述符映射对象作为参数:
const student = {};
Object.defineProperties(student, {
name: {
value: '小明',
enumerable: true
},
score: {
value: 90,
enumerable: true,
writable: true
},
grade: {
get() {
if (this.score >= 90) return 'A';
if (this.score >= 80) return 'B';
if (this.score >= 60) return 'C';
return 'D';
},
enumerable: true
}
});
console.log(student.name); // 输出:小明
console.log(student.score); // 输出:90
console.log(student.grade); // 输出:A
student.score = 75;
console.log(student.grade); // 输出:C实际应用场景
Object.defineProperty在前端开发中有很多常见的应用场景:
- 实现Vue 2.x的响应式系统,通过劫持对象的getter和setter来追踪依赖、触发视图更新
- 定义对象的只读属性,防止重要属性被意外修改
- 实现属性的校验逻辑,在设置属性值时进行合法性检查
- 隐藏对象的某些内部属性,避免被外部直接访问或枚举
需要注意的是,Object.defineProperty无法监听对象新增属性和数组索引变化,这也是Vue 2.x中需要使用$set方法添加新属性的原因。如果需要更全面的对象变化监听,在现代JavaScript环境中可以考虑使用Proxy API,它是对Object.defineProperty的能力补充和升级。
Object.defineProperty属性描述符数据描述符存取描述符JavaScript 本作品最后修改时间:2026-05-22 14:06:39