如何在JavaScript扩展运算符(...)中访问对象的嵌套命名属性?
嘿,这个问题我之前也遇到过!扩展运算符确实没法直接像你那样用['c']['y']来修改嵌套属性,因为对象字面量里的键定义不支持这种嵌套语法,咱们一步步来解决它。
为什么你的代码会报错?
你写的['a']: 2能正常工作,是因为这是合法的计算属性名,直接对应对象的顶层a属性;但['c']['y']这种写法在对象键的声明位置完全无效——JavaScript解析器无法识别你要修改的是c对象下的y属性,自然会抛出语法错误。
扩展运算符的核心作用是复制顶层属性,如果要修改嵌套属性,你需要逐层重构整个嵌套结构,同时保持原对象的不可变性(这也是Redux reducer的核心要求)。
解决方案1:手动逐层重构嵌套对象
对于你的示例代码,正确的写法应该是重新定义c属性,先展开原c对象的所有属性,再覆盖需要修改的y:
var foo = { a: 1, b: 2, c: {x: 999, y:998, z: 997}}; var foo1 = { ...foo, // 复制foo的顶层属性 ['a']: 2, // 计算属性名修改顶层a(直接写a:2也可以,更简洁) c: { ...foo.c, // 复制原c对象的所有属性 y: 1000 // 覆盖嵌套的y属性 } }; alert(foo1['c']['y']); // 输出1000,正常工作
这个方法的核心是:每一层嵌套都要展开原对象,再修改目标属性,这样既保留了未修改的属性(比如c.x和c.z),又保证了对象的不可变性——不会直接修改原foo对象。
如果嵌套更深(比如foo.c.d.e),只需要继续逐层展开:
var foo = { a:1, c: { d: { e: 5 } } }; var foo1 = { ...foo, c: { ...foo.c, d: { ...foo.c.d, e: 10 } } };
解决方案2:结合Redux场景的实践
在Redux reducer中,我们经常需要更新数组或深层嵌套的state属性,比如你提到的AgGrid单元格更新场景。这里举一个更贴近你需求的例子:
假设你的state结构是:
const initialState = { invoices: [ { id: 1, amount: 100, items: [{ name: "商品1", price: 50 }, { name: "商品2", price: 50 }] }, { id: 2, amount: 200 } ] };
如果要更新第0个发票的第1个商品价格,用扩展运算符的写法如下:
case UPDATE_ITEM_PRICE: { return { ...state, invoices: state.invoices.map((invoice, invIndex) => { // 找到目标发票 if (invIndex === action.payload.invIndex) { return { ...invoice, // 重构items数组 items: invoice.items.map((item, itemIndex) => { // 找到目标商品 if (itemIndex === action.payload.itemIndex) { return { ...item, price: action.payload.newPrice }; } return item; // 其他商品保持不变 }) }; } return invoice; // 其他发票保持不变 }) }; }
解决方案3:用工具库简化嵌套更新
手动逐层展开的写法在深层嵌套时会非常繁琐,推荐使用Immer库,它允许你用“可变”的写法创建不可变对象,大大简化代码:
首先安装Immer:
npm install immer
然后在reducer中使用:
import produce from 'immer'; // ... case UPDATE_ITEM_PRICE: { return produce(state, draft => { // 直接“修改”draft,Immer会自动生成不可变的新state draft.invoices[action.payload.invIndex].items[action.payload.itemIndex].price = action.payload.newPrice; }); }
这种写法既直观又符合Redux的不可变性要求,现在已经成为Redux社区的主流实践之一。
总结
- 扩展运算符只能直接处理顶层属性,嵌套属性需要逐层重构对象/数组
- 手动重构时,要确保每一层都展开原对象,避免修改原state(符合Redux不可变要求)
- 复杂嵌套场景推荐使用Immer简化代码
内容的提问来源于stack exchange,提问作者TomR




