Object.preventExtensions()
Object.preventExtensions(obj) 静态方法可以防止新属性被添加到对象中。它还可以防止对象的原型被重新指定。
- 不可扩展对象的属性仍然可以被删除
- 不能用 Object.seal() 和 Object.freeze() 几个其他操作的组合替代
- Reflect.preventExtensions()
- Object.preventExtensions() 只能防止添加自有属性。但其对象类型的原型依然可以添加新的属性。
- 该方法使得目标对象的 [[Prototype]] 不可变,目标对象的其他属性将保持可变。
- 一旦将对象变为不可扩展的对象,就再也不能使其可扩展。
// Object.preventExtensions 将原对象变的不可扩展,并且返回原对象。
const obj = {};
const obj2 = Object.preventExtensions(obj);
obj === obj2; // true
// 字面量方式定义的对象默认是可扩展的。
const empty = {};
Object.isExtensible(empty); // true
// 可以将其改变为不可扩展的。
Object.preventExtensions(empty);
Object.isExtensible(empty); // false
// 使用 Object.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常。
const nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", {
value: 8675309,
}); // 抛出 TypeError
// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出 TypeError 异常。
function fail() {
"use strict";
// 抛出 TypeError
nonExtensible.newProperty = "FAIL";
}
fail();
不可扩展对象的原型是不可变的
const fixed = Object.preventExtensions({});
// 抛出 TypeError
fixed.__proto__ = { oh: "hai" };
非对象参数
非对象参数将被原样返回,因为原始类型是定义上不可变的。
Object.preventExtensions(1);
// 1 (ES2015 code)
Object.isExtensible()
Object.isExtensible(obj) 静态方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
使用 Object.preventExtensions()、Object.seal()、Object.freeze() 或 Reflect.preventExtensions() 中的任一方法将对象标记为不可扩展
// 新对象是可拓展的。
const empty = {};
Object.isExtensible(empty); // true
// 它们可以变为不可拓展的
Object.preventExtensions(empty);
Object.isExtensible(empty); // false
// 根据定义,密封对象是不可拓展的。
const sealed = Object.seal({});
Object.isExtensible(sealed); // false
// 根据定义,冻结对象同样也是不可拓展的。
const frozen = Object.freeze({});
Object.isExtensible(frozen); // false
非对象参数
Object.isExtensible(1);
// false (ES2015 code)
Object.seal()
Object.seal() 静态方法密封一个对象。密封一个对象会阻止其扩展并且使得现有属性不可配置。
- 不能添加新属性
- 不能删除现有属性
- 不能更改枚举性和可配置性
- 不能重新分配其原型
const obj = {
prop() {},
foo: "bar",
};
// 可以添加新属性,可以更改或删除现有属性。
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
const o = Object.seal(obj);
o === obj; // true
Object.isSealed(obj); // true
// 更改密封对象的属性值仍然有效。
obj.foo = "quux";
// 但不能将数据属性转换成访问者属性,反之亦然。
Object.defineProperty(obj, "foo", {
get() {
return "g";
},
}); // 抛出 TypeError
// 现在,除了属性值之外的任何更改都将失败。
obj.quaxxor = "the friendly duck";
// 静默不添加属性
delete obj.foo;
// 静默不添删除属性
// ...且严格模式下,这种尝试将会抛出 TypeError。
function fail() {
"use strict";
delete obj.foo; // 抛出一个 TypeError
obj.sparky = "arf"; // 抛出一个 TypeError
}
fail();
// 尝试通过 Object.defineProperty 添加属性也会抛出错误。
Object.defineProperty(obj, "ohai", {
value: 17,
}); // 抛出 TypeError
Object.defineProperty(obj, "foo", {
value: "eit",
}); // 更改现有属性值
非对象参数
非对象参数将按原样返回,不会有任何错误
Object.seal(1);
// 1 (ES2015 code)
Object.isSealed()
Object.isSealed(obj) 静态方法判断一个对象是否被密封。
Object.preventExtensions + configurable: false
// 新建的对象默认不是密封的。
const empty = {};
Object.isSealed(empty); // false
// 如果你令一个空对象不可扩展,则它同时也会变成个密封对象。
Object.preventExtensions(empty);
Object.isSealed(empty); // true
// 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的。
const hasProp = { fee: "fie foe fum" };
Object.preventExtensions(hasProp);
Object.isSealed(hasProp); // false
// 如果把这个属性变的不可配置,则这个属性也就成了密封对象。
Object.defineProperty(hasProp, "fee", {
configurable: false,
});
Object.isSealed(hasProp); // true
// 密封一个对象最简单的方法当然是 Object.seal。
const sealed = {};
Object.seal(sealed);
Object.isSealed(sealed); // true
// 根据定义,密封对象是不可扩展的。
Object.isExtensible(sealed); // false
// 一个密封对象可能被冻结,但不一定。
Object.isFrozen(sealed); // true
//(所有属性也是不可写的)
const s2 = Object.seal({ p: 3 });
Object.isFrozen(s2); // false
//('p' 仍然可写)
const s3 = Object.seal({
get p() {
return 0;
},
});
Object.isFrozen(s3); // true
//(对于访问器属性,只有可配置性才有影响)
非对象参数
返回 true 而不会产生任何错误
Object.isSealed(1);
// true (ES2015 code)
Object.freeze()
Object.freeze(obj) 静态方法可以使一个对象被冻结。冻结对象可以防止扩展,并使现有的属性不可写入和不可配置。
-
不能添加新的属性
-
不能移除现有的属性
-
不能更改它们的可枚举性、可配置性、可写性或值
-
对象的原型也不能被重新指定
-
数组被冻结后,既不能更改它的元素,也不能向数组中添加或删除元素。
-
对象类型的值仍然可以修改
-
getter 返回的属性值仍然可以更改,setter 可以在设置属性时调用而不抛出错误
-
尝试冻结带有元素的
TypedArray或DataView会导致TypeError,因为它们是内存视图,可能会引起其他问题Object.freeze(new Uint8Array(0)); // 没有元素 // Uint8Array [] Object.freeze(new Uint8Array(1)); // 有元素 // TypeError: Cannot freeze array buffer views with elements Object.freeze(new DataView(new ArrayBuffer(32))); // 没有元素 // DataView {} Object.freeze(new Float64Array(new ArrayBuffer(64), 63, 0)); // 没有元素 // Float64Array [] Object.freeze(new Float64Array(new ArrayBuffer(64), 32, 2)); // 有元素 // TypeError: Cannot freeze array buffer views with elements
冻结对象
const obj = {
prop() {},
foo: "bar",
};
// 冻结前:可以添加新属性,也可以更改或删除现有属性
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
// 冻结。
const o = Object.freeze(obj);
// 返回值和我们传入的对象相同。
o === obj; // true
// 对象已冻结。
Object.isFrozen(obj); // === true
// 现在任何更改都会失败。
obj.foo = "quux"; // 静默但什么都没做
// 静默且没有添加属性
obj.quaxxor = "the friendly duck";
// 严格模式下,这样的尝试会抛出 TypeError
function fail() {
"use strict";
obj.foo = "sparky"; // 抛出 TypeError
delete obj.foo; // 抛出 TypeError
delete obj.quaxxor; // 返回 true,因为属性‘quaxxor’从未被添加过。
obj.sparky = "arf"; // 抛出 TypeError
}
fail();
// 尝试通过 Object.defineProperty 更改;
// 下面的两个语句都会抛出 TypeError。
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });
// 同样无法更改原型
// 下面的两个语句都会抛出 TypeError。
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };
冻结数组
const a = [0];
Object.freeze(a); // 数组现在开始无法被修改
a[0] = 1; // 静默失败
// 严格模式下,这样的尝试将抛出 TypeError
function fail() {
"use strict";
a[0] = 1;
}
fail();
// 尝试在数组末尾追加元素
a.push(2); // 抛出 TypeError
浅冻结
const employee = {
name: "Mayank",
designation: "Developer",
address: {
street: "Rohini",
city: "Delhi",
},
};
Object.freeze(employee);
employee.name = "Dummy"; // 在非严格模式下静默失败
employee.address.city = "Noida"; // 可以修改子对象的属性
console.log(employee.address.city); // "Noida"
function deepFreeze(object) {
// 获取对象的属性名
const propNames = Reflect.ownKeys(object);
// 冻结自身前先冻结属性
for (const name of propNames) {
const value = object[name];
if ((value && typeof value === "object") || typeof value === "function") {
deepFreeze(value);
}
}
return Object.freeze(object);
}
const obj2 = {
internal: {
a: null,
},
};
deepFreeze(obj2);
obj2.internal.a = "anotherValue"; // 非严格模式下会静默失败
obj2.internal.a; // null
Object.isFrozen()
Object.isFrozen(obj) 静态方法判断一个对象是否被冻结。
Object.preventExtensions + writable: false + configurable: false
// 一个新对象是默认是可扩展的,所以它也是非冻结的。
Object.isFrozen({}); // false
// 一个不可扩展的空对象同时也是一个冻结对象。
const vacuouslyFrozen = Object.preventExtensions({});
Object.isFrozen(vacuouslyFrozen); // true
// 一个非空对象默认也是非冻结的。
const oneProp = { p: 42 };
Object.isFrozen(oneProp); // false
// 即使令对象不可扩展,它也不会被冻结,因为属性仍然是可配置的(而且可写的)。
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp); // false
// 此时,如果删除了这个属性,则它会成为一个冻结对象。
delete oneProp.p;
Object.isFrozen(oneProp); // true
// 一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的。
const nonWritable = { e: "plep" };
Object.preventExtensions(nonWritable);
Object.defineProperty(nonWritable, "e", {
writable: false,
}); // 令其不可写
Object.isFrozen(nonWritable); // false
// 把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(nonWritable, "e", {
configurable: false,
}); // 令其不可配置
Object.isFrozen(nonWritable); // true
// 一个不可扩展的对象,拥有一个不可配置但可写的属性,则它也是非冻结的。
const nonConfigurable = { release: "the kraken!" };
Object.preventExtensions(nonConfigurable);
Object.defineProperty(nonConfigurable, "release", {
configurable: false,
});
Object.isFrozen(nonConfigurable); // false
// 把这个属性改为不可写,会让这个对象成为冻结对象。
Object.defineProperty(nonConfigurable, "release", {
writable: false,
});
Object.isFrozen(nonConfigurable); // true
// 一个不可扩展的对象,拥有一个访问器属性,则它仍然是非冻结的。
const accessor = {
get food() {
return "yum";
},
};
Object.preventExtensions(accessor);
Object.isFrozen(accessor); // false
// 把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(accessor, "food", {
configurable: false,
});
Object.isFrozen(accessor); // true
// 使用 Object.freeze 是冻结一个对象最方便的方法。
const frozen = { 1: 81 };
Object.isFrozen(frozen); // false
Object.freeze(frozen);
Object.isFrozen(frozen); // true
// 根据定义,一个冻结对象是不可拓展的。
Object.isExtensible(frozen); // false
// 同样,根据定义,一个冻结对象也是密封对象。
Object.isSealed(frozen); // true
非对象参数
返回 true 而不会出现错误
Object.isFrozen(1);
// TypeError: 1 is not an object(ES5 代码)
Object.isFrozen(1);
// true (ES2015 代码)
| 新增属性 | 删除属性 | 修改原型 | writable | enumerable | configurable | setters 和 getters | |
|---|---|---|---|---|---|---|---|
| Object.preventExtensions() | 不可以 | 可以 | 不可以 | / | / | / | / |
| Object.seal() | 不可以 | 不可以 | 不可以 | / | 不可以 | 不可以 | / |
| Object.freeze() | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | / |
| Object.preventExtensions() | Object.isExtensible() | Object.seal() | Object.isSealed() | Object.freeze() | Object.isFrozen() | |
|---|---|---|---|---|---|---|
| Object.isExtensible() | FALSE | / | FALSE | / | FALSE | / |
| Object.isSealed() | FALSE | / | TRUE | / | TRUE | / |
| Object.isFrozen() | FALSE | / | FALSE | / | TRUE | / |
| Object.preventExtensions() | Object.isExtensible() | Object.seal() | Object.isSealed() | Object.freeze() | Object.isFrozen() | |
|---|---|---|---|---|---|---|
| 非对象参数 | 原样返回 | 返回 false | 原样返回 | 返回 true | 原样返回 | 返回 true |