解决 "Cannot assign to read only property '0' of object '[object Array]'" 错误
问题描述
在使用 Angular 或 React 等前端框架时,开发者经常会遇到这样的错误:TypeError: Cannot assign to read only property '0' of object '[object Array]'
。
这个错误通常在以下场景发生:
- 对作为输入的数组(如 Angular 的
@Input()
数组)进行排序操作 - 在 Redux 状态管理中尝试直接修改状态数组
- 处理从外部接收的不可变数据时进行原地修改
如示例代码所示,问题出现在尝试直接修改数组元素时:
typescript
exchange(a, b) {
const temp = this.taskList[a];
this.taskList[a] = this.taskList[b]; // 这里报错
this.taskList[b] = temp;
}
问题根源
这个错误的根本原因是 JavaScript 中的**对象冻结(Object Freeze)**机制。当数组被设置为只读或不可变时:
- 通过
Object.freeze()
显式冻结的数组 - React/Redux 状态管理中的不可变状态
- Angular 的
@Input()
属性(默认情况下是不可变的) - 其他框架或库设置的不可变数据结构
注意
即使初始操作成功,后续操作可能失败,因为数据源的不可变性可能在不同阶段发生变化。
解决方案
方法一:使用扩展运算符创建副本(推荐)
typescript
// 创建数组副本再进行排序操作
const arrayCopy = [...originalArray];
arrayCopy.sort((a, b) => a.property - b.property);
// 或者在交换函数中
exchange(a, b) {
const newArray = [...this.taskList];
const temp = newArray[a];
newArray[a] = newArray[b];
newArray[b] = temp;
this.taskList = newArray; // 替换整个数组
}
方法二:使用 slice() 方法
typescript
const arrayCopy = originalArray.slice();
arrayCopy.sort((a, b) => a.property - b.property);
方法三:使用 Array.from()
typescript
const arrayCopy = Array.from(originalArray);
arrayCopy.sort((a, b) => a.property - b.property);
方法四:解构赋值结合排序
typescript
// 在需要排序时创建副本
taskChange(value, taskOrder) {
const sortOrder = taskOrder;
const selectedValue = value;
// 创建副本
const sortedArray = [...this.taskList];
// 对副本进行排序操作
// ... 排序逻辑
// 最后替换原数组
this.taskList = sortedArray;
}
React/Redux 特殊处理
在 React 和 Redux 环境中,状态是不可变的,必须特别注意:
typescript
// 错误做法:直接修改状态
const items = getItems(state);
items.sort((a, b) => a.order - b.order); // 会报错
// 正确做法:创建副本
const items = [...getItems(state)];
items.sort((a, b) => a.order - b.order);
最佳实践
- 始终假设输入数据是不可变的:不要直接修改传入的数组或对象
- 使用函数式编程模式:优先使用不会改变原数组的方法(如
map
,filter
,reduce
) - 在框架边界处处理不可变性:在接收到数据时立即创建可修改的副本
- 使用不可变库:对于复杂应用,考虑使用 Immutable.js 或 Immer 等库
性能考虑
虽然创建副本会增加内存使用,但对于大多数应用来说,这种开销是可以接受的。只有在处理极大数组时才需要考虑性能优化。
总结
Cannot assign to read only property
错误是由于尝试修改只读数组引起的解决方案是创建数组的副本并对副本进行操作,而不是直接修改原数组。这种方法符合函数式编程原则和现代前端框架的最佳实践,能有效避免此类错误。