JavaScript中的bject.defineProperty()和Object.freeze()方法
为了更好的理解Object.defineProperty()和Object.freeze()方法,先回顾一下JavaScript中的常量。
JavaScript常量
在JavaScript中,const关键字用于声明常量,并且也必须在声明时初始化。如果尝试声明一个const常量而不初始化,将会导致一个语法错误。换言之,在JavaScript中,使用const关键字声明的变量是不能被重新赋值的,这意味着它们的值在初始化后不能被改变。下面是一个例子:
const MAX_VALUE = 100;
MAX_VALUE = 200; // 这会抛出一个运行时错误
如果你尝试运行上面的代码,JavaScript会抛出一个错误,因为你试图给一个用const声明的常量重新赋值。
然而,当使用const声明一个对象时,对象的属性值是可以被修改的,因为const仅保证变量名指向的引用不会改变,而不是对象内容的不变性。例如:
const MY_OBJECT = { key: "initial value" };
MY_OBJECT.key = "new value"; // 这是允许的
在上面的例子中,尽管MY_OBJECT是一个常量,我们仍然可以改变它的key属性。
为了创建一个真正不可变的对象,可以使用Object.freeze()方法,或者使用Object.defineProperty()来定义一个不可写的属性。
Object.defineProperty()方法
Object.defineProperty() 是 JavaScript 中用于定义或修改对象属性的方法。使用这个方法,你可以精确地控制属性的特性,例如可枚举性、可配置性、可写性等。Object.defineProperty() - JavaScript | MDN
下面是一个简单的示例:
使用Object.defineProperty()来定义一个不可写的属性。这里是一个使用Object.defineProperty()的例子:
const MAX_VALUE = {};
Object.defineProperty(MAX_VALUE, "value", {
value: 100,
writable: false, // 不能写入
enumerable: true, // 可枚举
configurable: false // 不能配置
});
MAX_VALUE.value = 200; // 这不会有任何效果,因为属性是不可写的
在上面的代码中,MAX_VALUE对象的value属性被设置为不可写,所以尝试修改它将不会有任何效果。这样,你就能在JavaScript中创建一个真正的常量属性。但请注意,Object.defineProperty()是针对对象属性的,而不是变量本身。如果你需要一个不可变的复杂数据结构,你可能需要对对象中的每个属性都使用Object.defineProperty()。
请注意,Object.defineProperty()是针对对象属性的,而不是变量本身。
Object.freeze() 方法
Object.freeze() 方法可以冻结一个对象,这意味着你不能再给这个对象添加新的属性,不能删除已有的属性,也不能修改已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。被冻结的对象是一个不可变的常量对象。Object.freeze() - JavaScript | MDN
下面是一个使用 Object.freeze() 方法的例子:
const MY_OBJECT = {
key: "initial value"
};
// 冻结对象
Object.freeze(MY_OBJECT);
// 尝试修改对象的属性
MY_OBJECT.key = "new value"; // 这不会有任何效果,因为对象被冻结了
// 尝试添加新的属性
MY_OBJECT.newKey = "new value"; // 这也不会有任何效果
// 尝试删除属性
delete MY_OBJECT.key; // 这不会有任何效果
console.log(MY_OBJECT); // 输出: { key: "initial value" }
在上面的代码中,尽管我们尝试修改了 MY_OBJECT 的属性、添加新属性和删除属性,但是由于对象已经被 Object.freeze() 冻结,这些操作都不会对对象产生任何影响。
需要注意的是,Object.freeze() 只能冻结对象的直接属性,如果对象的属性值是另一个对象,那么这个嵌套对象的属性仍然是可以被修改的。如果你需要深度冻结一个对象(即冻结对象及其所有嵌套对象),你需要递归地使用 Object.freeze()。下面是一个深度冻结的例子:
function deepFreeze(object) {
// 取得对象的属性名
const propNames = Object.getOwnPropertyNames(object);
// 在冻结自身之前,先冻结每个属性
propNames.forEach(function(name) {
const prop = object[name];
// 如果属性是对象或数组,则递归冻结
if (typeof prop == 'object' && prop !== null) {
deepFreeze(prop);
}
});
// 冻结自身 (无法修改、删除、添加属性)
return Object.freeze(object);
}
const MY_COMPLEX_OBJECT = {
subObject: {
key: "value"
}
};
// 深度冻结对象
deepFreeze(MY_COMPLEX_OBJECT);
// 尝试修改嵌套对象的属性
MY_COMPLEX_OBJECT.subObject.key = "new value"; // 这不会有任何效果
console.log(MY_COMPLEX_OBJECT); // 输出: { subObject: { key: "value" } }
在这个例子中,deepFreeze 函数递归地冻结了 MY_COMPLEX_OBJECT 中的所有对象,包括嵌套的 subObject 对象。因此,尝试修改嵌套对象的属性也不会有任何效果。