Object.freeze()
Object.freeze()
静态方法冻结一个对象。冻结一个对象会阻止扩展并使现有属性不可写且不可配置。冻结的对象无法再更改:无法添加新属性,无法删除现有属性,无法更改其可枚举性、可配置性、可写性或值,并且无法重新分配对象的原型。freeze()
返回传入的相同对象。
冻结对象是 JavaScript 提供的最高完整性级别。
试一试
语法
Object.freeze(obj)
参数
obj
-
要冻结的对象。
返回值
传递给函数的对象。
描述
冻结对象等效于阻止扩展,然后将所有现有属性的描述符的configurable
更改为false
——对于数据属性,也将writable
更改为false
。无法向冻结对象的属性集添加或从中删除任何内容。任何尝试这样做都会失败,要么静默失败,要么抛出TypeError
异常(最常见,但并非唯一,在严格模式下)。
对于冻结对象的 数据属性,由于writable
和configurable
属性设置为false
,因此无法更改其值。访问器属性(getter 和 setter)的工作方式相同——getter 返回的属性值可能仍然会更改,并且在设置属性时,调用 setter 仍然不会抛出错误。请注意,除非对象也被冻结,否则对象的值仍然可以修改。作为对象,数组可以被冻结;这样做后,其元素将无法更改,并且无法向数组添加或从中删除元素。
私有属性没有属性描述符的概念。冻结包含私有属性的对象不会阻止这些私有属性的值被更改。(冻结对象通常是为了防止外部代码进行安全措施,但外部代码无论如何都无法访问私有属性。)无论对象是否冻结,都无法向对象添加或从中删除私有属性。
freeze()
返回传入函数的相同对象。它不会创建冻结的副本。
TypedArray
或具有元素的DataView
将导致TypeError
,因为它们是内存上的视图,并且肯定会导致其他可能的问题
Object.freeze(new Uint8Array(0)); // No elements
// Uint8Array []
Object.freeze(new Uint8Array(1)); // Has elements
// TypeError: Cannot freeze array buffer views with elements
Object.freeze(new DataView(new ArrayBuffer(32))); // No elements
// DataView {}
Object.freeze(new Float64Array(new ArrayBuffer(64), 63, 0)); // No elements
// Float64Array []
Object.freeze(new Float64Array(new ArrayBuffer(64), 32, 2)); // Has elements
// TypeError: Cannot freeze array buffer views with elements
请注意,由于标准的三个属性(buf.byteLength
、buf.byteOffset
和buf.buffer
)是只读的(ArrayBuffer
或SharedArrayBuffer
也是如此),因此没有理由尝试冻结这些属性。
与Object.seal()
不同,使用Object.freeze()
冻结的对象中的现有属性将变为不可变,并且数据属性无法重新赋值。
示例
冻结对象
const obj = {
prop() {},
foo: "bar",
};
// Before freezing: new properties may be added,
// and existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
// Freeze.
const o = Object.freeze(obj);
// The return value is just the same object we passed in.
o === obj; // true
// The object has become frozen.
Object.isFrozen(obj); // === true
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
// silently doesn't add the property
obj.quaxxor = "the friendly duck";
// In strict mode such attempts will throw TypeErrors
function fail() {
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.foo; // throws a TypeError
delete obj.quaxxor; // returns true since attribute 'quaxxor' was never added
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty;
// both statements below throw a TypeError.
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });
// It's also impossible to change the prototype
// both statements below will throw a TypeError.
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };
冻结数组
const a = [0];
Object.freeze(a); // The array cannot be modified now.
a[0] = 1; // fails silently
// In strict mode such attempt will throw a TypeError
function fail() {
"use strict";
a[0] = 1;
}
fail();
// Attempted to push
a.push(2); // throws a TypeError
被冻结的对象是不可变的。但是,它不一定是常量。以下示例显示冻结的对象不是常量(冻结是浅层的)。
const obj1 = {
internal: {},
};
Object.freeze(obj1);
obj1.internal.a = "aValue";
obj1.internal.a; // 'aValue'
要成为一个常量对象,整个引用图(对其他对象的直接和间接引用)必须仅引用不可变的冻结对象。被冻结的对象被称为不可变的,因为整个对象状态(值和对其他对象的引用)在整个对象中都是固定的。请注意,字符串、数字和布尔值始终是不可变的,并且函数和数组是对象。
深度冻结
调用Object.freeze(object)
的结果仅适用于object
本身的直接属性,并且只会阻止在object
上进行将来的属性添加、删除或值重新赋值操作。如果这些属性的值本身是对象,则这些对象不会被冻结,并且可能是属性添加、删除或值重新赋值操作的目标。
const employee = {
name: "Mayank",
designation: "Developer",
address: {
street: "Rohini",
city: "Delhi",
},
};
Object.freeze(employee);
employee.name = "Dummy"; // fails silently in non-strict mode
employee.address.city = "Noida"; // attributes of child object can be modified
console.log(employee.address.city); // "Noida"
要使对象不可变,请递归冻结每个非基本类型属性(深度冻结)。当您知道对象在引用图中不包含任何循环时,请根据您的设计在逐案的基础上使用此模式,否则将触发无限循环。例如,使用function
语法创建的函数具有一个prototype
属性,该属性具有一个指向函数本身的constructor
属性,因此它们默认具有循环。其他函数,例如箭头函数,仍然可以被冻结。
对deepFreeze()
的增强是存储它已经访问过的对象,以便在对象正在变得不可变的过程中,您可以抑制递归调用deepFreeze()
。例如,请参阅使用WeakSet
检测循环引用。您仍然存在冻结不应该冻结的对象的风险,例如window
。
function deepFreeze(object) {
// Retrieve the property names defined on object
const propNames = Reflect.ownKeys(object);
// Freeze properties before freezing self
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"; // fails silently in non-strict mode
obj2.internal.a; // null
规范
规范 |
---|
ECMAScript 语言规范 # sec-object.freeze |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。