JavaScript控制对象状态

有时候,需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze

1 Object.preventExtensions()

Object.preventExtensions方法可以使得一个对象无法再添加新的属性。

var obj = new Object();
Object.preventExtensions(obj);

Object.defineProperty(obj, 'p', {
  value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.

obj.p = 1;
obj.p // undefined

上面代码中,obj对象经过Object.preventExtensions以后,就无法添加新属性了。

2 Object.isExtensible()

Object.isExtensible方法用于检查一个对象是否使用了Object.preventExtensions方法。也就是说,检查是否可以为一个对象添加属性。

var obj = new Object();

Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false

上面代码中,对obj对象使用Object.preventExtensions方法以后,再使用Object.isExtensible方法,返回false,表示已经不能添加新属性了。

3 Object.seal()

Object.seal方法使得一个对象既无法添加新属性,也无法删除旧属性。

var obj = { p: 'hello' };
Object.seal(obj);

delete obj.p;
obj.p // "hello"

obj.x = 'world';
obj.x // undefined

上面代码中,obj对象执行Object.seal方法以后,就无法添加新属性和删除旧属性了。
Object.seal实质是把属性描述对象的configurable属性设为false,因此属性描述对象不再能改变了。

var obj = {
  p: 'a'
};

// seal方法之前
Object.getOwnPropertyDescriptor(obj, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Object.seal(obj);

// seal方法之后
Object.getOwnPropertyDescriptor(obj, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: false
// }

Object.defineProperty(o, 'p', {
  enumerable: false
})
// TypeError: Cannot redefine property: p

上面代码中,使用Object.seal方法之后,属性描述对象的configurable属性就变成了false,然后改变enumerable属性就会报错。
Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值。

var obj = { p: 'a' };
Object.seal(obj);
obj.p = 'b';
obj.p // 'b'

上面代码中,Object.seal方法对p属性的value无效,是因为此时p属性的可写性由writable决定。

4 Object.isSealed()

Object.isSealed方法用于检查一个对象是否使用了Object.seal方法。

var obj = { p: 'a' };

Object.seal(obj);
Object.isSealed(obj) // true

这时,Object.isExtensible方法也返回false

var obj = { p: 'a' };

Object.seal(obj);
Object.isExtensible(obj) // false

5 Object.freeze()

Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。

var obj = {
  p: 'hello'
};

Object.freeze(obj);

obj.p = 'world';
obj.p // "hello"

obj.t = 'hello';
obj.t // undefined

delete obj.p // false
obj.p // "hello"

上面代码中,对obj对象进行Object.freeze()以后,修改属性、新增属性、删除属性都无效了。这些操作并不报错,只是默默地失败。如果在严格模式下,则会报错

6 Object.isFrozen()

Object.isFrozen方法用于检查一个对象是否使用了Object.freeze方法。

var obj = {
  p: 'hello'
};

Object.freeze(obj);
Object.isFrozen(obj) // true

使用Object.freeze方法以后,Object.isSealed将会返回trueObject.isExtensible返回false

var obj = {
  p: 'hello'
};

Object.freeze(obj);

Object.isSealed(obj) // true
Object.isExtensible(obj) // false

Object.isFrozen的一个用途是,确认某个对象没有被冻结后,再对它的属性赋值。

var obj = {
  p: 'hello'
};

Object.freeze(obj);

if (!Object.isFrozen(obj)) {
  obj.p = 'world';
}

上面代码中,确认obj没有被冻结后,再对它的属性赋值,就不会报错了。

7 局限性

上面的三个方法锁定对象的可写性有一个漏洞:可以通过改变原型对象,来为对象增加属性。

var obj = new Object();
Object.preventExtensions(obj);

var proto = Object.getPrototypeOf(obj);
proto.t = 'hello';
obj.t
// hello

上面代码中,对象obj本身不能新增属性,但是可以在它的原型对象上新增属性,就依然能够在obj上读到。
一种解决方案是,把obj的原型也冻结住。

var obj = new Object();
Object.preventExtensions(obj);

var proto = Object.getPrototypeOf(obj);
Object.preventExtensions(proto);

proto.t = 'hello';
obj.t // undefined

另外一个局限是,如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容。

var obj = {
  foo: 1,
  bar: ['a', 'b']
};
Object.freeze(obj);

obj.bar.push('c');
obj.bar // ["a", "b", "c"]

上面代码中,obj.bar属性指向一个数组,obj对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值