时间:2022-08-19 09:51:21 | 栏目:JavaScript代码 | 点击:次
对象不变性在任何编程语言中都是一个重要的概念。它会限制对象修改并防止不需要的更改。简而言之,对象的不变性就是将它的状态变为只读的,下面就来看看在 JavaScript 中的对象不变性。?
在JavaScript
中,一个对象可以有一个或多个属性。每个属性都是一个键值对,
下面是一个对象:
const user = { name: 'CUGGZ', age: 24, }
这里使用const
关键字定义了一个对象,它具有两个属性。默认情况下,对象是可变的,也就是说,我们可以给对象添加新属性,修改现有属性或者删除已有属性。而在某些情况下,我们可能希望对象是不可变的,即不能添加新属性,也不能修改和删除已有属性。
顾名思义,freeze()
就是用来冻结对象的。只需要将不想被更改的对象传递给它,就会返回该对象的不可变版本:
const user = { name: 'CUGGZ', age: 24, } const freezeUser = Object.freeze(user); freezeUser.age = 18; console.log(freezeUser.age) // 24
这时,新的对象就不可变了,相当于被冻结了。?
在JavaScript
了,提供了一个Object.isFrozen()
,它可以用来判断一个对象是否被冻结:
Object.isFrozen(user) // false Object.isFrozen(freezeUser) // true
需要注意的是, Object.freeze()
方法不会影响嵌套对象,对于嵌套对象,冻结后仍然是可以操作的:
const user = { name: 'CUGGZ', age: 24, article: { title: 'learn javascript', number: 1234 } } const freezeUser = Object.freeze(user); freezeUser.age = 18 freezeUser.article.number = 666; console.log(freezeUser.age) // 24 console.log(freezeUser.article.number); // 666
可以看到,使用Object.freeze()
方法冻结的对象,age是不可以更改的,但是嵌套对象的number
属性还是可以修改的。如果需要冻结嵌套对象使其不可变,就需要使用循递归来逐级冻结,下面是一个简单的递归冻结实现:
const deepFreeze = obj => { Object.keys(obj).forEach(prop => { if (typeof obj[prop] === 'object') { deepFreeze(obj[prop]); } }); return Object.freeze(obj); }; deepFreeze(user);
Object.freeze()
方法除了可以用来冻结对象以外,还可以用于冻结数组,使其不可变:
const number = [1, 2, 3, 4, 5]; const freezeNumber = Object.freeze(number); freezeNumber.push(6);
此时就会报错了:
VM210:3 Uncaught TypeError: Cannot add property 5, object is not extensible
Object.seal()
顾名思义就是密封对象,它是另一种使对象不可变的方法。相对于freeze(),Object.seal() 方法仅保护对象不能添加和删除属性,它允许更新现有的属性。
const user = { name: 'CUGGZ', age: 24, } const sealUser = Object.seal(user); sealUser.age = 18; delete sealUser.name; console.log(sealUser) // {name: 'CUGGZ', age: 18}
可以看到,我们识图删除对象中的name
属性,删除失败;而修改已有的age属性,修改成功。?
Object.seal()
方法和Object.freeze()
一样,对于嵌套的对象,是无法实现不可变的,如果想要实现,同样要使用递归来一层层来密封对象。?
JavaScript同样提供了一个Object.isSealed()
方法来确认对象的密封状态:
Object.isSealed(user) // false Object.isSealed(sealUser) // true
你是否会认为,使用const关键字也能达到相同的结果呢?实际上,他们是不一样的,当我们使用const
关键字来创建对象时,它会阻止我们重新分配值,但是我们可以更新、添加、删除已有对象的属性:
const user = { name: 'CUGGZ', age: 24, } delete user.age; user.height = 180; user.name = 'hello'; console.log(user); // {name: 'hello', height: 180}
而如果我们给user重新赋值,那么就会报错了:
Uncaught TypeError: Assignment to constant variable.
因此,const关键字仅仅是提供了赋值的不变性,而不会提供值的不变性(对于对象来说)。
本文简单介绍了两种可以用于使JavaScript
不可变的方法。简而言之,Object.seal()
方法会阻止更新、删除和向对象添加新属性,Object.seal()只会阻止添加和删除属性。、
除此之外,JavaScript
还提供了一个Object.preventExtensions()
方法,该方法可以让一个对象变的不可扩展,也就是永远不能再添加新的属性。
const user = { name: 'CUGGZ', age: 24, } const newUser = Object.preventExtensions(user); newUser.height = 180; console.log(newUser); // {name: 'CUGGZ', age: 24}
最后来看看它们三个的对比:
方法/操作 | 读取 | 创建 | 更新 | 删除 |
---|---|---|---|---|
Object.freeze() | ?? | ? | ? | ? |
Object.seal() | ?? | ? | ?? | ? |
Object.preventExtensions() | ?? | ? | ?? | ?? |