属性描述符
刀刀
4/7/2025
0 字
0 分钟
概念
创建一个对象 obj
,并为其添加一个属性 x
,值为 10。然后,使用 Object.getOwnPropertyDescriptor()
方法获取 x
属性的描述符。得到了以下的结果:
{
value: 10,
writable: true,
enumerable: true,
configurable: true
}
结果表明,这个对象的 x
属性值是 10,并且是可写的、可枚举的、可配置的。
属性描述符是可以设置的,通过 Object.defineProperty()
方法可以修改属性的特性。例如,将 x
属性值设置为 20,并且只读,不可枚举:
Object.defineProperty(obj, 'x', {
value: 20,
writable: false, // 只读
enumerable: false, // 不可枚举
});
设置为不可枚举之后,x
属性将不会出现在 for...in
循环中,也不会出现在 Object.keys()
的结果中,打印 obj
也不会显示 x
属性。
但是没设置 configurable
,x
属性的描述符还可以修改,此时如果修改 x
的是否可写、是否可遍历属性描述符是允许的。
Object.defineProperty(obj, 'x', {
writable: true, // 可写
enumerable: true, // 可枚举
});
obj.x = 20
for (let key in obj) {
console.log(key); // 输出:x
console.log(obj[key]); // 输出:20
}
如果设置了 configurable: false
,则不能修改 x
属性的描述符,也不能删除 x
属性。
Object.defineProperty(obj, 'x', {
configurable: false, // 不可配置
});
// 此时再修改 x 属性的描述符会报错
Object.defineProperty(obj, 'x', {
writable: true,
});
// TypeError: Cannot redefine property: x
项目实战
声明一个类,构造器内声明一个数据对象 data
,这个 data
希望是内部修改的,外部只能使用,不能修改它的值。
class UIGoods {
constructor(data) {
Object.defineProperty(this, 'data', {
value: data,
writable: false,
});
}
}
const goods = new UIGoods({ name: '商品1', price: 100 });
goods.data = { name: '商品2', price: 200 };
此时虽然外部使用的人无法修改,但是没有任何报错提示。如果是多人大型项目开发,开发者发现修改了 data
属性,但是没有任何报错提示,这会导致混乱。因此有一个报错提示是很有必要的。
访问器
Object.defineProrperty()
方法可以设置访问器属性,访问器属性由一对 get
和 set
函数组成。读取对象的属性,实际上是从 get
函数上获取值,设置对象的属性,实际上是调用 set
函数。
Object.defineProperty(obj, 'x', {
get() {
console.log('get')
return 10;
},
set(value) {
console.log(value);
},
});
obj.x = obj.x + 10
console.log(obj.x);
此时输出的是 get
20 10。首先 obj.x
读取时调用 get
函数,返回 10。然后 obj.x + 10
执行时,触发 set
函数,最后 console.log
时再次触发 get
函数。只不过 set
函数内没有做赋值操作,且 get
函数内写死 return
10,因此最后返回结果还是 10。
解决
结合属性描述符和访问器属性,可以创建一个只读属性,并且在开发者修改属性时,抛出错误。
class UIGoods {
constructor(data) {
let _data = JSON.parse(JSON.stringify(data));
// 商品数据
Object.defineProperty(this, '_data', {
configurable: false,
get() {
return this.data;
},
set(value) {
throw new Error('不可修改 data');
},
});
// 选择了多少商品
let internalChooseValue = 0
Object.defineProperty(this, 'choose', {
configurable: false,
get() {
return internalChooseValue;
},
set(value) {
if (typeof value !== 'number') throw new Error('choose 必须是数字');
let temp = parseInt(value)
if (temp !== value) throw new Error('choose 必须是整数');
if (val < 0) throw new Error('choose 必须大于等于 0');
internalChooseValue = val
},
})
}
// 计算总价
get totalPrice() {
return this.data.price * this.choose
}
}
let good = new UIGoods({ name: '商品1', price: 100 })
good.data = { name: '商品2', price: 200 } // 报错
good.choose = -10 // 报错
good.choose = 10
console.log(good.totalPrice) // 1000
总结
属性描述符和访问器属性是 JavaScript 对象属性的重要特性,它们允许开发者控制属性的行为和访问方式。通过使用属性描述符可以定义属性的值、可写性、可枚举性和可配置性。而访问器属性则允许通过自定义的 get
和 set
函数来控制属性的读取和设置。