迹忆客 专注技术分享

当前位置:主页 > 学无止境 > WEB前端 > JavaScript >

JavaScript 中 Cannot assign to read only property of Object 错误

作者:迹忆客 最近更新:2023/02/20 浏览次数:

当我们尝试更改已冻结对象的属性或使用 Object.defineProperties() 定义属性时,会发生错误“Cannot assign to read only property of Object”。

要解决该错误,需要创建对象或数组的副本,或将属性设置为可写。

以下是发生上述错误的 3 个示例。

// 👇️ 使用 OBJECTS 👇️
const obj = {
  name: 'James',
};

Object.freeze(obj);

// ⛔️ Error: Cannot assign to read only property 'name' of object '#<Object>'
obj.name = 'Fql';

// ------------------------------------------------------

// 👇️ 使用 ARRAYS 👇️
const arr = ['a', 'b', 'c'];

Object.freeze(arr);

// ⛔️ Error: Cannot assign to read only property '0' of object '[object Array]'
arr[0] = 'z';

// ------------------------------------------------------


// 👇️ 使用 Object.defineProperties() 👇️
const obj2 = {};

Object.defineProperties(obj2, {
  country: {
    value: 'Germany',
    // 👉️ 必须设置为 writable: true
    // writable: true, // 👈️ 这里取消注释
  },
});

// ⛔️ Error: Cannot assign to read only property 'country' of object '#<Object>'
obj2.country = 'Austria';

前两个错误的发生是因为对象或数组已被 Object.freeze() 方法冻结。

错误的常见原因

“Cannot assign to read only property of object”错误的最常见原因是:

  1. 试图将属性分配给冻结的对象或数组。
  2. 使用 Object.defineProperties 时忘记将 writable 设置为 true
  3. 在使用第三方库(例如 React.js)时尝试就地修改状态对象或数组。

创建数组或对象的副本

无法再更改冻结的对象,但我们可以创建数组或对象的副本并更改副本。

// ✅ 使用 OBJECTS
const obj = {
  name: 'James',
};

Object.freeze(obj);

const objCopy = {...obj}; // 👈️ 创建副本
objCopy.name = 'Fql';
console.log(objCopy); // 👉️ {name: 'Fql'}

// --------------------------------------

// ✅ 使用 ARRAYS
const arr = ['a', 'b', 'c'];

Object.freeze(arr);

const arrCopy = [...arr]; // 👈️ 创建副本
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);

// 👇️ 创建副本
const arrCopy = [...arr];

// 👇️ 对副本排序
arrCopy.sort();

console.log(arrCopy); // 👉️ [ 'a', 'b', 'c' ]

我们使用扩展语法 ... 创建数组的副本,并在副本上调用 sort() 方法。

现在我们不再试图改变只读数组。

我们还可以使用 Array.slice() 方法创建冻结数组的浅表副本。

const arr = ['a', 'b', 'c'];

Object.freeze(arr);

// 👇️ 创建数组的浅表副本
const arrCopy = arr.slice();

arrCopy.sort();

console.log(arrCopy); // 👉️ [ 'a', 'b', 'c' ]

当不带任何参数调用 slice() 方法时,它返回原始数组的浅表副本。


如果使用 Object.defineProperties() ,将 writable 设置为 true

如果在使用 Object.defineProperties 方法时出现错误,如果要更改其值,请将属性设置为可写。

const obj2 = {};

Object.defineProperties(obj2, {
  country: {
    value: 'Germany',
    writable: true, // 👈️ 将属性设置为可写
  },
});

obj2.country = 'Austria';

console.log(obj2.country); // 👉️ "Austria"

country 属性设置为可写,因此可以更改。

使用 Object.defineProperties() 方法时,可写参数默认为 false。

如果我们希望能够更改属性的值,请将 writable 显式设置为 true

我们可能必须在对象上设置的其他属性是可配置和可枚举的:

  • configurable - 如果为 false,则无法删除或更改该属性。 默认为假。
  • enumerable - 如果为 true,则该属性在循环中迭代。 默认为假。
  • writable - 如果为 false,则不能更改属性的值
const obj2 = {};

Object.defineProperties(obj2, {
  country: {
    value: 'Germany',
    writable: true, // 👈️ 将属性设置为可写
    configurable: true,
    enumerable: true,
  },
});

console.log(obj2);

obj2.country = 'Austria';

console.log(obj2.country); // 👉️ "Austria"

我们还将可枚举和可配置属性设置为 true

configurable 设置为 true 时,可以删除该属性。 该属性的默认值为 false

enumerable 设置为 true 时,该属性将在循环中迭代。 该属性的默认值为 false


将对象传递给 Object.freeze 方法

如果将对象或数组传递给 Object.freeze,则不能:

  • 向其添加新属性或元素
  • 删除现有属性
  • 更改现有属性

解决此问题的最佳方法是创建对象或数组的副本并更改副本。


使用带有冻结状态对象和数组的第三方库

如果我们使用某些已冻结状态对象或数组的第三方库,我们很可能不应该直接改变 state 对象。

该库可能会导出一个应该用于更改 state 的方法。

使用 React.js 等库时,不应直接修改状态。

相反,该库提供了可用于更改状态的方法和钩子。

如果我们从库中获取的对象被标记为只读,那么这是有意为之,因为用户不应直接修改它们。

总结

要解决“Cannot assign to read only property of Object”错误:

  1. 确保在设置属性之前创建冻结对象或数组的副本。
  2. 如果我们使用 Object.defineProperties,请将可写属性设置为 true。
  3. 确保不要修改 React.js 等库中的状态对象,并使用内置方法设置状态。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便