【问题标题】:Difference between freeze and seal冷冻和密封的区别
【发布时间】:2014-02-19 13:05:59
【问题描述】:

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

这里有一个简短的例子如何使用它:

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

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

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

freezeseal 有什么区别?它们能提高性能吗?

【问题讨论】:

  • 只是给任何查看此问题的人的注释,接受的答案实际上是不正确的。 @tungd 的回答是正确的。
  • 另外注意,除了Object.sealObject.freeze之外,还有Object.preventExtensionsObject.preventExtensions 只是防止将新项目添加到对象中。您可以删除、配置和更改使用 Object.preventExtensions 关闭扩展性的对象的属性值。

标签: javascript ecmascript-5


【解决方案1】:

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

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

【讨论】:

  • Object.seal() 似乎也冻结原型属性:\
【解决方案2】:

Object.seal

  • 它可以防止添加和/或删除密封对象的属性;使用 delete 将返回 false
  • 它使每个现有属性non-configurable:它们不能从“数据描述符”转换为“访问器描述符”(反之亦然),并且访问器描述符的任何属性都不能被修改(而数据描述符可以改变它们的writable 属性,以及它们的 value 属性(如果 writeable 为真)。
  • 尝试修改密封对象本身的值时会抛出TypeError(最常见于严格模式

Object.freeze

  • 正是Object.seal 所做的,另外:
  • 它可以防止修改任何现有属性

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


性能:

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

  • Firefox:枚举性能不受影响
  • IE:枚举性能影响可以忽略不计
  • Chrome:枚举性能在密封或冻结对象时更快
  • Safari:密封或冻结的对象枚举速度慢 92%(截至 2014 年)

测试:Sealed objectsFrozen objects

【讨论】:

  • 您能谈谈我们为什么会使用这些方法吗?就因为我们可以?
  • 未来我认为在开发库/框架时会大量使用它们(如果优化正确的话)。它们允许您防止用户(甚至是无意地)破坏您的代码(并且,如答案中所述,优化应该会带来很大的速度提升)。但这纯粹是猜测:)
  • 这个答案有很多事实错误。一方面,seal 还使现有属性不可配置,参见jsfiddle.net/btipling/6m743whn 编号 2,您仍然可以编辑,即更改密封对象上现有属性的值。
  • FWIW,冻结和密封对象现在比 Chrome Canary v43.0.2317.0 中的未冻结和未密封对象更快。
  • @AlanDong 来得有点晚,但这就是为什么要锁定对象的原因。JavaScript 的一个特点是您可以随时添加属性;您也可以通过输入错误不小心做到这一点。我的许多学生都尝试添加一个名为 onClickonlick 的事件处理程序,并想知道为什么它不起作用。如果 JavaScript 抛出错误,那么出错的可能性就会降低。其次,这允许您在对象上实现常量属性以防止更改。这对对象方法特别有用。
【解决方案3】:

Object.freeze() 创建一个冻结对象,这意味着它需要一个 现有对象并在本质上对其调用Object.seal(),但它还将所有“数据访问器”属性标记为writable:false,因此它们的值无法更改。

-- Kyle Simpson,你不懂 JS - 这个和对象原型

【讨论】:

    【解决方案4】:

    我正在查看 ECMAScript 5 中的 Freeze 和 Seal 之间的差异,并创建了一个脚本来阐明这些差异。 Frozen 创建了一个不可变的对象,包括数据和结构。 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());
    }
    

    【讨论】:

      【解决方案5】:

      我知道我可能迟到了,但是

      • 相似性:两者都用于创建不可扩展 对象
      • 区别:冻结时可配置、可枚举和可写 对象的属性设置为false。在密封的地方 可写属性设置为true,其余属性为假。

      【讨论】:

      • 这并不完全正确。 Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable === true.
      【解决方案6】:

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

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

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

      【讨论】:

        【解决方案7】:

        我写了一个test project 比较了这三种方法:

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

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

        • [C] 添加新属性
        • [R] 读取现有属性
        • [U] 修改现有属性
        • [D] 移除现有属性

        结果:

        【讨论】:

        • 这太棒了。 UPDATE 是否考虑修改(通过defineProperty)描述符属性,例如可配置、可枚举、可写?
        • 我一直认为 DOM 对象应该被密封(当然是在 polyfills 之后)。这将有助于防止很多拼写错误。
        • @Manngo 你可以密封你的 DOM 对象。只需创建一个DEBUGMODE 变量并将其设置为true。然后,执行if (DEBUGMODE) { ... }。在... 中,放置确保所有 DOM 对象始终处于密封状态的功能。然后,当您准备好分发您的网页脚本时,将DEBUGMODE 更改为false,通过闭包编译器运行您的脚本,然后分发它。就这么简单。
        • @JackGiffin 感谢您的评论。我只是说我一直认为这是个好主意。我有很多学生最终输入element.onlick=something 之类的内容并因为不起作用而感到沮丧,但从技术上讲这不是错误。
        • @Lonely 那么它就不会拼写 CRUD。你必须满足于粗鲁之类的东西;)
        【解决方案8】:

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

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

        【讨论】:

          猜你喜欢
          • 2021-11-05
          • 2021-01-06
          • 2016-03-29
          • 2020-06-08
          • 1970-01-01
          • 2017-11-28
          • 1970-01-01
          • 2021-01-11
          • 2016-04-05
          相关资源
          最近更新 更多