☰
现代 JavaScript 教程:深入理解 JavaScript 对象(Object)核心机制
2026/10/1 22:06:38 网站建设 项目流程
  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

对象(Object)是 JavaScript 中最重要的数据类型之一,它用于存储键值对(key-value pairs)集合,承载着语言中几乎所有复杂的数据结构。本文以 en.javascript.info 仓库中 1-js/04-object-basics/01-object/article.md 章节为核心,系统讲解对象的创建、属性访问、计算属性、属性名限制、存在性检测、遍历顺序等机制,并结合仓库中 2-hello-object、3-is-empty、5-sum-object、8-multiply-numeric 等配套练习任务与测试代码,帮助读者真正掌握对象这一"一切皆对象"语言的地基。

什么是对象:键值对的集合

JavaScript 中有八种数据类型,其中七种被称为"原始类型"(primitive),它们的值只包含单一的数据(如一个字符串、一个数字)。与之相对,对象用于存储各种数据的键值集合以及更复杂的实体。

一个对象可以通过花括号{…}创建,并可选地包含一个属性列表。属性是"key: value"对,其中key是字符串(也称为"属性名"),value可以是任意类型。

可以把对象想象成一个带标签文件的档案柜:每一份数据按 key 存放在对应的文件中,通过文件名可以轻松查找、添加或移除文件。

创建一个空对象("空档案柜")有两种语法:

let user = new Object(); // "对象构造函数"语法 let user = {}; // "对象字面量"语法

通常使用花括号{...},这种声明方式被称为对象字面量(object literal)。

字面量与属性

我们可以在{...}中立即放入一些属性作为 "key: value" 对:

let user = { // 一个对象 name: "John", // 通过 key "name" 存储值 "John" age: 30 // 通过 key "age" 存储值 30 };

一个属性在冒号":"之前是 key(也称为"名称"或"标识符"),冒号右侧是值。上面的user对象有两个属性:名为"name"值为"John"的属性,以及名为"age"值为30的属性。

可以随时添加、删除和读取其中的"文件"。属性值使用点号表示法(dot notation)访问:

// 获取对象的属性值: alert( user.name ); // John alert( user.age ); // 30

属性的值可以是任意类型,例如添加一个布尔值:

user.isAdmin = true;

要删除属性,使用delete运算符:

delete user.age;

也可以使用多词属性名,但此时必须加引号:

let user = { name: "John", age: 30, "likes birds": true // 多词属性名必须加引号 };

列表中的最后一个属性可以以逗号结尾:

let user = { name: "John", age: 30, }

这被称为"尾随逗号"(trailing/hanging comma),它让添加、删除或移动属性变得更方便,因为所有行看起来都一样。

方括号表示法

对于多词属性,点号表示法无法工作:

// 这会产生语法错误 user.likes birds = true

JavaScript 会认为在访问user.likes,然后遇到意外的birds时报语法错误。点号要求 key 必须是合法的变量标识符,即:不含空格、不以数字开头、不包含特殊字符($和_除外)。

有一种替代方案——方括号表示法(square bracket notation),它适用于任意字符串:

let user = {}; // 设置 user["likes birds"] = true; // 获取 alert(user["likes birds"]); // true // 删除 delete user["likes birds"];

注意方括号内的字符串必须正确加引号(任何引号类型都可以)。

方括号还提供了一种通过表达式获取属性名的方式——而不仅仅是字面字符串,例如从变量获取:

let key = "likes birds"; // 等同于 user["likes birds"] = true; user[key] = true;

这里的变量key可以在运行时计算,或依赖于用户输入,然后用它来访问属性,这带来了极大的灵活性。例如:

let user = { name: "John", age: 30 }; let key = prompt("你想了解用户哪方面的信息?", "name"); // 通过变量访问 alert( user[key] ); // 如果输入 "name" 则显示 John

点号表示法则不能这样使用:

let user = { name: "John", age: 30 }; let key = "name"; alert( user.key ) // undefined

计算属性

在创建对象的字面量中也可以使用方括号,这被称为计算属性(computed properties):

let fruit = prompt("想买什么水果?", "apple"); let bag = { [fruit]: 5, // 属性名取自变量 fruit }; alert( bag.apple ); // 如果 fruit="apple" 则显示 5

计算属性的含义很简单:[fruit]表示属性名应取自fruit变量。如果访问者输入"apple",bag将变成{apple: 5}。

本质上这与下面的写法效果相同:

let fruit = prompt("想买什么水果?", "apple"); let bag = {}; // 从 fruit 变量获取属性名 bag[fruit] = 5;

方括号内还可以使用更复杂的表达式:

let fruit = 'apple'; let bag = { [fruit + 'Computers']: 5 // bag.appleComputers = 5 };

方括号表示法比点号表示法强大得多——它允许任意属性名和变量。但它写起来也更繁琐。所以大多数时候,当属性名已知且简单时使用点号;需要更复杂的场景时再切换到方括号。

属性值简写

在实际代码中,我们经常用已有的变量作为属性值:

function makeUser(name, age) { return { name: name, age: age, // ...其他属性 }; } let user = makeUser("John", 30); alert(user.name); // John

当属性名与变量名相同时,可以使用属性值简写(property value shorthand),把name: name直接写成name:

function makeUser(name, age) { return { name, // 等同于 name: name age, // 等同于 age: age // ... }; }

同一个对象中也可以混用普通属性和简写:

let user = { name, // 等同于 name: name age: 30 };

属性名的限制

变量不能使用语言保留字(如for、let、return等)作为名称,但对象属性没有这种限制:

// 这些属性都合法 let obj = { for: 1, let: 2, return: 3 }; alert( obj.for + obj.let + obj.return ); // 6

简言之,属性名没有任何限制,可以是任意字符串或 symbol(一种用于标识符的特殊类型,后续章节会讲到)。其他类型会自动转换为字符串。

例如,数字0作为属性键时会变成字符串"0":

let obj = { 0: "test" // 等同于 "0": "test" }; // 两个 alert 访问的是同一个属性(数字 0 被转换为字符串 "0") alert( obj["0"] ); // test alert( obj[0] ); // test(同一个属性)

有一个小陷阱:特殊属性__proto__。我们无法将其设置为非对象值:

let obj = {}; obj.__proto__ = 5; // 赋值为数字 alert(obj.__proto__); // [object Object] - 值是一个对象,赋值未生效

从代码可以看到,对原始值5的赋值被忽略了。__proto__的特殊性质将在 原型继承 章节详细讨论,原型方法 章节也会给出解决此类行为的建议方法。

属性存在性测试:"in" 运算符

与许多其他语言不同,JavaScript 对象的显著特性是可以访问任何属性——即使属性不存在也不会报错!读取不存在的属性只会返回undefined,因此可以轻松测试属性是否存在:

let user = {}; alert( user.noSuchProperty === undefined ); // true,表示"没有这个属性"

此外还有一个专门的"in"运算符,语法为:

"key" in object

例如:

let user = { name: "John", age: 30 }; alert( "age" in user ); // true,user.age 存在 alert( "blabla" in user ); // false,user.blabla 不存在

注意in的左侧必须是属性名,通常是一个带引号的字符串。如果省略引号,则意味着变量应该包含要测试的实际名称:

let user = { age: 30 }; let key = "age"; alert( key in user ); // true,属性 "age" 存在

为什么需要in运算符?与undefined比较还不够吗?大多数情况下比较undefined都能正常工作,但在一种特殊情况下会失败,而"in"能正确处理——那就是属性存在但存值为undefined:

let obj = { test: undefined }; alert( obj.test ); // undefined,所以——没有这个属性? alert( "test" in obj ); // true,属性确实存在!

上面代码中obj.test属性在技术上存在,所以in运算符判断正确。这种情况很少发生,因为undefined不应被显式赋值,我们通常用null表示"未知"或"空"值。因此in运算符在代码中是相当少见的存在。

"for..in" 循环

要遍历对象的所有键,存在一种特殊的循环形式for..in,它与之前学过的for(;;)完全不同:

for (key in object) { // 对对象的每个属性键执行循环体 }

例如,输出user的所有属性:

let user = { name: "John", age: 30, isAdmin: true }; for (let key in user) { // 键 alert( key ); // name, age, isAdmin // 键对应的值 alert( user[key] ); // John, 30, true }

注意,所有的 "for" 结构都允许在循环内部声明循环变量,如这里的let key。也可以使用其他变量名,例如"for (let prop in obj)"也很常见。

像对象一样排序

对象是有序的吗?换句话说,如果遍历对象,是否会按照属性添加的顺序获得所有属性?可以依赖这一点吗?

简短的回答是:"以一种特殊方式排序"——整数属性按升序排列,其他属性按创建顺序排列。细节如下。

以一个存有电话区号的对象为例:

let codes = { "49": "Germany", "41": "Switzerland", "44": "Great Britain", // ..., "1": "USA" }; for (let code in codes) { alert(code); // 1, 41, 44, 49 }

这个对象可能用于向用户提供选项列表。如果网站主要面向德国用户,可能希望49排在最前面。但运行代码会看到完全不同的结果:USA(1)排第一,然后是 Switzerland(41)等。电话区号按升序排列,因为它们都是整数,所以看到的是1, 41, 44, 49。

什么是"整数属性"?这里的"整数属性"指可以无损地转换成整数再转回来的字符串。因此"49"是整数属性名(转成数字再转回仍是它本身),而"+49"和"1.2"不是:

// Number(...) 显式转换为数字 // Math.trunc 是移除小数部分的内置函数 alert( String(Math.trunc(Number("49"))) ); // "49",相同,是整数属性 alert( String(Math.trunc(Number("+49"))) ); // "49",不同 "+49" ⇒ 不是整数属性 alert( String(Math.trunc(Number("1.2"))) ); // "1",不同 "1.2" ⇒ 不是整数属性

另一方面,如果键不是整数,则按创建顺序列出:

let user = { name: "John", surname: "Smith" }; user.age = 25; // 再添加一个 // 非整数属性按创建顺序列出 for (let prop in user) { alert( prop ); // name, surname, age }

要解决电话区号的问题,可以"作弊"让代码变成非整数——在每个区号前加上"+"号即可:

let codes = { "+49": "Germany", "+41": "Switzerland", "+44": "Great Britain", // ..., "+1": "USA" }; for (let code in codes) { alert( +code ); // 49, 41, 44, 1 }

现在按预期工作了。

配套练习:动手验证对象机制

en.javascript.info 仓库为本章配备了多个由浅入深的编程任务,每个任务都配有标准解法和 Mocha 测试(_js.view目录下的test.js),非常适合用来验证本文所述的机制。

练习一:Hello, object(创建与增删改)

任务要求逐行完成:创建空对象user;添加属性name(值John)与surname(值Smith);将name改为Pete;最后删除name属性。标准解法(见 2-hello-object/solution.md)正好覆盖了对象字面量、点号赋值与delete运算符:

let user = {}; user.name = "John"; user.surname = "Smith"; user.name = "Pete"; delete user.name;

练习二:检查空对象(for..in 的经典应用)

任务要求编写isEmpty(obj)函数,判断对象是否没有任何属性。解法思路是:循环遍历对象,只要循环启动就说明存在至少一个属性,立即return false(见 3-is-empty/solution.md):

function isEmpty(obj) { for (let key in obj) { // 如果循环启动了,就说明有属性 return false; } return true; }

仓库中的 3-is-empty/_js.view/test.js 用 Mocha 断言验证了两种场景:空对象{}返回true,含anything: false属性的对象返回false(注意值为false也视为属性存在)。

练习三:对象属性求和(遍历取值)

任务要求对存有团队工资的对象求和,空对象时结果为0。解法利用for..in逐个累加(见 5-sum-object/solution.md):

let salaries = { John: 100, Ann: 160, Pete: 130 }; let sum = 0; for (let key in salaries) { sum += salaries[key]; } alert(sum); // 390

练习四:数值属性翻倍(typeof 类型过滤)

任务要求编写multiplyNumeric(obj),将对象中所有数值型属性的值乘以 2,字符串等其他类型保持不变,且原地修改、无需返回。仓库提供了完整实现与测试(见 8-multiply-numeric/_js.view/solution.js 与 8-multiply-numeric/_js.view/test.js):

function multiplyNumeric(obj) { for (let key in obj) { if (typeof obj[key] == 'number') { obj[key] *= 2; } } }

配套测试断言:width200 → 400、height300 → 600,而title字符串保持不变;并且调用multiplyNumeric({})的返回值必须是undefined(即函数不返回任何东西)。这个小练习同时演示了typeof类型检测、for..in遍历与通过obj[key]动态读写属性这三个核心知识点。

总结

对象是带有若干特殊特性的关联数组(associative arrays),它们以键值对存储属性:

  • 属性键必须是字符串或 symbol(通常是字符串);
  • 值可以是任意类型。

访问属性的方式:

  • 点号表示法:obj.property;
  • 方括号表示法obj["property"]——方括号允许从变量取键,如obj[varWithKey]。

其他运算符:

  • 删除属性:delete obj.prop;
  • 检查是否存在具有给定键的属性:"key" in obj;
  • 遍历对象:for (let key in obj)循环。

本章研究的是"普通对象"(plain object),也就是Object本身。JavaScript 中还有很多其他类型的对象:

  • Array用于存储有序数据集合,
  • Date用于存储日期和时间信息,
  • Error用于存储错误信息,
  • ……等等。

它们都有各自特殊的特性,将在后续章节中学习。有时人们会说"Array 类型"或"Date 类型",但从形式上讲,它们并不是独立的类型,而是属于同一个"object"数据类型,并以各种方式扩展它。

JavaScript 中的对象非常强大,这里只是触及了这个庞大主题的表面。后续教程中我们会继续深入使用对象并了解更多相关知识。

  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

相关推荐

上一篇:Node.js 18.14.2 (LTS) 发布公告深度解读:npm 9.5.0 升级、安全变更与全平台下载校验指南
下一篇:基于 Matter SDK all-devices-app 构建量产级定制产品的完整实践指南(Custom Product Baseline)

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询