无法分配给 JavaScript 中对象的只读属性
Cannot assign to read only property of Object in JavaScript
当我们尝试更改已冻结的对象的属性或使用定义的属性时,会出现错误“无法分配给对象的只读属性” Object.defineProperties()
。
要解决该错误,请创建对象或数组的副本,或将属性设置为writable
。
以下是错误发生方式的 3 个示例。
// 👇️ With OBJECTS 👇️ const obj = { name: 'James', }; Object.freeze(obj); // ⛔️ Error: Cannot assign to read only property 'name' of object '#<Object>' obj.name = 'Alice'; // ------------------------------------------------------ // 👇️ With ARRAYS 👇️ const arr = ['a', 'b', 'c']; Object.freeze(arr); // ⛔️ Error: Cannot assign to read only property '0' of object '[object Array]' arr[0] = 'z'; // ------------------------------------------------------ // 👇️ With Object.defineProperties() 👇️ const obj2 = {}; Object.defineProperties(obj2, { country: { value: 'Germany', // 👉️ must be set writable: true // writable: true, // 👈️ uncomment this }, }); // ⛔️ Error: Cannot assign to read only property 'country' of object '#<Object>' obj2.country = 'Austria';
前两个错误的发生是因为对象或数组已被
Object.freeze()
方法冻结。
常见错误原因
最常见的错误原因是:
- 试图将属性分配给冻结的对象或数组。
- 使用时忘记设置
writable
为。true
Object.defineProperties
- 在使用第三方库(例如 React.js)时尝试就地修改状态对象或数组。
创建数组或对象的副本
无法再更改冻结的对象,但您可以创建数组或对象的副本并更改副本。
// ✅ With OBJECTS const obj = { name: 'James', }; Object.freeze(obj); const objCopy = {...obj}; // 👈️ create copy objCopy.name = 'Alice'; console.log(objCopy); // 👉️ {name: 'Alice'} // -------------------------------------- // ✅ With ARRAYS const arr = ['a', 'b', 'c']; Object.freeze(arr); const arrCopy = [...arr]; // 👈️ create copy arrCopy[0] = 'z'; console.log(arrCopy); // 👉️ ['z', 'b', 'c']
我们使用
扩展语法 (…)创建对象和数组的副本,因此我们可以更改它们。
尝试对只读数组进行排序
当您尝试对只读数组进行排序时也会发生此错误,因为该
Array.sort()
方法会在适当的位置对数组进行排序。
const arr = ['a', 'b', 'c']; Object.freeze(arr); // ⛔️ TypeError: Cannot assign to read only property '0' of object '[object Array]' arr.sort();
要解决该错误,
请创建数组的副本并对副本进行排序。
const arr = ['a', 'b', 'c']; Object.freeze(arr); // 👇️ create copy const arrCopy = [...arr]; // 👇️ sort the copy arrCopy.sort(); console.log(arrCopy); // 👉️ [ 'a', 'b', 'c' ]
我们使用扩展语法 (…) 创建数组的副本并调用
sort()
副本上的方法。
现在我们不再试图改变只读数组。
您还可以使用该Array.slice()
方法创建冻结数组的浅表副本。
const arr = ['a', 'b', 'c']; Object.freeze(arr); // 👇️ create a shallow copy of the array const arrCopy = arr.slice(); arrCopy.sort(); console.log(arrCopy); // 👉️ [ 'a', 'b', 'c' ]
当slice()
不带任何参数调用该方法时,它返回原始数组的浅表副本。
设置writable
为true
如果使用Object.defineProperties()
如果您在使用Object.defineProperties方法时遇到错误
,请将属性设置为writable
if 您想要更改其值。
const obj2 = {}; Object.defineProperties(obj2, { country: { value: 'Germany', writable: true, // 👈️ set property to writable }, }); obj2.country = 'Austria'; console.log(obj2.country); // 👉️ "Austria"
该country
属性设置为writable
,因此可以更改。
writable
默认为。false
Object.defineProperties()
如果您希望能够更改属性的值,请显式设置
writable
为true
.
您可能必须在对象上设置的其他属性是configurable
和
enumerable
:
- 可配置 – 如果
false
,则无法删除或更改该属性。默认为false
. - 可枚举 – 如果
true
,则该属性在循环中迭代。默认为
false
. - 可写 – 如果
false
,则不能更改属性的值
const obj2 = {}; Object.defineProperties(obj2, { country: { value: 'Germany', writable: true, // 👈️ set property to writable configurable: true, enumerable: true, }, }); console.log(obj2); obj2.country = 'Austria'; console.log(obj2.country); // 👉️ "Austria"
我们还将enumerable
和configurable
属性设置为true
。
-
configurable
设置为 时true
,可以删除该属性。该属性的默认值为false
。 -
当
enumerable
设置为时true
,该属性将在循环中迭代。该属性的默认值为false
。
将对象传递给Object.freeze
方法
如果将对象或数组传递给Object.freeze
,则不能:
- 向其添加新属性或元素
- 删除现有属性
- 更改现有属性
解决此问题的最佳方法是创建对象或数组的副本并更改副本。
使用带有冻结状态对象和数组的第三方库
如果您使用某些已冻结对象或数组的第三方库state
,您很可能不应该state
直接改变该对象。
该库可能会导出一个应该用于更改state
.
使用 React.js 等库时,不应直接修改状态。
相反,该库提供了可用于更改状态的方法和挂钩。
如果您从库中获取的对象被标记为只读,那么这是有意为之,因为用户不应直接修改它们。
以下是使用 React.js 时如何更改状态数组或状态对象的指南:
结论
要解决“无法分配给对象的只读属性”错误:
- 确保在设置属性之前创建冻结对象或数组的副本。
- 如果您使用 ,请将该
writable
属性设置为。true
Object.defineProperties
- 确保不要修改 React.js 等库中的状态对象,并使用内置方法设置状态。