Property Descriptors
理解和使用 JavaScript 属性描述符
问题
JavaScript 中每个对象属性都有一个”属性描述符”,它控制着属性的行为。如何获取、定义和修改属性描述符?
解答
什么是属性描述符
属性描述符是一个对象,描述了属性的特性。分为两种类型:
- 数据描述符:有
value和writable - 访问器描述符:有
get和set
两者共有的属性:configurable 和 enumerable
获取属性描述符
const obj = { name: 'Alice' };
// 获取单个属性的描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor);
// {
// value: 'Alice',
// writable: true,
// enumerable: true,
// configurable: true
// }
// 获取所有属性的描述符
const allDescriptors = Object.getOwnPropertyDescriptors(obj);
console.log(allDescriptors);
定义属性描述符
const obj = {};
// 定义数据描述符
Object.defineProperty(obj, 'name', {
value: 'Bob',
writable: false, // 不可修改
enumerable: true, // 可枚举
configurable: false // 不可删除或重新配置
});
obj.name = 'Alice'; // 静默失败,严格模式下报错
console.log(obj.name); // 'Bob'
// 定义访问器描述符
let _age = 18;
Object.defineProperty(obj, 'age', {
get() {
return _age;
},
set(val) {
if (val > 0) _age = val;
},
enumerable: true,
configurable: true
});
obj.age = 25;
console.log(obj.age); // 25
批量定义属性
const obj = {};
Object.defineProperties(obj, {
firstName: {
value: 'John',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
value: 'Doe',
writable: true,
enumerable: true,
configurable: true
},
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
},
enumerable: true,
configurable: true
}
});
console.log(obj.fullName); // 'John Doe'
描述符默认值
// 通过字面量创建的属性,默认都是 true
const obj1 = { a: 1 };
// 等同于
Object.defineProperty({}, 'a', {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
// 通过 defineProperty 创建的属性,默认都是 false
Object.defineProperty(obj1, 'b', { value: 2 });
// 等同于
Object.defineProperty(obj1, 'b', {
value: 2,
writable: false,
enumerable: false,
configurable: false
});
实际应用:实现简单的响应式
function reactive(obj) {
const result = {};
Object.keys(obj).forEach(key => {
let value = obj[key];
Object.defineProperty(result, key, {
get() {
console.log(`读取 ${key}: ${value}`);
return value;
},
set(newVal) {
console.log(`设置 ${key}: ${value} -> ${newVal}`);
value = newVal;
},
enumerable: true,
configurable: true
});
});
return result;
}
const state = reactive({ count: 0 });
state.count; // 读取 count: 0
state.count = 1; // 设置 count: 0 -> 1
关键点
- 数据描述符有
value、writable;访问器描述符有get、set,两者互斥 enumerable控制属性是否出现在for...in和Object.keys()中configurable: false后属性不可删除,且不能再修改描述符(writable从 true 改 false 除外)- 字面量创建的属性默认全为 true,
defineProperty创建的默认全为 false - Vue 2 的响应式原理就是基于
Object.defineProperty实现的
目录