属性描述符(Property Descriptor)定义了对象属性的特性,包括值、可写性、可枚举性、可配置性等。
每个对象属性都有描述符,可以通过 Object.getOwnPropertyDescriptor() 获取:
const obj = {
name: 'Alice'
};
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor);
// {
// value: 'Alice',
// writable: true,
// enumerable: true,
// configurable: true
// }
包含以下属性:
const obj = {};
// 使用 Object.defineProperty 定义属性
Object.defineProperty(obj, 'name', {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
});
// 只读属性
Object.defineProperty(obj, 'readonly', {
value: 'cannot change',
writable: false
});
obj.readonly = 'try to change';
console.log(obj.readonly); // 'cannot change' (未改变)
// 不可枚举属性
Object.defineProperty(obj, 'hidden', {
value: 'hidden',
enumerable: false
});
console.log(Object.keys(obj)); // ['name'] (不包含 hidden)
console.log(obj.hidden); // 'hidden' (仍可访问)
包含以下属性:
const obj = {
_name: 'Alice'
};
Object.defineProperty(obj, 'name', {
get: function() {
return this._name;
},
set: function(value) {
this._name = value.toUpperCase();
},
enumerable: true,
configurable: true
});
console.log(obj.name); // 'Alice'
obj.name = 'bob';
console.log(obj.name); // 'BOB'
定义单个属性的描述符:
const obj = {};
// 定义数据属性
Object.defineProperty(obj, 'prop1', {
value: 42,
writable: false,
enumerable: true,
configurable: true
});
// 定义访问器属性
let _value = 0;
Object.defineProperty(obj, 'prop2', {
get: function() {
return _value;
},
set: function(newValue) {
_value = newValue * 2;
},
enumerable: true,
configurable: true
});
obj.prop2 = 5;
console.log(obj.prop2); // 10
同时定义多个属性:
const obj = {};
Object.defineProperties(obj, {
name: {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 25,
writable: false,
enumerable: true,
configurable: false
},
_email: {
value: '',
writable: true,
enumerable: false,
configurable: true
},
email: {
get: function() {
return this._email;
},
set: function(value) {
this._email = value.toLowerCase();
},
enumerable: true,
configurable: true
}
});
obj.email = '[email protected]';
console.log(obj.email); // '[email protected]'
const obj = {};
Object.defineProperty(obj, 'readonly', {
value: 'initial',
writable: false
});
obj.readonly = 'changed';
console.log(obj.readonly); // 'initial'
// 严格模式下会抛出错误
'use strict';
obj.readonly = 'changed'; // TypeError
const obj = {
visible: 'visible',
hidden: 'hidden'
};
Object.defineProperty(obj, 'hidden', {
enumerable: false
});
console.log(Object.keys(obj)); // ['visible']
console.log(Object.values(obj)); // ['visible']
for (let key in obj) {
console.log(key); // 'visible'
}
console.log(obj.propertyIsEnumerable('hidden')); // false
const obj = {};
Object.defineProperty(obj, 'prop', {
value: 'value',
configurable: false
});
// 不能删除
delete obj.prop;
console.log(obj.prop); // 'value'
// 不能修改描述符
Object.defineProperty(obj, 'prop', {
writable: false
}); // TypeError
// 但可以将 writable 从 true 改为 false
const obj2 = {};
Object.defineProperty(obj2, 'prop', {
value: 'value',
writable: true,
configurable: false
});
Object.defineProperty(obj2, 'prop', {
writable: false
}); // 成功
function createPerson(name) {
const _name = name;
const person = {};
Object.defineProperty(person, 'name', {
get: function() {
return _name;
},
enumerable: true,
configurable: false
});
return person;
}
const person = createPerson('Alice');
console.log(person.name); // 'Alice'
person.name = 'Bob'; // 无效(没有 setter)
console.log(person.name); // 'Alice'
const user = {
_age: 0
};
Object.defineProperty(user, 'age', {
get: function() {
return this._age;
},
set: function(value) {
if (typeof value !== 'number' || value < 0 || value > 150) {
throw new Error('Invalid age');
}
this._age = value;
},
enumerable: true,
configurable: true
});
user.age = 25; // 成功
user.age = -1; // Error: Invalid age
const rectangle = {
width: 10,
height: 5
};
Object.defineProperty(rectangle, 'area', {
get: function() {
return this.width * this.height;
},
enumerable: true,
configurable: true
});
console.log(rectangle.area); // 50
rectangle.width = 20;
console.log(rectangle.area); // 100
function observe(obj, prop, callback) {
let value = obj[prop];
Object.defineProperty(obj, prop, {
get: function() {
return value;
},
set: function(newValue) {
const oldValue = value;
value = newValue;
callback(newValue, oldValue);
},
enumerable: true,
configurable: true
});
}
const data = { count: 0 };
observe(data, 'count', (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`);
});
data.count = 1; // count changed from 0 to 1
data.count = 5; // count changed from 1 to 5
获取对象所有属性的描述符:
const obj = {
name: 'Alice'
};
Object.defineProperty(obj, 'age', {
value: 25,
writable: false,
enumerable: true,
configurable: true
});
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// {
// name: { value: 'Alice', writable: true, enumerable: true, configurable: true },
// age: { value: 25, writable: false, enumerable: true, configurable: true }
// }
const proto = {
greet: function() {
return `Hello, ${this.name}`;
}
};
const obj = Object.create(proto, {
name: {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 25,
writable: false,
enumerable: true,
configurable: false
}
});
console.log(obj.greet()); // 'Hello, Alice'