一、不可扩展对象

 默认情况下,所有对象都是可以拓展的。也就是说,任何时候都可以向面对对象中添加属性和方法:

let person = {"name": "xiaoMing"};
person.age = 18;
console.log(person); // {name: 'xiaoMing', age: 18}

根据上面的示例我们可以看到,即使第一行已经完整定义了person对象,但第二行仍然可以给它添加属性。

现在,我们可以使用Object.preventExtensions()方法来改变这个行为,防止他人再给person添加属性和方法:

let person = {"name": "xiaoMing"};
Object.preventExtensions(person);
person.age = 18;
console.log(person); // {name: 'xiaoMing'}

在非严格模式下,给不可扩展对象添加属性和方法会默认失败。而在严格模式下,给不可扩展对象添加属性和方法则会导致抛出错误。

不可扩展对象虽然不能添加属性和方法,但是能够修改和删除已有的成员。另外,我们可以通过Object.isExtensible()来确定对象是否可以扩展:

let person = {"name": "xiaoMing"};
console.log(Object.isExtensible(person)); // true

Object.preventExtensions(person);
console.log(Object.isExtensible(person)); // false

二、密封对象

 密封对象不可扩展,而且其已有成员的[[Configurable]]特性被设置成了false。这就意味着不能删除其属性和方法,仅能修改属性值。要密封对象,可以使用Object.seal()方法:

let person = {"name": "xiaoMing"};
Object.seal(person);

person.age = 18;
console.log(person); // {name: 'xiaoMing'}

delete person.name;
console.log(person); // {name: 'xiaoMing'}

在非严格模式下,添加age属性的行为被忽略,而尝试删除name属性的操作也被忽略,person对象未受到任何影响。而在严格模式下,尝试添加或删除对象的成员都会导致抛出错误。

使用Object.isSealed()方法可以确定对象是否被密封,另外由于密封对象不可被扩展,所以使用Object.isExtensible()监测被密封的对象也会返回false:

let person = {"name": "xiaoMing"};
console.log(Object.isExtensible(person)); // true
console.log(Object.isSealed(person)); // false

Object.freeze(person);
console.log(Object.isExtensible(person)); // false
console.log(Object.isSealed(person)); // true

三、冻结的对象

最严格的防篡改级别是冻结对象。冻结对象既不可扩展,又是密封的,而且对象数据属性的[[Writable]]特性会被设置为false。如果定义[[Set]]函数,访问器属性任然是可写的。使用Object.freeze()方法可以来冻结对象。

let person = {"name": "xiaoMing"};
Object.freeze(person);

person.age = 18;
console.log(person); // {name: 'xiaoMing'}

delete person.name;
console.log(person); // {name: 'xiaoMing'}

person.name = "xiaoHong";
console.log(person); // {name: 'xiaoMing'}

与不可扩展及密封一样,在非严格模式下,对冻结对象执行非法操作都会被忽略。而在严格模式下则会导致抛出错误。

使用Object.isFrozen()可以检测对象是否被冻结。而且因为冻结对象既是密封又是不可扩展的,所以用Object.isExtensible()和Object.isSealed()检测冻结对象将分别返回false和true:

let person = {"name": "xiaoMing"};
console.log(Object.isExtensible(person)); // true
console.log(Object.isSealed(person)); // false
console.log(Object.isFrozen(person)); // false

Object.freeze(person);
console.log(Object.isExtensible(person)); // false
console.log(Object.isSealed(person)); // true
console.log(Object.isFrozen(person)); // true

对于JS库的作者而言,冻结对象是很有必要的。因为js库最怕有人意外(或有意)的修改库中的核心对象。冻结(或密封)主要的库对象能够防止这些问题的发生。

参考:

《JavaScript高级程序设计》第3版