Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript对象属性教程,详解属性描述符value、writable、enumerable、configurable,数据属性vs访问器属性,Object.defineProperty()使用。包含完整代码示例,适合前端开发者深度掌握对象属性机制。
核心关键词:JavaScript对象属性2024、属性描述符、Object.defineProperty、数据属性、访问器属性、writable enumerable configurable
长尾关键词:JavaScript属性描述符怎么用、Object.defineProperty用法、对象属性特性、数据属性和访问器属性区别、JavaScript属性配置
通过本节JavaScript对象属性详解,你将系统性掌握:
属性描述符是JavaScript对象系统的核心机制,它决定了属性的行为特征。理解属性描述符,就理解了JavaScript对象的内部工作原理,这是实现数据绑定、响应式系统、API封装的基础。
💡 核心理解:属性描述符是JavaScript对象系统的"配置文件",掌握它就掌握了对象的精髓
属性描述符是一个对象,用于描述属性的特性。JavaScript中有两种属性描述符:数据描述符和访问器描述符。
// 🎉 获取属性描述符
const person = {
name: 'Alice',
age: 25
};
// 查看默认属性描述符
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// {
// value: 'Alice',
// writable: true,
// enumerable: true,
// configurable: true
// }
// 查看所有属性描述符
console.log(Object.getOwnPropertyDescriptors(person));数据属性包含四个特性:value、writable、enumerable、configurable。
// 🎉 数据属性特性详解
const obj = {};
// 定义具有特定特性的属性
Object.defineProperty(obj, 'name', {
value: 'Alice', // 属性值
writable: true, // 是否可写
enumerable: true, // 是否可枚举
configurable: true // 是否可配置
});
// value特性:属性的值
console.log(obj.name); // 'Alice'
// writable特性:控制属性是否可以被赋值
Object.defineProperty(obj, 'id', {
value: 12345,
writable: false, // 只读属性
enumerable: true,
configurable: true
});
obj.id = 67890; // 静默失败(严格模式下报错)
console.log(obj.id); // 12345(值未改变)
// enumerable特性:控制属性是否出现在枚举中
Object.defineProperty(obj, 'secret', {
value: 'hidden',
writable: true,
enumerable: false, // 不可枚举
configurable: true
});
console.log(Object.keys(obj)); // ['name'] (secret不出现)
console.log(obj.secret); // 'hidden' (仍可直接访问)
// configurable特性:控制属性是否可以被删除或修改特性
Object.defineProperty(obj, 'constant', {
value: 'unchangeable',
writable: false,
enumerable: true,
configurable: false // 不可配置
});
delete obj.constant; // 静默失败
console.log(obj.constant); // 'unchangeable'
// 尝试修改不可配置属性的特性会报错
try {
Object.defineProperty(obj, 'constant', {
writable: true
});
} catch (error) {
console.log('Cannot reconfigure property'); // TypeError
}// 🎉 使用Object.defineProperties批量定义
const user = {};
Object.defineProperties(user, {
firstName: {
value: 'John',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
value: 'Doe',
writable: true,
enumerable: true,
configurable: true
},
id: {
value: Math.random().toString(36).substr(2, 9),
writable: false, // 只读ID
enumerable: false, // 隐藏ID
configurable: false // 不可删除
},
createdAt: {
value: new Date(),
writable: false,
enumerable: true,
configurable: false
}
});
console.log(user.firstName); // 'John'
console.log(Object.keys(user)); // ['firstName', 'lastName', 'createdAt']数据属性特性总结:
访问器属性不包含数据值,而是包含getter和setter函数。
// 🎉 访问器属性基础用法
const person = {
firstName: 'John',
lastName: 'Doe'
};
// 定义访问器属性
Object.defineProperty(person, 'fullName', {
get: function() {
return `${this.firstName} ${this.lastName}`;
},
set: function(value) {
const parts = value.split(' ');
this.firstName = parts[0] || '';
this.lastName = parts[1] || '';
},
enumerable: true,
configurable: true
});
console.log(person.fullName); // 'John Doe'
person.fullName = 'Jane Smith';
console.log(person.firstName); // 'Jane'
console.log(person.lastName); // 'Smith'// 🎉 复杂访问器属性示例
function createTemperature(celsius = 0) {
// 私有存储
let _celsius = celsius;
const temp = {};
// 摄氏度访问器
Object.defineProperty(temp, 'celsius', {
get() {
return _celsius;
},
set(value) {
if (typeof value !== 'number') {
throw new TypeError('Temperature must be a number');
}
if (value < -273.15) {
throw new RangeError('Temperature cannot be below absolute zero');
}
_celsius = value;
},
enumerable: true,
configurable: false
});
// 华氏度访问器
Object.defineProperty(temp, 'fahrenheit', {
get() {
return (_celsius * 9/5) + 32;
},
set(value) {
this.celsius = (value - 32) * 5/9;
},
enumerable: true,
configurable: false
});
// 开尔文访问器
Object.defineProperty(temp, 'kelvin', {
get() {
return _celsius + 273.15;
},
set(value) {
this.celsius = value - 273.15;
},
enumerable: true,
configurable: false
});
// 只读的描述属性
Object.defineProperty(temp, 'description', {
get() {
if (_celsius < 0) return 'Freezing';
if (_celsius < 10) return 'Cold';
if (_celsius < 25) return 'Cool';
if (_celsius < 35) return 'Warm';
return 'Hot';
},
enumerable: true,
configurable: false
});
return temp;
}
const temp = createTemperature(20);
console.log(temp.celsius); // 20
console.log(temp.fahrenheit); // 68
console.log(temp.kelvin); // 293.15
console.log(temp.description); // 'Cool'
temp.fahrenheit = 86;
console.log(temp.celsius); // 30
console.log(temp.description); // 'Warm'// 🎉 简单的响应式系统实现
function createReactive(data) {
const listeners = {};
const reactive = {};
// 为每个属性创建访问器
Object.keys(data).forEach(key => {
let value = data[key];
listeners[key] = [];
Object.defineProperty(reactive, key, {
get() {
return value;
},
set(newValue) {
if (value !== newValue) {
const oldValue = value;
value = newValue;
// 通知所有监听器
listeners[key].forEach(listener => {
listener(newValue, oldValue, key);
});
}
},
enumerable: true,
configurable: true
});
});
// 添加监听器方法
reactive.$watch = function(key, callback) {
if (listeners[key]) {
listeners[key].push(callback);
}
};
// 移除监听器方法
reactive.$unwatch = function(key, callback) {
if (listeners[key]) {
const index = listeners[key].indexOf(callback);
if (index > -1) {
listeners[key].splice(index, 1);
}
}
};
return reactive;
}
// 使用响应式系统
const state = createReactive({
name: 'Alice',
age: 25,
email: 'alice@example.com'
});
// 添加监听器
state.$watch('name', (newValue, oldValue) => {
console.log(`Name changed from ${oldValue} to ${newValue}`);
});
state.$watch('age', (newValue, oldValue) => {
console.log(`Age changed from ${oldValue} to ${newValue}`);
});
// 触发变化
state.name = 'Bob'; // "Name changed from Alice to Bob"
state.age = 30; // "Age changed from 25 to 30"访问器属性特性:
// 🎉 属性验证和拦截
function createValidatedUser(initialData = {}) {
const user = {};
const validators = {
email: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
},
age: (value) => {
return typeof value === 'number' && value >= 0 && value <= 150;
},
name: (value) => {
return typeof value === 'string' && value.length >= 2;
}
};
// 为每个验证字段创建属性
Object.keys(validators).forEach(key => {
let value = initialData[key];
Object.defineProperty(user, key, {
get() {
return value;
},
set(newValue) {
if (validators[key](newValue)) {
value = newValue;
} else {
throw new Error(`Invalid ${key}: ${newValue}`);
}
},
enumerable: true,
configurable: false
});
});
// 只读属性
Object.defineProperty(user, 'id', {
value: Math.random().toString(36).substr(2, 9),
writable: false,
enumerable: true,
configurable: false
});
Object.defineProperty(user, 'createdAt', {
value: new Date(),
writable: false,
enumerable: true,
configurable: false
});
return user;
}
const user = createValidatedUser({
name: 'Alice',
email: 'alice@example.com',
age: 25
});
console.log(user.name); // 'Alice'
console.log(user.id); // 随机生成的ID
try {
user.email = 'invalid-email';
} catch (error) {
console.log(error.message); // "Invalid email: invalid-email"
}
try {
user.age = -5;
} catch (error) {
console.log(error.message); // "Invalid age: -5"
}// 🎉 属性代理和计算属性
function createSmartObject(data) {
const computed = {};
const cache = new Map();
// 基础数据属性
Object.keys(data).forEach(key => {
Object.defineProperty(computed, key, {
value: data[key],
writable: true,
enumerable: true,
configurable: true
});
});
// 添加计算属性的方法
computed.$computed = function(name, computeFn, dependencies = []) {
Object.defineProperty(this, name, {
get() {
// 检查缓存
const cacheKey = dependencies.map(dep => this[dep]).join('|');
if (cache.has(name) && cache.get(name).key === cacheKey) {
return cache.get(name).value;
}
// 计算新值
const value = computeFn.call(this);
cache.set(name, { key: cacheKey, value });
return value;
},
enumerable: true,
configurable: true
});
// 监听依赖变化,清除缓存
dependencies.forEach(dep => {
const descriptor = Object.getOwnPropertyDescriptor(this, dep);
if (descriptor && descriptor.configurable) {
let value = this[dep];
Object.defineProperty(this, dep, {
get() {
return value;
},
set(newValue) {
value = newValue;
cache.delete(name); // 清除计算属性缓存
},
enumerable: true,
configurable: true
});
}
});
};
return computed;
}
const person = createSmartObject({
firstName: 'John',
lastName: 'Doe',
birthYear: 1990
});
// 添加计算属性
person.$computed('fullName', function() {
console.log('Computing fullName...'); // 用于观察计算次数
return `${this.firstName} ${this.lastName}`;
}, ['firstName', 'lastName']);
person.$computed('age', function() {
console.log('Computing age...');
return new Date().getFullYear() - this.birthYear;
}, ['birthYear']);
console.log(person.fullName); // "Computing fullName..." -> "John Doe"
console.log(person.fullName); // "John Doe" (从缓存获取)
console.log(person.age); // "Computing age..." -> 当前年龄
person.firstName = 'Jane';
console.log(person.fullName); // "Computing fullName..." -> "Jane Doe"// 🎉 创建不可变对象
function createImmutable(obj) {
const immutable = {};
Object.keys(obj).forEach(key => {
const value = obj[key];
if (typeof value === 'object' && value !== null) {
// 递归处理嵌套对象
Object.defineProperty(immutable, key, {
value: createImmutable(value),
writable: false,
enumerable: true,
configurable: false
});
} else {
Object.defineProperty(immutable, key, {
value: value,
writable: false,
enumerable: true,
configurable: false
});
}
});
// 防止添加新属性
Object.preventExtensions(immutable);
return immutable;
}
const mutableData = {
name: 'Alice',
age: 25,
address: {
city: 'New York',
country: 'USA'
}
};
const immutableData = createImmutable(mutableData);
console.log(immutableData.name); // 'Alice'
// 以下操作都会失败
immutableData.name = 'Bob'; // 静默失败
immutableData.newProp = 'value'; // 静默失败
delete immutableData.age; // 静默失败
immutableData.address.city = 'LA'; // 静默失败
console.log(immutableData.name); // 'Alice' (未改变)通过本节JavaScript对象属性详解的学习,你已经掌握:
A: 数据属性用于存储简单值,访问器属性用于需要计算、验证或副作用的场景。如果需要在获取或设置值时执行逻辑,使用访问器属性。
A: configurable为false后,不能删除属性,不能修改configurable和enumerable。但如果writable为true,仍可以修改value和writable特性。
A: 使用Object.getOwnPropertyDescriptor()检查返回的描述符。数据属性有value和writable,访问器属性有get和set。
A: Proxy可以拦截更多操作(如属性添加、删除),性能更好,支持数组索引监听,而Object.defineProperty只能监听已存在的属性。
A: 使用Object.freeze()或将所有属性的writable和configurable设为false,并使用Object.preventExtensions()防止添加新属性。
"属性描述符是JavaScript对象的DNA,掌握它就掌握了对象的生命密码。用好属性特性,让你的代码更安全、更优雅!"