JS `Object.seal()` 与 `Object.preventExtensions()`:控制对象的可扩展性

咳咳,各位同学,咱们今天来聊聊JavaScript对象控制欲的问题,啊不,是可扩展性的问题。具体来说,我们要深入了解一下 Object.seal()Object.preventExtensions() 这两位“对象界包青天”,看看它们如何为对象伸张正义,维护对象秩序,防止对象被随意“改造”。

首先,咱们得明确一个概念:什么是对象的可扩展性?

在JavaScript里,对象就像一块橡皮泥,默认情况下,你可以随意捏它,加属性、删属性、改属性,随心所欲。这种“捏橡皮泥”的行为,就叫做对象的可扩展性。但是,有时候我们希望对象能保持“原汁原味”,不希望别人乱动它,这时候就需要用到 Object.seal()Object.preventExtensions() 了。

一、 Object.preventExtensions():只许进,不许出!

Object.preventExtensions() 的作用非常简单粗暴:禁止给对象添加新的属性。就像在对象门口立了个牌子,上面写着“此路不通,禁止添加新属性!”。

咱们来看个例子:

let obj = {
  name: '张三',
  age: 30
};

Object.preventExtensions(obj);

obj.address = '北京'; // 试图添加新属性

console.log(obj.address); // 输出 undefined

delete obj.name; // 可以删除现有属性

console.log(obj.name); // 输出 undefined

obj.age = 31; // 可以修改现有属性

console.log(obj.age); // 输出 31

从上面的例子可以看出,使用 Object.preventExtensions() 之后:

  • 不能添加新属性:试图添加 address 属性失败,因为 obj.address 的值为 undefined
  • 可以删除现有属性name 属性可以成功删除。
  • 可以修改现有属性age 属性可以成功修改。

所以,Object.preventExtensions() 就像一个门卫,只管阻止新属性的进入,对现有属性的修改和删除则睁一只眼闭一只眼。

注意事项:

  • Object.preventExtensions() 只能阻止对象本身添加新属性,不能阻止对象原型链上的属性添加。
  • 在严格模式下,试图给不可扩展的对象添加新属性会抛出 TypeError 错误。

二、 Object.seal():内外兼修,固若金汤!

Object.seal()Object.preventExtensions() 更加严格,它不仅禁止添加新属性,还禁止删除现有属性,并将所有现有属性的 configurable 特性设置为 false。这意味着,你不仅不能添加新属性,也不能删除现有属性,甚至连现有属性的 configurable 特性都不能修改了。

configurable 特性是干嘛的?简单来说,它决定了属性是否可以被删除,以及属性的特性是否可以被修改。如果 configurablefalse,则该属性无法被删除,也无法被重新配置(比如改变它的 writableenumerable 等特性)。

咱们再来看个例子:

let obj = {
  name: '李四',
  age: 25
};

Object.seal(obj);

obj.address = '上海'; // 试图添加新属性

console.log(obj.address); // 输出 undefined

delete obj.name; // 试图删除现有属性

console.log(obj.name); // 输出 '李四'

obj.age = 26; // 可以修改现有属性

console.log(obj.age); // 输出 26

Object.defineProperty(obj, 'name', {
  configurable: true // 试图修改 configurable 特性
}); // 在严格模式下会抛出 TypeError,非严格模式下静默失败

console.log(Object.getOwnPropertyDescriptor(obj, 'name').configurable); // 输出 false

从上面的例子可以看出,使用 Object.seal() 之后:

  • 不能添加新属性:试图添加 address 属性失败。
  • 不能删除现有属性:试图删除 name 属性失败。
  • 可以修改现有属性的值age 属性可以成功修改。
  • 不能修改现有属性的 configurable 特性:试图修改 name 属性的 configurable 特性失败。

所以,Object.seal() 就像给对象穿上了一层盔甲,让它刀枪不入,固若金汤。

注意事项:

  • Object.seal() 只能作用于对象本身,不能阻止对象原型链上的属性被修改或删除。
  • 在严格模式下,试图删除或重新配置一个 configurablefalse 的属性会抛出 TypeError 错误。

三、 Object.isExtensible()Object.isSealed():探查虚实,明察秋毫!

既然我们有 Object.preventExtensions()Object.seal() 这两把利剑来保护对象,那么如何判断一个对象是否已经被保护起来了呢? 这时候就需要用到 Object.isExtensible()Object.isSealed() 这两个侦察兵了。

  • Object.isExtensible(obj):判断对象 obj 是否可扩展,如果可以添加新属性,则返回 true,否则返回 false
  • Object.isSealed(obj):判断对象 obj 是否已经密封,如果已经密封,则返回 true,否则返回 false

咱们来看个例子:

let obj1 = { name: '王五' };
let obj2 = { name: '赵六' };

Object.preventExtensions(obj1);
Object.seal(obj2);

console.log(Object.isExtensible(obj1)); // 输出 false
console.log(Object.isExtensible(obj2)); // 输出 false

console.log(Object.isSealed(obj1)); // 输出 false
console.log(Object.isSealed(obj2)); // 输出 true

从上面的例子可以看出:

  • obj1Object.preventExtensions() 处理后,Object.isExtensible(obj1) 返回 false,表示 obj1 不可扩展。
  • obj2Object.seal() 处理后,Object.isSealed(obj2) 返回 true,表示 obj2 已经密封。

四、 总结:选择合适的“保护策略”

Object.preventExtensions()Object.seal() 都是用来保护对象的,但它们保护的程度不同。选择哪一个,取决于你的具体需求。

咱们用一个表格来总结一下它们的区别:

特性 Object.preventExtensions() Object.seal()
禁止添加新属性
禁止删除现有属性
修改现有属性值
修改属性的 configurable 特性

什么时候使用 Object.preventExtensions()

  • 当你希望对象保持现有属性不变,但允许修改现有属性的值时。
  • 例如,你可能希望创建一个配置对象,该对象包含一些默认配置项,你不希望别人添加新的配置项,但允许修改现有配置项的值。

什么时候使用 Object.seal()

  • 当你希望对象完全保持不变,不允许添加、删除或重新配置属性时。
  • 例如,你可能希望创建一个常量对象,该对象包含一些固定的常量值,你不希望别人修改这些常量值,或者添加、删除新的常量。

五、 深入思考:Object.freeze() 的乱入

除了 Object.preventExtensions()Object.seal() 之外,JavaScript还有一个 Object.freeze() 方法,它的作用比 Object.seal() 更加严格。

Object.freeze() 不仅禁止添加、删除和重新配置属性,还禁止修改现有属性的值。也就是说,Object.freeze() 会将对象变成一个真正的“冰冻状态”,任何试图修改对象的操作都会失败。

let obj = {
  name: '冻梨',
  age: 18
};

Object.freeze(obj);

obj.name = '冰糖葫芦'; // 试图修改属性值

console.log(obj.name); // 输出 '冻梨'

delete obj.age; //试图删除属性

console.log(obj.age); // 输出 18

obj.address = '东北'; //试图增加属性

console.log(obj.address); // 输出 undefined

Object.freeze() 就像把对象封印起来,彻底禁止任何修改。

Object.preventExtensions()Object.seal()Object.freeze() 的比较:

特性 Object.preventExtensions() Object.seal() Object.freeze()
禁止添加新属性
禁止删除现有属性
修改现有属性值
修改属性的 configurable 特性
修改属性的 writable 特性 否 (设置为 false)

六、 总结与实战

掌握 Object.preventExtensions()Object.seal()Object.freeze() 可以帮助我们更好地控制对象的行为,防止对象被意外修改,提高代码的健壮性和安全性。

在实际开发中,我们可以根据具体需求选择合适的保护策略。例如,在开发一个框架或库时,可以使用 Object.freeze() 来保护核心对象,防止用户修改这些对象。或者,在开发一个配置对象时,可以使用 Object.seal() 来防止用户添加或删除配置项,但允许用户修改现有配置项的值。

总之,合理运用 Object.preventExtensions()Object.seal()Object.freeze(),可以让我们的代码更加健壮、安全、可维护。

好了,今天的讲座就到这里,希望大家有所收获! 下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注