【问题标题】:Creating new objects from frozen parent objects从冻结的父对象创建新对象
【发布时间】:2013-11-10 23:42:06
【问题描述】:

此示例创建一个对象,将其冻结,然后从冻结的对象创建一个新对象。 如果第二个对象试图更改测试属性,它不能。它保持冻结状态 第一个对象的值为 10。

//Create an object and freeze it

var first = {
    test: 10
};
Object.freeze(first);

//Create a second object from the first one and
//try and change the new test property (you can't)

var second = Object.create(first);
second.test = 20;
console.log(second.test); //10

这是我的问题:

second.test 是新对象上的新属性,还是只是对冻结的第一个对象中的属性的引用?
是否可以使用冻结的first.test 作为默认值,但如果需要让second.test 覆盖它?

我问的原因是因为我想让一个不可变的基础对象作为具有默认值的模板,然后用它来制作我可以自定义的新对象。最好的方法是什么?

谢谢!

【问题讨论】:

    标签: javascript inheritance prototypal-inheritance


    【解决方案1】:

    在新版本的 javascript 中,您可以使用对象解构来创建具有旧对象属性的新对象。

    const first = {
        test: 10
    };
    Object.freeze(first);
    
    //Clone the object
    const second = {...first};
    second.test = 20;
    console.log(second.test); // 20 where the first is locked
    

    【讨论】:

      【解决方案2】:

      second实际上是一个新对象,firstsecond的原型。原因

      second.test = 20;
      

      不起作用是因为在分配时,它将查找原型上的设置(即configurableenumerablewritable[[Extensible]])并且如果其中任何一个为 false,则不会分配给实例1。要直接分配给实例,您必须在 second 上使用 Object.defineProperty

      var first = {
          test: 10
      };
      Object.freeze(first);
      
      var second = Object.create(first);
      Object.defineProperty(second, 'test', { value: 20, enumerable: true, configurable: true, writable: true });
      console.log(second.test); // 20
      

      1:[[Put]]: the ECMAScript Specification, §8.12.5

      【讨论】:

      • 看来“第二”的行为就像它也被冻结一样。它继承了它的“冷冻”。但是如果你调用 Object.isFrozen(second),我认为会返回 false
      【解决方案3】:

      使用 Object.assign

               var first = {
                  test: 10
              };
              Object.freeze(first);
      
              //Create a second object from the first one and
              //try and change the new test property (you can't)
      
              var second = Object.assign({}, first, {
                  test: 20
              });
              console.log(second.test); //20
      

      【讨论】:

      • 相当外部情况,但请注意,如果冻结对象包含对其他冻结对象的引用,并且您尝试覆盖 它们的 属性,这仍然不起作用。对于简单的数据结构来说仍然是一种非常有用的技术,只是没有人认为这是一种创建不可变对象的万无一失的方法。
      【解决方案4】:

      在您的情况下,second 是对first 的引用(就像您假设的那样)。一个解决方案是克隆您的对象。克隆对象没有内置方法 - 你必须自己做,这是(source)的方法:

      function clone(obj){
         if(obj == null || typeof(obj) != 'object')
            return obj;
      
         var temp = obj.constructor();
      
         for(var key in obj)
             temp[key] = clone(obj[key]);
         return temp;
      }
      

      那你这样用:

      var first = {
          test: 10
      };
      Object.freeze(first);
      
      // clone it into a new one
      var second = clone(first);
      second.test = 20;
      console.log(second.test); // 20 where the first is locked
      

      【讨论】:

      • 没有内置的克隆方式?我会说,Object.assign 几乎是一个克隆器。
      • @loldrup 很好,这在 2013 年得到了回答,我相信 Object.assign 是在 ECMAScript2015 规范中引入的,即使现在它也需要针对 IE11 的 polyfill。无论如何,我同意答案可能应该被更新/编辑,以将其作为一个选项并与时俱进。
      • 需要明确的是,“... second 是对 first 的引用”是不正确的。正如在接受的答案中所说“第一个是第二个的原型”
      • @PanuLogic 虽然您使用的术语在 JS 的上下文中确实更好(我同意)。但是,根据我的理解,这两个短语的意思是一样的?有语义上的区别吗?我的意思是在 JS 中作为“原型”就是“引用”,不是吗?
      • 是的,单词可以有很多不同的含义。我的理解是当你有一个“参考”时,你实际上是在谈论“同一件事”。而对象的原型是另一个对象,与它是原型的对象分开。原型的属性可能比作为原型的对象少,并且属性的值可以不同。原型为 object :"B" 的对象 "A" 确实具有对 "B" 的引用。但是“B”没有引用“A”。
      猜你喜欢
      • 2017-07-14
      • 2013-04-06
      • 2010-10-12
      • 1970-01-01
      • 2017-10-24
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多