Javascript不可变对象

本文介绍了JavaScript中不可变对象的概念,包括不可扩展、密封和冻结三种方式。通过Object.preventExtensions、Object.seal和Object.freeze方法,可以防止对象被添加新属性、修改或删除属性。这些操作具有不同程度的限制,并且都是浅层操作。文章还提供了相应的检测方法和属性描述符变化的分析,帮助理解不同操作的差异和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可变对象

我们知道,JavaScript中对象是弱类型的。一般情况下,可以不受限制的为对象添加属性,修改属性,删除属性。大部分情况下,我们使用的都是可变对象。

不可变对象

对应的,我们不希望代码中某些对象被任意修改,比如添加、修改、删除等。这就是我们的不可变对象。JavaScript为我们提供了一些原生方法,借助它们可以讲一些可变对象转变成不可变对象。一共有三种:不可扩展,密封,冻结。

不可扩展

如果一个对象可以添加新的属性,则这个对象是可扩展的。相反,不可以添加新属性时就是不可扩展对象。我们可以使用 preventExtensions 让这个对象变的不可扩展。当对象不可扩展时,添加属性将静默失败或者 TypeError(严格模式下)。

function Person(name) {
	this.name = name;
}
var kaka = new Person('kaka')
Object.preventExtensions(kaka);
kaka.secondName = 'zhang';
// 非严格模式下,静默失败
// 'use strict' 下 TypeError: Can't add property secondName, object is not extensible
检测

JS 提供了 Object.isExtensible 方法帮我们检测对象是否可扩展。

Object.isExtensible(kaka) // false, kaka为不可扩展对象
浅层

我们知道,JavaScript中对象的赋值有浅复制和深复制。同样,这里也有。

// 浅层 prevent
var kaka = {
	name : 'qiqi',
	info: {
		age: 22,
		height: 170
	}
}
var preventedkaka = Object.preventExtensions(kaka);
preventedkaka.info.sex = 'male';    // 可以扩展深层的属性
可以发现,JavaScript只阻止浅层的扩展,引用属性的扩展不受影响。因此,可以想象。不可扩展对象 __proto__ 是不能重新指向对象的,但是可以为原型添加属性。
而且,将对象变成不可扩展的对象是不可逆的。


密封

Object.seal(obj) 方法可以让一个对象密封,并返回被密封后的对象。密封对象将会阻止向对象添加新的属性。另外也会 改变属性的 configurable 描述。

看代码

function Person(name) {
	this.name = name;
}
var kaka = new Person('kaka');
kaka.secondName = 'sb'
Object.getOwnPropertyDescriptor(kaka, 'secondName')
var secondJo = Object.seal(kaka)
delete secondJo.secondName // false, 删除失败,严格模式下,TypeError

Object.defineProperty(kaka, "age", { value: 22 })// TypeError: Can't add property secondName, object is not extensible

也就是说,当对象被密封后,也不能对其进行添加属性。


从上面可看,两个属性的 configurable 描述都已经改变。configurable 决定属性能否被 delete

检测

对应的,我们可以通过 Object.isSealed(jo) 来判断对象是否密封了。如果这个对象是密封的,则返回 true,否则返回 false。

Object.isSealed(jo) // true

另外,最好不要对原始类型进行检测,ES6对着有了变化。

// 对比
// ES5
Object.isSealed(1); // TypeError: 1 is not an object 

// ES6
Object.isSealed(1); // true 

浅密封

值得一说的是,密封也是浅层属性的密封。

var kaka = {
	name : 'qiqi',
	info: {
		age: 22,
		height: 170
	}
}
var sealedKaka = Object.seal(kaka);
Object.isSealed(sealedKaka.info) // false, 只密封了当前对象,引用属性的对象没有被密封。

冻结

Object.freeze() 方法可以冻结一个对象。冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。

function Person(name) {
	this.name = name;
}
var jo = new Person('jo');
Object.getOwnPropertyDescriptor(jo, 'name')

var freezedJo = Object.freeze(jo)
Object.getOwnPropertyDescriptor(jo, 'name')
Object.getOwnPropertyDescriptor(freezedJo, 'name')
freezedJo === jo // 其实是同一个对象
对比下,冻结前后的描述符。


从上面可以看出,冻结会改变 所有属性的 configurable 和 writable 两个描述符。

检测

Object.isFrozen(freezedJo)// true


浅冻结
跟上面一样,对于有引用属性的对象,只能冻结当前对象的原始属性。

var kaka = {
	name : 'qiqi',
	info: {
		age: 22,
		height: 170
	}
}
var freezedkaka = Object.freeze(kaka)
Object.isFrozen(freezedkaka.info) // false

总结

总的来说构建一个不可变对象,有上面三种方式。具体使用哪种可以根据需要。

相同点

1. 操作并返回操作对象,且操作不可逆的

2. 均有对应的检查函数

3. 均不能添加新属性,严格模式下 TypeError

4. 均是浅层操作,不影响引用类型

不同点

三种方式还是有程度上的区别的。下面用一个属性的描述符上来对比下。

var obj = {
	name: 'kaka'
}

下面是操作后 name 属性描述符的影响

属性描述符defaultpreventExtensionssealfreeze
writabletruetruefalsefalse
configurabletruetruetruefalse
enumerabletruetruetruetrue

从上面看,三个操作对对象及其属性的限制是逐渐变强的。实际使用需要哪个可以根据需要选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值