Skip to content

对象属性

属性(Properties)是 JavaScript 对象的基本组成部分。它们将一个名称(一个键,可以是字符串或 Symbol)与一个值关联起来。

属性是与对象关联的一个键值对(key-value pair)。值可以是原始类型(primitive),如数字 (number)、字符串 (string) 或布尔值 (boolean);也可以是另一个对象 (object);或者是一个函数 (function)(在这种情况下,属性通常被称为方法 method)。

JavaScript 对象可以看作是属性的集合。尽管在现代 JavaScript 引擎中(自 ES2015 起),对于非整数键(non-integer keys),对象的自身属性的属性顺序通常是保持的,但最好不要依赖它来进行关键逻辑处理,特别是对于旧环境或继承属性。

属性通常可以被创建 (created)、读取 (read)、更新 (updated) 和删除 (deleted)。然而,一些属性(特别是内置属性或那些使用特定特性定义的属性)可能是只读的 (read-only) 或不可配置的 (non-configurable)。

有两种主要方式来访问对象的属性:

  1. 点表示法(Dot Notation):objectName.propertyName 示例:person.age

  2. 方括号表示法(Bracket Notation):objectName['propertyNameString'] 或 objectName[expression](其中 expression 求值结果为表示属性名称的字符串或 Symbol) 示例:person['age'] 或 let prop = 'age'; person[prop]

如果属性名是有效的 JavaScript 标识符(以字母、$ 或 _ 开头,包含字母、数字、$ 或 _,且不是保留字),通常首选点表示法,因为它更简洁。方括号表示法更通用:如果属性名不是有效的标识符(例如,'first name','user-id'),或者属性名是动态确定的(例如,存储在变量中),则必须使用方括号表示法。

const person = {
firstName: "Ada",
lastName: "Lovelace",
age: 36,
'country of birth': 'England'
};
// 使用点表示法
console.log(`${person.firstName} is ${person.age} years old.`); // Output: Ada is 36 years old.
// 使用方括号表示法(对于 'country of birth' 是必需的)
let dynamicProperty = 'lastName';
console.log(`Her full name is ${person.firstName} ${person[dynamicProperty]}.`); // Output: Her full name is Ada Lovelace.
console.log(`Born in: ${person['country of birth']}`); // Output: Born in: England

for...in 语句迭代对象的可枚举属性键(名称)。这包括对象的自身属性 (own properties) 和从其原型链 (prototype chain) 继承的属性。

for (const key in object) {
// 检查属性是否是自身属性至关重要
if (Object.hasOwnProperty.call(object, key)) {
// 'key' 是属性名称
// 'object[key]' 是属性值
// 在此处处理自身属性
}
}

for...in 循环内的代码块对于在对象或其原型链中找到的每个可枚举属性键执行一次。

遍历对象的自身属性:

const userProfile = {
username: "coder123",
email: "coder@example.com",
isActive: true
};
let profileDetails = "User Profile:\n";
for (const propKey in userProfile) {
if (Object.hasOwnProperty.call(userProfile, propKey)) {
profileDetails += `${propKey}: ${userProfile[propKey]}\n`;
}
}
console.log(profileDetails);
/* 输出(对于旧引擎,顺序可能会有所不同):
User Profile:
username: coder123
email: coder@example.com
isActive: true
*/

对于仅迭代对象的自身属性,现代替代方法如 Object.keys(obj).forEach()、Object.values(obj).forEach() 或 Object.entries(obj).forEach()(或结合 for...of 使用这些方法)通常是首选,以获得更好的控制和清晰度。更多详情请参阅循环章节。

你可以通过使用点表示法或方括号表示法,将值赋给一个新的属性名,从而向现有对象添加新属性。

假设 person 对象已经存在 - 然后你可以给它新的属性:

const person = {
firstName: "Grace",
lastName: "Hopper"
};
// 使用点表示法添加新属性
person.occupation = "Computer Scientist";
// 使用方括号表示法添加新属性
person['date of birth'] = "December 9, 1906";
console.log(person.occupation); // Output: Computer Scientist
console.log(person['date of birth']); // Output: December 9, 1906
console.log(person);
// { firstName: 'Grace', lastName: 'Hopper', occupation: 'Computer Scientist', 'date of birth': 'December 9, 1906' }

如果使用点表示法,属性名必须是有效的 JavaScript 标识符。使用方括号表示法时,属性名是字符串,可以包含保留字或任何字符。然而,即使使用方括号表示法,使用保留字作为属性名有时也会导致混淆,除非必要,通常最好避免。

delete 运算符从对象中删除一个属性。如果删除成功或属性不存在,它返回 true;如果属性不可配置(不能被删除),则返回 false。

const book = {
title: "The Pragmatic Programmer",
author: "David Thomas, Andrew Hunt",
year: 1999
};
console.log(book.year); // Output: 1999
const wasDeleted = delete book.year;
console.log(wasDeleted); // Output: true
console.log(book.year); // Output: undefined (属性不再存在)
console.log(book); // { title: '...', author: '...' }
// 尝试删除不存在的属性
const nonExistentDeleted = delete book.nonExistentProperty;
console.log(nonExistentDeleted); // Output: true(仍然是 true,因为操作没有失败)

delete 关键字既删除属性的值,也从对象中删除属性本身。

删除后,访问该属性将得到 undefined。该属性已完全从对象的自身属性中移除。

delete 运算符专门用于对象属性。它不能删除使用 var、let 或 const 声明的变量,也不能删除独立函数(除非它们是全局对象的属性,但这通常不建议修改)。

尝试删除不可配置属性(如许多标准对象的内置属性,例如 Math.PI)将失败(返回 false)或在严格模式 (strict mode) 下抛出 TypeError。通常不建议尝试删除预定义 JavaScript 对象的属性,除非你有非常具体的原因并了解后果。

除了名称和值之外,JavaScript 对象中的每个属性都有一组内部特性(attributes)(也称为描述符 descriptors),它们定义了属性的行为。数据属性的主要特性有:

  • value:与属性关联的实际值。
  • writable:一个布尔值。如果为 true,则可以更改属性的 value。如果为 false,尝试更改值将被忽略(或在严格模式下抛出错误)。
  • enumerable:一个布尔值。如果为 true,该属性将在 for...in 循环和 Object.keys() 中列出。如果为 false,它将对这些操作隐藏。
  • configurable:一个布尔值。如果为 true,该属性可以从对象中删除,并且可以更改其特性(writable、enumerable、configurable 和 value)。如果为 false,则不能删除该属性,并且不能更改其 enumerable 和 configurable 特性。当 configurable 为 false 时,writable 可以从 true 更改为 false,但不能从 false 更改为 true。

你可以使用 Object.getOwnPropertyDescriptor() 检查这些特性,并使用 Object.defineProperty() 或 Object.defineProperties() 定义或修改它们。这些方法提供了对对象属性的精细控制。

使用 Object.defineProperty() 的示例:

const myObj = {};
Object.defineProperty(myObj, 'readOnlyProp', {
value: "I cannot be changed!",
writable: false, // 不可写
enumerable: true, // 将出现在 for...in 和 Object.keys() 中
configurable: false // 不能被删除或重新配置
});
console.log(myObj.readOnlyProp); // I cannot be changed!
try {
myObj.readOnlyProp = "New Value"; // 在静默模式或严格模式下会失败或抛出错误
} catch (e) {
console.error(e.message);
}
console.log(myObj.readOnlyProp); // 仍然是 "I cannot be changed!"
console.log(Object.keys(myObj)); // ['readOnlyProp']
const deleteStatus = delete myObj.readOnlyProp; // 失败,因为 configurable 为 false
console.log(deleteStatus); // false (或在严格模式下抛出错误)
console.log(myObj.readOnlyProp); // 仍然是 "I cannot be changed!"

JavaScript 对象有一个内部链接指向另一个对象,称为它们的原型(prototype)。当你尝试访问对象上的一个属性时,如果对象本身没有该属性(它不是一个“自身”属性 ‘own’ property),JavaScript 就会在其原型上查找,然后在原型的原型上查找,以此类推,沿着原型链 (prototype chain) 向上查找,直到找到该属性或链的末端(null)。这些被称为继承属性 (inherited properties)。

delete 运算符只作用于对象的自身属性。它不能删除从原型链继承的属性。然而,如果你直接在原型对象上修改或删除一个属性,这个改变将反映在所有继承自该原型的对象中(除非它们有自己的同名覆盖属性)。

function Dog(name) {
this.name = name; // 自身属性
}
Dog.prototype.species = "Canine"; // 原型上的属性
Dog.prototype.bark = function() { console.log('Woof!'); }; // 原型上的方法
const buddy = new Dog("Buddy");
const lucy = new Dog("Lucy");
console.log(buddy.species); // Canine (继承自 Dog.prototype)
buddy.bark(); // Woof! (继承自 Dog.prototype 的方法)
// 对实例使用 'delete' 不会影响其他实例的原型属性
delete buddy.species; // 这只会在 'species' 是 'buddy' 的自身属性时起作用。
// 因为它是继承的,所以此操作对 'buddy.species' 没有影响。
console.log(buddy.species); // 仍然是 Canine(它会再次在原型上查找)
// 如果 buddy 有它自己的 'species' 属性,delete 将删除那个自身属性:
buddy.species = "Super Dog"; // 现在 buddy 拥有它自己的 'species' 属性,这遮蔽了原型上的属性。
console.log(buddy.species); // Super Dog
console.log(lucy.species); // Canine (lucy 仍然继承自 Dog.prototype)
delete buddy.species; // 删除 buddy 的自身 'species' 属性
console.log(buddy.species); // Canine (现在再次继承自 Dog.prototype)
// 要影响所有实例,直接修改原型:
// delete Dog.prototype.species;
// console.log(buddy.species); // undefined(如果从原型中删除且不存在自身属性)
// console.log(lucy.species); // undefined