对象作为 JavaScript 中存储键值对数据的核心结构,其属性操作是基础且关键的能力。我们以用户信息对象为案例,系统讲解属性的定义方式、访问方法及修改规则。
对象字面量定义是最常用的创建方式,通过键值对直接声明对象。ES6 引入了属性初始化简写、方法简写和可计算属性名等语法糖,大幅简化了对象定义过程:
// ES5 传统写法
let es5User = {
name: 'Alice',
age: 28,
greet: function() {
console.log(`Hello, ${this.name}`);
}
};
// ES6 简写语法
let name = 'Bob';
let age = 30;
let key = 'role';
let es6User = {
name, // 属性初始化简写(等同于 name: name)
age,
greet() { // 方法简写(省略 function 关键字)
console.log(`Hello, ${this.name}`);
},
[key]: 'user' // 可计算属性名(方括号内为表达式)
};
属性访问支持两种方式:点表示法(obj.property)和方括号表示法(obj['property']),后者适用于属性名包含特殊字符或动态生成的场景。属性修改通过直接赋值实现,如 es6User.age = 31 即可更新年龄属性。
若需限制属性被修改,可使用 Object.defineProperty 方法精细控制属性特性。该方法允许定义属性的 writable(可写性)、enumerable(可枚举性)、configurable(可配置性)等高级特性。以下示例为用户对象添加只读 id 属性:
let user = {
name: 'Charlie',
age: 25
};
// 定义只读 id 属性
Object.defineProperty(user, 'id', {
value: 'u12345', // 属性值
writable: false, // 设为 false 后属性不可修改
enumerable: true, // 设为 true 可被枚举(如 for...in 遍历)
configurable: false // 设为 false 后不可删除或重新定义特性
});
console.log(user.id); // 输出:u12345
user.id = 'u67890'; // 尝试修改只读属性,严格模式下会报错
console.log(user.id); // 输出:u12345(值未改变)
注意:Object.defineProperty 定义的属性默认特性为 writable: false、enumerable: false、configurable: false,而直接赋值定义的属性默认特性为 true。如需属性可被遍历或修改,需显式设置对应特性。
遍历对象属性是日常开发的常见需求,for...in 循环与 Object.keys() 方法是两种主要方式,但存在显著差异:
| 特性 | for...in 循环 |
Object.keys(obj) |
|---|---|---|
| 遍历范围 | 自身及继承的可枚举属性 | 仅自身可枚举属性 |
| 返回值 | 无返回值,需手动收集属性 | 返回属性名组成的数组 |
| 原型链污染风险 | 可能遍历到原型链上的意外属性 | 仅遍历对象自身属性,无原型链风险 |
| 典型用途 | 需遍历所有可枚举属性(含继承)时使用 | 仅需处理对象自身属性时使用 |
示例对比:
// 给 Object 原型添加自定义属性(模拟原型链污染)
Object.prototype.customProp = 'inherited';
let user = {
name: 'Diana',
age: 28
};
// 使用 for...in 遍历(会包含继承的 customProp)
console.log('for...in 遍历结果:');
for (let key in user) {
console.log(key); // 输出:name, age, customProp
}
// 使用 Object.keys 遍历(仅包含自身属性)
console.log('Object.keys 遍历结果:', Object.keys(user)); // 输出:[ 'name', 'age' ]
为避免 for...in 遍历到继承属性,通常需配合 hasOwnProperty 方法过滤:
for (let key in user) {
if (user.hasOwnProperty(key)) { // 仅处理自身属性
console.log(key); // 输出:name, age
}
}
ES2024 新增的 Object.groupBy 方法为数组分组提供了极简方案,其接收可迭代对象和分组回调函数,直接返回按指定键分组的对象。相比传统 reduce 方法,代码量减少 60% 以上,可读性显著提升。
场景:将用户数组按 role 属性分组(如 'admin'、'user'、'guest')。
传统 reduce 实现(需手动初始化累加器、处理属性不存在情况):
const users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'user' },
{ name: 'Diana', role: 'guest' }
];
const groupedByRole = users.reduce((acc, user) => {
// 若分组键不存在,初始化为空数组
acc[user.role] = acc[user.role] || [];
// 将用户添加到对应分组
acc[user.role].push(user);
return acc;
}, {}); // 初始累加器为空对象
Object.groupBy 实现(一行代码完成分组):
const groupedByRole = Object.groupBy(users, user => user.role);
两种方式均返回相同结果:
{
admin: [{ name: 'Alice', role: 'admin' }],
user: [{ name: 'Bob', role: 'user' }, { name: 'Charlie', role: 'user' }],
guest: [{ name: 'Diana', role: 'guest' }]
}
{
"legend": {
"bottom": 0,
"data": [
"用户数量"
],
"textStyle": {
"fontSize": 16
}
},
"series": [
{
"data": [
1,
2,
1
],
"name": "用户数量",
"type": "bar"
}
],
"title": {
"left": "center",
"text": "用户角色分布对比",
"textStyle": {
"fontSize": 20
}
},
"tooltip": {
"trigger": "item"
},
"xAxis": {
"data": [
"admin",
"user",
"guest"
],
"type": "category"
},
"yAxis": {
"name": "用户数量",
"type": "value"
}
}
Object.groupBy 的回调函数需返回字符串类型的分组键,其内部自动处理分组初始化逻辑,大幅降低了手动实现的出错风险。该方法已被主流浏览器支持,是处理分组场景的首选方案。
对象基础操作是 JavaScript 编程的核心能力,需重点掌握:
Object.defineProperty 精细管理属性可写性;Object.keys 避免原型链属性干扰,必要时配合 hasOwnProperty;Object.groupBy 替代传统 reduce,简化分组逻辑。这些技能将为后续复杂对象操作(如原型链、类与继承)奠定坚实基础。