嵌套对象允许您使用纯 JavaScript 来建模现实世界的分层数据、包含个人资料的用户、包含商品的订单以及包含子设置的设置。掌握嵌套对象后,您将编写更简洁、更安全、更易于管理的代码。在本教程中,我们将通过简单的模式和实际操作示例来逐步讲解嵌套对象的使用方法。首先,我们将重点介绍您将要学习的内容,然后从创建对象到访问、更新、迭代和克隆对象;每个部分都相互衔接,以确保学习过程的顺利进行。
您将学到什么
最后,您将能够:
创建并可视化嵌套对象。
使用点/括号符号和可选链接访问深层属性。
安全地更新和添加嵌套值(可变和不可变)。
迭代并搜索嵌套结构。
解构嵌套值以获得更清晰的代码。
深度克隆和合并,避免常见的陷阱。
现在您已经了解了目标,让我们首先快速定义嵌套对象的样子。
什么是嵌套对象?
嵌套对象只不过是一个包含其他对象作为值的对象。
const user = {
id: 42,
profile: {
name: {
first: "Wisdom",
last: "Udo"
},
contact: {
email: "wisdom@example.com",
phone: null
}
},
preferences: {
theme: "dark",
notifications: {
email: true,
sms: false
}
}
};此设置反映了现实世界的数据,并将相关信息组织在一起。概念明确后,让我们看看如何创建和扩展这些结构。
下一步:创建和可视化嵌套对象。
创建和可视化嵌套对象
嵌套对象可以一次性初始化,也可以逐步组装。
// One-shot
const settings = {
ui: {
theme: "light",
density: "comfortable"
},
auth: {
token: "abc1234",
expiresIn: 3600
}
};
// Step-by-step (useful when fields are conditional)
const config = {};
config.api = {};
config.api.endpoints = { users: "/api/users", posts: "/api/posts" };提示:在开发过程中记录深层片段以“查看”结构。
console.log(settings.ui.theme); // "light"
console.dir(config, { depth: null });现在我们可以创建和检查嵌套对象,我们需要安全地从它们中读取值。
下一步:访问深层属性。
(安全地)访问嵌套属性
访问已知键时使用点符号,但对于变量键或带有空格的键,请切换到括号符号。
const lastName = user.profile.name.last; // dot const key = "email"; const email = user.profile.contact[key]; // bracket
为了避免 TypeError:无法读取未定义的属性,请应用可选链接(?。)和空值合并(??)。
const phone = user.profile?.contact?.phone ?? "No phone on file";
对象里面有数组?结合索引:
const order = { items: [{ sku: "A2", qty: 2 }] };const firstSku = order.items?.[0]?.sku;
// "A2"现在我们已经了解了如何读取嵌套值,下一步是学习如何编写和更新它们。
下一步:更新和添加嵌套值。
更新和添加嵌套值
直接突变是最简单的:
user.preferences.notifications.sms = true; user.profile.contact.phone = "+234-000-0000";
但是,如果应用程序的其他部分共享相同的引用,则突变可能会使其感到意外。为了更安全的模式,尤其是在 React/Redux 中,请使用对象扩展的不可变更新:
const updatedUser = {
...user,
preferences: {
...user.preferences,
notifications: {
...user.preferences.notifications,
sms: true
}
}
};添加新分支也是同样的模式:
const withLocale = {
...user,
profile: {
...user.profile,
locale: {
lang: "en",
region: "GB"
}
}
};现在我们可以读取和写入数据,让我们以编程方式完成它。
下一步:迭代和搜索嵌套对象。
迭代和搜索嵌套结构
浅层迭代:
Object.keys(user.preferences).forEach(k => {
console.log(k, user.preferences[k]);
});对于深度遍历,一个小的递归助手很方便:
function getByPath(obj, path) {
return path.split(".").reduce((acc, key) => acc?.[key], obj);
}
getByPath(user, "profile.name.first"); // "Wisdom"或者访问每个键/值:
function walk(obj, visit, basePath = "") {
for (const [k, v] of Object.entries(obj)) {
const path = basePath ? ${basePath}.${k} : k;visit(path, v);
if (v && typeof v === "object" && !Array.isArray(v)) {
walk(v, visit, path);
}
}
}
walk(user, (path, value) => console.log(path, value));一旦遍历清楚了,就可以有效地选择或重新组织数据。
下一步:使用解构来提取嵌套值。
解构嵌套对象
解构允许您在单个语句中提取字段,同时还支持默认值和重命名。
const {
profile: {
name: {
first: firstName,
last: lastName
},
contact: {
email,
phone = "N/A"
}
},
preferences: { theme = "light" }
} = user;
console.log(firstName, lastName, email, phone, theme);这样可以在需要特定字段时保持使用简洁。提取后,您可能需要克隆或合并对象而不破坏引用。
下一步:安全地克隆和合并。
浅拷贝(扩展或Object.assign)仅复制顶层:
const shallow = { ...user }; // profile/ preferences are still shared references对于深度克隆,structuredClone在现代环境中更受欢迎:
const deep = structuredClone(user); deep.profile.name.first = "Joy"; console.log(user.profile.name.first); // still "Wisdom"
开发人员经常使用JSON.parse(JSON.stringify(obj))来快速回退,尽管它会删除函数Date、、、和。为了获得更可靠的解决方案Map,像 Lodash 这样的实用程序是首选。Setundefined_.cloneDeep
对于深度合并(组合嵌套分支),可以使用库或微小的递归合并:
function deepMerge(target, source) {
const out = { ...target };
for (const [k, v] of Object.entries(source)) {
if (v && typeof v === "object" && !Array.isArray(v)) {
out[k] = deepMerge(target[k] ?? {}, v);
} else {
out[k] = v;
}
}
return out;
}
const merged = deepMerge(user, { preferences: { notifications: { push: true } } });通过工具包中的克隆和合并,最后的几个陷阱将帮助您避免错误。
下一步:常见陷阱和快速修复。
常见陷阱和快速解决方法
未定义路径:使用可选链(a?.b?.c)和默认值(??)。
浅拷贝惊喜:当您真正需要独立时,请使用structuredClone深度克隆实用程序。
动态键:始终切换到括号表示法 ( obj[key]),而不是点 ( obj.key)。
数组与对象:递归之前检查类型;数组需要索引处理。
为了在 UI 应用程序中保持一致的状态,最好使用不可变的更新而不是频繁的变异。
有了概念和保障措施,我们就可以总结一下了。
结论
嵌套对象是 JavaScript 中复杂数据建模的支柱。您已经了解了如何创建嵌套对象、安全地访问它们的值、以不可变的方式更新它们、使用辅助函数进行迭代和搜索、使用解构来获得更简洁的代码,以及如何在不出错的情况下进行克隆或合并。通过应用这些模式,您的代码将保持强大、易读且易于维护。
巩固这些概念的一个实用方法是获取 JSON 响应,识别其嵌套路径,构建一个类似 的函数getByPath,并使用不可变扩展来处理深度更新。这个练习将巩固你的学习成果。