冻结和密封之间的区别


164

我刚刚听说过JavaScript方法freezeseal,它们可用于使任何对象不可变。

这是一个简短的示例如何使用它:

var o1 = {}, o2 = {};
Object.freeze(o2);

o1["a"] = "worked";
o2["a"] = "worked";

alert(o1["a"]);   //prints "worked"
alert(o2["a"]);   //prints "undefined"

freeze和之间有什么区别seal?它们可以提高性能吗?


6
只是给任何看这个问题的人一个注释,被接受的答案实际上是错误的。@tungd的答案是正确的。
比约恩

2
另一个注意事项是,Object.preventExtensions除了Object.sealObject.freezeObject.preventExtensions只是防止将新项目添加到对象。您可以删除,配置和更改通过禁用可扩展性的对象的属性值Object.preventExtensions
比约恩2014年

Answers:


192

Object.seal

  • 它可以防止增加和/或去除密封物体的性能;使用delete将返回false
  • 它使每个现有属性都不可配置:它们无法从“数据描述符”转换为“访问器描述符”(反之亦然),并且访问器描述符的属性根本无法修改(而数据描述符可以更改其writable属性,并且其value属性(如果writeable为true)。
  • 可以扔一个 TypeError尝试修改密封对象本身的值时会(通常在严格模式下

Object.freeze

  • 究竟是什么Object.seal,再加上:
  • 它防止修改任何现有属性

都不会影响“深” /孙子对象。例如,如果obj冻结,obj.el则无法重新分配,但是obj.el可以修改的值,例如obj.el.id可以更改。


性能:

密封或冻结对象可能会影响其枚举速度,具体取决于浏览器:

  • Firefox:枚举性能不受影响
  • IE:枚举性能的影响可以忽略不计
  • Chrome:对于密封或冻结的对象,枚举性能更快
  • Safari:密封或冻结的物体枚举速度降低了92%(截至2014年)

测试:密封物体冷冻物体


2
您能谈谈为什么我们会使用这些方法吗?仅仅因为我们可以吗?
艾伦·董

3
将来,我认为在开发库/框架时,它们将被大量使用(如果正确优化)。它们使您可以防止用户(甚至是无意间)破坏您的代码(并且,如答案中所述,优化应该可以大大提高速度)。但是,这是纯粹的猜测:)
尼可罗马基Campolungo

2
这个答案有许多事实错误。首先,seal它还会使现有属性不可配置,请参阅jsfiddle.net/btipling/6m743whn数字2,您仍然可以进行编辑,即更改密封对象上现有属性的值。
比约恩2014年

8
FWIW,冷冻和密封的对象现在比Chrome Canary v43.0.2317.0中的未冷冻和未密封的对象快。
llambda

2
@AlanDong来晚了一点,但这就是为什么要锁定对象的原因。JavaScript的功能之一是您可以随时添加属性。您也可以通过错误键入意外地执行此操作。我的许多学生都尝试添加名为onClick或的事件处理程序,onlick并想知道为什么它不起作用。如果JavaScript引发错误,那么出错就少了一件。其次,这允许您对防止更改的对象实施常量属性。这在对象方法上特别有用。
芒果

119

我编写了一个测试项目,比较了这三种方法:

  • Object.freeze()
  • Object.seal()
  • Object.preventExtensions()

我的单元测试涵盖了CRUD案例:

  • [C]新增属性
  • [R]读取现有属性
  • [U]修改现有属性
  • [D]删除现有财产

结果:

在此处输入图片说明


2
太好了 UPDATE是否考虑(通过defineProperty)修改描述符属性,例如可配置,可枚举,可写?
德雷奈

我总是尽管应该将DOM对象密封(当然在polyfill之后)。这将有助于防止很多错字。
曼戈

@Manngo您可以密封DOM对象。只需创建一个DEBUGMODE变量并将其设置为true。然后,执行if (DEBUGMODE) { ... }。在中...,放上确保始终密封所有DOM对象的功能。然后,当您准备分发网页脚本时,请更改DEBUGMODEfalse,通过闭包编译器运行脚本,然后进行分发。就这么简单。
杰克·吉芬

@JackGiffin感谢您的评论。我只是说我一直认为这是个好主意。我有很多学生最终输入类似的内容element.onlick=something并因为无法使用而感到沮丧,但这在技术上不是错误。
曼戈

2
@寂寞然后它不会拼写CRUD。您必须适应像RUDE之类的东西;)
Manngo

84

您始终可以在MDN中查找这些内容。简而言之:

  • 冻结:使对象不可变,这意味着不允许更改已定义的属性,除非它们是对象。
  • 密封:防止添加属性,但是仍然可以更改定义的属性。

1
Object.seal()也似乎冻结原型属性:\
K。。

10

Object.freeze()创建一个冻结的对象,这意味着它需要一个现有的对象并从本质Object.seal()上对其进行调用,但是它还将所有“数据访问器”属性标记为writable:false,因此它们的值不能更改。-Kyle Simpson,您不懂JS-此和对象原型


4

我正在研究ECMAScript 5中的Freeze和Seal之间的差异,并创建了一个脚本来阐明差异。冻结会创建一个包含数据和结构的不可变对象。Seal阻止更改命名的接口-不能添加,删除-但是您可以对对象进行突变并重新定义其接口的含义。

function run()
{
    var myObject = function() 
    { 
        this.test = "testing"; 
    }

    //***************************SETUP****************************

    var frozenObj = new myObject();
    var sealedObj = new myObject();

    var allFrozen = Object.freeze(frozenObj);
    var allSealed = Object.seal(sealedObj);
    alert("frozenObj of myObject type now frozen - Property test= " + frozenObj.test);
    alert("sealedObj of myObject type now frozen - Property test= " + sealedObj.test);

    //***************************FROZEN****************************

    frozenObj.addedProperty = "added Property"; //ignores add
    alert("Frozen addedProperty= " + frozenObj.addedProperty);
    delete frozenObj.test; //ignores delete
    alert("Frozen so deleted property still exists= " + frozenObj.test);
    frozenObj.test = "Howdy"; //ignores update
    alert("Frozen ignores update to value= " + frozenObj.test);
    frozenObj.test = function() { return "function"; } //ignores
    alert("Frozen so ignores redefinition of value= " + frozenObj.test);

    alert("Is frozen " + Object.isFrozen(frozenObj));
    alert("Is sealed " + Object.isSealed(frozenObj));
    alert("Is extensible " + Object.isExtensible(frozenObj));

    alert("Cannot unfreeze");
    alert("result of freeze same as the original object: " + (frozenObj === allFrozen).toString());

    alert("Date.now = " + Date.now());

    //***************************SEALED****************************

    sealedObj.addedProperty = "added Property"; //ignores add
    alert("Sealed addedProperty= " + sealedObj.addedProperty);
    sealedObj.test = "Howdy"; //allows update
    alert("Sealed allows update to value unlike frozen= " + sealedObj.test);
    sealedObj.test = function() { return "function"; } //allows
    alert("Sealed allows redefinition of value unlike frozen= " + sealedObj.test);
    delete sealedObj.test; //ignores delete
    alert("Sealed so deleted property still exists= " + sealedObj.test);
    alert("Is frozen " + Object.isFrozen(sealedObj));
    alert("Is sealed " + Object.isSealed(sealedObj));
    alert("Is extensible " + Object.isExtensible(sealedObj));

    alert("Cannot unseal");
    alert("result of seal same as the original object: " + (sealedObj === allSealed).toString());

    alert("Date.now = " + Date.now());
}

3

我知道我可能迟到了

  • 相似:它们都用于创建不可扩展的对象
  • 区别:在“冻结”中 对象的可配置,可枚举和可写属性设置为false。其中,如“密封 可写”属性设置为true,其余属性为false。

6
这并不完全正确。Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable=== true
Leon Adler

2

现在,您可以强制冻结单个对象属性,而不是冻结整个对象。您可以Object.defineProperty使用writable: false作为参数来实现。

var obj = {
    "first": 1,
    "second": 2,
    "third": 3
};
Object.defineProperty(obj, "first", {
    writable: false,
    value: 99
});

在此示例中,obj.first现在将其值锁定为99。


0

我创建了一个简单的表格来比较以下功能并解释这些功能之间的区别。

  • Object.freeze()
  • Object.seal()
  • Object.preventExtensions()

该表解释了上述三种方法之间的区别

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.