【问题标题】:Opposite of Object.freeze or Object.seal in JavaScriptJavaScript 中 Object.freeze 或 Object.seal 的对立面
【发布时间】:2013-10-10 10:29:47
【问题描述】:

Object.freezeObject.seal 的反义词是什么?有没有一个函数有 detach 之类的名字?

【问题讨论】:

  • 游戏Return True to Win有三个关于解冻冻结物体的问题。但我还没有找到任何答案! (见“解冻”questions here,但也有剧透)

标签: javascript


【解决方案1】:

没有办法做到这一点,一旦对象被冻结,就无法解冻它。

Source

冻结对象是锁定的最终形式。一旦一个对象 已被冻结,无法解冻 - 也无法以任何方式篡改 方式。这是确保您的对象保持不变的最佳方式 就像你离开他们一样,无限期

【讨论】:

  • 就改变现有对象而言,这在技术上是正确的。但是,您现在可以复制/克隆现有对象并改变它的属性。有关如何完成此操作,请参阅 Andennour TOUMI (stackoverflow.com/a/26752410/1251309) 的答案。
  • 只要 unfreeze 调用发生在同一范围内,如果对象可以解冻,那就太好了。这将大大节省内存使用量,以避免克隆。 f.e. Object.freeze(obj); thirdPartyRoutine(obj); Object.unfreeze(obj); /*continue using obj*/
  • 可能是因为安全问题?如果您有一个不希望任何人触摸的全局对象怎么办。如果潜在的黑客可以执行 Object.unfreeze(obj) 并绕过它,冻结它们将毫无意义。
  • 如果它在那里,你的同事不可避免地会在你指望被冻结的物体上使用它。这类问题也经常逃过单元测试。
  • 对于同一范围内的冻结和解冻,freeze(锁定)函数还可以返回 key 所需的 key(解锁)它。
【解决方案2】:

我认为你可以做到,使用一些技巧:

  • 首先创建一个原始对象的重复临时变量
  • 然后将原始变量设置为未定义
  • 从临时重置它的值。

代码在这里:

var obj = {a : 5};

console.log(obj); // {a: 5}
Object.freeze(obj);

obj.b = 10; // trying to add something to obj var
console.log(obj); // output: {a: 5} -> means its frozen

// Now use this trick
var tempObj = {};
for(var i in obj){
    tempObj[i] = obj[i];
}
console.log(tempObj); // {a: 5}

// Resetting obj var
obj = tempObj;
console.log(obj);// {a: 5}

obj.b = 10; // trying to add something to obj var
console.log(obj); // output: {a: 5, b: 10} -> means it's not frozen anymore

注意:记住一件事,不要做tempObj = obj,那它就行不通了,因为tempObj也被冻结在那里。

在这里提琴:http://jsfiddle.net/mpSYu/

【讨论】:

  • 我明白你的意思。复制值是一种“绕过”冻结对象的方法,但最后obj != tempObj - 它们的签名不再相同。
  • 但是,冻结的对象可以删除!!因此,目前这可能是一个可以接受的解决方案。克隆冻结的对象并分配给相同的引用,然后删除冻结的对象将使我们回到旧状态,唯一的缺点是手动克隆它。当然,我们丢失了原来的那个。
  • @vivek_nk 不,不一样。仅仅因为新变量具有相同的名称并不意味着它是相同的引用(指针地址)。您覆盖了冻结的obj,但仅在本地范围内。 p.s.我个人喜欢对象无法解冻。
  • 这有创建分配更多内存的缺点,例如,如果这是在动画的每一帧中发生的事情,那么它可能会变得“janky”。只要 unfreeze 调用发生在同一范围内,如果对象可以解冻,那将是 SWEEEEET。或者,也许 Object.freeze 可以返回一个可以谨慎传递的“解冻”函数(就像 Promise 的解析和拒绝函数可以谨慎传递一样)。
  • 这不是解冻,只是将原始对象复制到 tempObj,然后将 obj 变量重新分配给该 tempObj。换句话说,原来的 obj 现在指向新的引用。对此的证明是 obj 和 tempObj 在更改 obj 后记录相同。只需使用 var tempObj = {...obj}; 就可以在没有 for 循环的情况下更简单地实现这一点obj = tempObj;
【解决方案3】:

有线解决方案:)

 Object.unfreeze=function(o){
       var oo=undefined;
        if( o instanceof Array){
                oo=[];var clone=function(v){oo.push(v)};
                o.forEach(clone); 
        }else if(o instanceof String){
           oo=new String(o).toString();
      }else  if(typeof o =='object'){

         oo={};
        for (var property in o){oo[property] = o[property];}


        }
        return oo;
 }

最佳实践:


 var obj={a:1,b:2}
 // {a:1,b:2}
   obj.c=3; 
  //{a:1,b:2,c:3}
  Object.freeze(obj)
  //{a:1,b:2,c:3}
  obj.d=5;
  //Error: Read only object 
  obj=Object.unfreeze(obj)
  //{a:1,b:2,c:3}
   obj.d=5;
  //{a:1,b:2,c:3,d:5}

 var tab=[1,2,3]
 //[1,2,3]
tab.push(4)
 //[1,2,3,4]
Object.freeze(tab);
//[1,2,3,4]
tab.push(5)
// Error : Ready only object
tab=Object.unfreeze(tab);
//[1,2,3,4]
tab.push(9)

//[1,2,3,4,9]

【讨论】:

  • 这应该是公认的答案。尽管另一个答案在技术上是正确的,但这完成了 OP 的任务。
  • 这不会解冻,这会复制对象,完全不同的东西
  • 郑重声明,这个问题的所有答案都是错误的,并且涉及一个超出仍冻结对象范围的新副本...
  • 这是一个 hack,在很多不同的情况下都会失败。
  • 如果这是对象的副本,那么扩展运算符或Object.assign 是更好的选择。然而,副本/克隆也不是我想要的,
【解决方案4】:

您无法解冻冻结的对象。

但是,您可以通过将 Object.freeze 方法重写为无操作,使讨厌的库在未来无法冻结任何内容:

Object.freeze = function(obj) { return obj; }; // just return the original object

在大多数情况下,这就足够了。只需在加载库之前运行上面的代码,它就不能再冻结任何东西。 ; )


编辑:某些库(例如immer@7.0.7)会出现问题,除非在调用Object.freeze(obj)Object.isFrozen(obj) 返回true

因此,以下是阻止对象冻结的更安全方法:(原语检查是因为如果您传入原语,WeakSet 会出错)

const fakeFrozenObjects = new WeakSet();
function isPrimitive(val) {
    return val == null || (typeof val != "object" && typeof val != "function");
}
Object.freeze = obj=>{
    if (!isPrimitive(obj)) fakeFrozenObjects.add(obj);
    return obj;
};
Object.isFrozen = obj=>{
    if (isPrimitive(obj)) return true;
    return fakeFrozenObjects.has(obj);
};

【讨论】:

  • 这对我来说似乎是合理的,就像反向垫片一样。为什么这被否决了?
  • @amay0048 垫片添加行为。这删除现有行为。这是一个糟糕的想法,绝对不应该在生产中使用。 Object.freeze 的工作方式是有原因的。
  • 使用 shim 消除生产中的行为是一个糟糕的主意。另一方面,如果你想破解,你可以做任何你想做的事情来实现你的目标。
  • 您可以通过检查您希望解冻的对象的标志来扩展函数覆盖,因此它永远不会被冻结。其他物体可以静止不动。
  • 这真的和原帖没有太大关系。以他们的界面建议的方式处理“讨厌的”库可能会更好。我愿意假设东西被冻结是有原因的。这可能会导致比它解决的问题更多的问题。您可能会因删除自己的答案而获得徽章.... ;-)
【解决方案5】:

您不能解冻(解冻)对象,但如果对象只是基元的集合(没有函数或类),您可以像这样获得对象的解冻克隆:

const unfrozenObj = JSON.parse(JSON.stringify(frozenObj));

【讨论】:

  • 我想知道您是否可以将其视为解冻。您实际上是在创建一个新对象,冻结的对象仍然是不可变的
【解决方案6】:

根据答案和 cmets,似乎人们在需要对冻结对象进行变异时会找到此帖子。说“那是不可能的”虽然准确,但并不是很有帮助。

考虑到这一点,这是我根据 2014 年的 @Abdennour TOUMI 回答提出的现代变体

function unfreeze(obj) {
  if (Object.isFrozen(obj)) {
    return Object.assign({}, obj);
  }
  return obj;
}

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

let myObj = { one: "1", two: "2", three: "3" };
Object.freeze(myObj);
// error
// myObj.one = "4";
// succeeds
myObj = unfreeze(myObj);
myObj.one = "4";

【讨论】:

  • 这不会解冻对象。它只是创建一个副本,就像所有其他答案一样。此外,说其他 cmets 是“准确但无益”是毫无用处的。如果有人想做到 1+1=3 并且 cmets 说这是不可能的,你还会说“准确但无益”吗?这只是不明智的。您不能解冻和反对,也不能将 1 添加到自身以获得 3。故事结束。唯一真正有用的事情是帮助某人以不涉及解冻对象(或更改算术)的方式解决他们的实际问题,而这个答案没有这样做。
  • @siride 我的意思是,如果 27 个人给出相同的答案,1+1=3 不可能是真的,这既准确又无益。对现有答案发表评论或投票就足够了。
【解决方案7】:

在 FF 52 中测试:

就冻结对象的(符号)“父”对象(它被符号引用,除了/除了代码的其他部分对同一对象的其他符号引用之外/之外)不冻结(如窗口) , 仍然可以通过 delete-operator 删除它, - 比如:

删除window.tinymce;

即使 window.tinymce 在之前被 Object.freeze(window.tinymce) 冻结; (否则,“父级”本身会变成某种“冻结”,因为它包含不可破坏的对象引用,这会使未冻结的父级的符号不可删除......)

只要在删除/删除之前已经制作了原始对象的副本/克隆/重建/自己的版本/,它就摆脱了/没有/原始限制(冻结、可扩展性、可配置性、可写性等) on),可以将对该副本/克隆/重建/自己的版本/的引用放置/分配到原始符号位置,- 就像这样:

window.tinymce = the_copy_clone_reconstruction_own_version_object;

确保在您的解决方法代码完成后,该“copy_clone_reconstruction_own_version_object”在全局范围内不会被删除! [实际上,对象本身应该被删除/它的内存被释放/仅当对它的最后一个引用已从任何范围中删除时, - 一段时间后,由于垃圾收集,但我不确定优先级高于'函数完成 - 删除所有本地变量']

未经测试: 此外,其他符号引用可能指向原始的、冻结/受限的对象, - 像某些东西,它被设置为

myobj.subobj=window.tinymce;

在您的操作开始之前。

这样的东西 (myobj.subobj) 可能会(试一试!)而且指向冻结的原始文件 (?)。

下一个概念: 未测试!

如何使用“代理”功能来包装冻结/密封或其他受限(可扩展性,...)对象的 value-get/-set 和其他行为(函数,...)? 在全球范围内创建,例如 p = 新代理(目标,处理程序); 或者 window.p = 新代理(目标,处理程序);
// 其中 target 是为拦截/挂钩/监控而包装的对象,例如“window.tinymce”

proxy-topic 的 mdn-doc 说,被包装对象的限制(冻结,...)被考虑在内,但这可能是指核心/原始对象本身(由代理包装)并且最终可能不会引用代理所做的模仿......

范围规则可能如上所述适用...

【讨论】:

    【解决方案8】:

    我也遇到过这个问题。为了修复它,我使用 JavaScript JSON API 来解冻我的对象:const unfreezeObject = JSON.parse(JSON.stringify(freezeObject))。之后,我做了所有我需要的突变。

    【讨论】:

    • 手动(首选)或 lodash deepClone 是更好的选择。 JSON.parse(JSON.stringify(...)) 是一种执行深度复制的有缺陷的方法。此外,除非您进行深度冻结(递归应用冻结),否则可能只需要一个浅拷贝 { ...item }
    【解决方案9】:

    聚会晚了一点,但您也可以在可变变量 (let) 中创建一个对象,并在需要重置时将原始对象重新分配给该变量。

    例如:

    let obj = { objProp: "example" };
    
    if (condition) {
        Object.freeze(obj);
    }
    else {
        obj = { objProp: "example" };
    }
    

    【讨论】:

      【解决方案10】:

      您可以使用展开运算符解冻数组。

      //let suppose arr is a frozen array i.e. immutable
      var arr = [1, 2, 3];
      
      //if arr is frozen arr you cannot mutate any array referring to it
      var temp = arr;
      
      temp.push(4);  //throws an error "Cannot modify frozen array elements"
      
      //here mutableArr gets the elements of arr but not reference to it
      //hence you can mutate the mutableArr
      
      var mutableArr = [...arr];
      
      mutableArr.push(4);  //executes successfully 
      

      【讨论】:

      • 这只是创建一个副本。它不会解冻它。
      猜你喜欢
      • 1970-01-01
      • 2022-12-11
      • 1970-01-01
      • 2018-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多