JavaScript如何动态访问并更新任意深度嵌套对象的属性
问题背景
已经掌握通过变量配合方括号表示法访问平面对象属性的方法,但该方案仅支持无嵌套的单层对象,无法适配多层嵌套结构。当前需要处理包含不同深度层级属性的对象,实现通过变量动态访问、修改对应属性值的能力。
示例目标对象结构如下:
{ settings: { datasets: [ { label: "Suivi", type: "line", backgroundColor: "#2bb0e9", borderColor: "#2bb0e9", pointBackgroundColor: "#2bb0fc", pointBorderColor: "#2bb0fc", pointRadius: 0, data: [], cubicInterpolationMode: "monotone", tension: 0.4, }, ], }, config: { maintainAspectRatio: false, plugins: { legend: { display: false, }, tooltip: { callbacks: { label: function (context) { let label = ""; if ( context.dataset.backgroundColor[context.dataIndex] !== "#f4f4f4" ) { label = context.formattedValue; } else { label = "No information"; } return label; }, }, }, }, }, }
实际使用场景下,需要支持访问不同深度的属性,例如3层深度的object.config.maintainAspectRatio、5层深度的object.settings.datasets[0].backgroundColor等,要求实现一个动态更新对象的函数,入参为属性路径path和新值newValue。
此前已尝试两种实现思路,均存在明显缺陷:
- 思路1:将完整属性路径存为单个字符串(例如
"settings.datasets[0].label"),直接通过object[path]访问属性,方案完全无法生效 - 思路2:将不同深度层级的路径拆分为字符串数组(例如
["settings", "datasets", "0", "borderColor"]),通过多层if判断逐层级匹配路径长度完成赋值,代码冗余度极高,属于不良实践
优雅实现方案
不需要编写多层分支判断适配不同路径长度,核心逻辑仅需两步,可兼容任意深度的对象、数组嵌套场景:
- 路径标准化:将传入的路径统一处理为按层级拆分的键名数组,既支持带
.、[]的字符串路径格式,也支持提前拆分好的数组路径格式。例如字符串settings.datasets[0].backgroundColor,只需将方括号包裹的数字替换为.数字格式,再按.拆分即可得到标准键数组。 - 逐层遍历赋值:从根对象开始逐层向下遍历,始终持有当前遍历层级的引用,遍历到路径最后一个键时直接赋值即可。遍历过程中如果遇到中间层不存在的情况,可根据下一个键的类型自动初始化空对象或空数组,避免访问报错。
完整代码实现
/** * 动态设置嵌套对象指定路径的属性值 * @param {Object|Array} target 目标对象/数组 * @param {string|Array} path 属性路径,支持字符串格式(如"a.b[0].c")或数组格式 * @param {*} newValue 要设置的新值 * @returns 修改后的目标对象 */ function setNestedValue(target, path, newValue) { // 统一解析路径为标准化键数组 const pathKeys = Array.isArray(path) ? path : path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(Boolean); let current = target; for (let i = 0; i < pathKeys.length; i++) { const key = pathKeys[i]; // 遍历到最后一个键时直接赋值 if (i === pathKeys.length - 1) { current[key] = newValue; break; } // 中间层不存在时自动初始化结构,下一个键为数字则初始化为数组,否则初始化为对象 if (current[key] == null) { current[key] = /^\d+$/.test(pathKeys[i + 1]) ? [] : {}; } // 移动引用到下一层 current = current[key]; } return target; } /** * 配套的嵌套属性读取方法 * @param {Object|Array} target 目标对象/数组 * @param {string|Array} path 属性路径 * @param {*} defaultValue 路径不存在时返回的默认值 * @returns 读取到的属性值 */ function getNestedValue(target, path, defaultValue = undefined) { const pathKeys = Array.isArray(path) ? path : path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(Boolean); let current = target; for (const key of pathKeys) { if (current == null) return defaultValue; current = current[key]; } return current ?? defaultValue; }
使用示例
const chartConfig = {/* 此处为前文给出的示例对象 */}; // 修改3层深度属性 setNestedValue(chartConfig, "config.maintainAspectRatio", true); // 修改5层深度带数组下标的属性 setNestedValue(chartConfig, "settings.datasets[0].backgroundColor", "#ff0000"); // 支持数组格式路径传参 setNestedValue(chartConfig, ["config", "plugins", "legend", "display"], true); // 读取属性验证结果 console.log(getNestedValue(chartConfig, "settings.datasets[0].backgroundColor")); // 输出 #ff0000 console.log(getNestedValue(chartConfig, "config.maintainAspectRatio")); // 输出 true
该方案代码量固定,和属性嵌套深度无关,同时兼容对象、数组混合嵌套场景,支持两种路径传参格式,遍历过程自动补全不存在的中间层结构,不会出现访问报错。
内容的提问来源于stack exchange,提问作者FlowRan
相关产品推荐
相关产品推荐

