与JavaScript中的Object.freeze或Object.seal相反


Answers:


101

无法执行此操作,一旦冻结了对象,就无法解冻它。

资源

冻结对象是锁定的最终形式。一旦冻结了对象,就不能将其冻结-也不能以任何方式对其进行篡改。这是确保您的对象无限期保持原样的最佳方法


10
就变异现有对象而言,这在技术上是正确的。但是,您现在可以复制/克隆现有对象并对其属性进行突变。有关如何完成此操作的信息,请参阅Andennour TOUMI的答案(stackoverflow.com/a/26752410/1251309)。
李维·罗伯茨

5
只要取消冻结调用发生在同一范围内,就可以取消冻结对象。这样可以节省大量内存,避免克隆。feObject.freeze(obj); thirdPartyRoutine(obj); Object.unfreeze(obj); /*continue using obj*/
trusktr

您能说出为什么他们没有提供解冻对象的步骤吗?
badarshahzad

3
可能是因为安全问题?如果您有一个全局对象该怎么办,不要让任何人触摸。如果潜在的黑客只能执行Object.unfreeze(obj)并解决它,冻结它们将毫无意义。
贾登·巴普蒂斯塔

1
如果在那里,您的同事将不可避免地将其用于您要冻结的对象上。这些问题通常也常常逃脱单元测试。
erich2k8

15

我认为您可以使用一些技巧来做到:

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

代码在这里:

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/


4
我了解您的意思。复制值是一种“解决”冻结对象的方法,但是到最后,obj != tempObj它们的签名不再相同。
CodingIntrigue 2014年

但是,冻结的对象可以删除!因此,这可能是目前可以接受的解决方案。克隆冻结的对象并分配给相同的引用,然后删除冻结的对象将使我们回到旧状态,唯一的缺点是手动克隆它。当然,我们失去了原始的。
vivek_nk 2014年

3
@vivek_nk不,不一样。仅仅因为新变量具有相同的名称并不意味着它具有相同的引用(指针地址)。您覆盖了冻结对象obj,但只覆盖了本地范围。ps我个人喜欢那些物体不能被冻结。
亚历山大·谢普科夫

这具有创建分配更多内存的缺点,例如,如果在动画的每一帧中都发生这种情况,则它可能会变得“混乱”。只要取消冻结调用发生在同一范围内,就可以取消冻结对象。或者,也许Object.freeze可以返回可以仔细传递的“取消冻结”功能(就像Promise的resolve和reject函数可以小心地传递一样)。
trusktr

11

有线解决方案:)

 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]

1
这应该是公认的答案。尽管另一个答案在技术上是正确的,但这可以完成OP的任务。
李维·罗伯茨

41
这不会解冻,这会复制对象,这完全是另一回事
Willem D'Haeseleer

2
从记录来看,这个问题的所有答案都是错误的,并且包含一个新副本,该副本覆盖了仍然冻结的对象……
Leathan

这是一个hack,在很多情况下都会失败。
trusktr '18

如果这是对象的副本,则使用散布运算符或Object.assign更好的选择。但是,复制/克隆也不是我想要的,
ghiscoding '20

7

您无法解冻冻结的对象。

但是,可以通过将Object.freeze方法重写为no-op来使讨厌的库将来不再冻结任何内容:

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

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


编辑:某些库(例如immer@7.0.7)有问题,除非在之后Object.isFrozen(obj)返回trueObject.freeze(obj)调用。

因此,以下是阻止对象冻结的更安全方法:(原始检查是因为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

12
@ amay0048垫片会增加行为。这消除了现有的行为。这是一个可怕的想法,绝对不应该在生产中使用。Object.freeze发挥作用是有原因的。
CodingIntrigue '01

使用垫片消除生产中的行为是一个糟糕的主意。另一方面,如果您想破解,则可以做任何想实现自己目标的事情。
mjz19910

1
您可以通过检查要取消冻结的对象的标记来扩展功能覆盖,因此它永远不会被冻结。其他对象仍可以冻结。
Mike de Klerk

这确实与原始帖子没有太大关系。最好按照界面提示的方式处理“讨厌的”库。我愿意假定某事被冻结。这可能会导致更多的问题而不是解决的问题。您可能会获得删除自己答案的徽章。...;-)
theUtherSide

3

您不能解冻(解冻)对象,但是如果该对象仅仅是基元的集合(没有函数或类),则可以得到对象的解冻克隆,如下所示:

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

2
我想知道您是否可以将此视为冻结。您实际上是在创建一个新对象,冻结的对象仍然是不可变的
Deepak Jha,

1

在FF 52中测试:

只要冻结对象的(符号)“父”对象(由代码的其他部分中的其他符号引用旁边,/或与该对象的其他符号引用进行符号引用)未冻结(例如窗口),则可以尽管如此,还是由delete-operator删除它,例如:

删除window.tinymce;

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

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

window.tinymce = the_copy_clone_reconstruction_own_version_object;

确保解决方法代码完成后,全局范围内的“ copy_clone_reconstruction_own_version_object”不被删除![实际上,仅当对象的最后引用从任何作用域中删除时,才应该删除对象本身/释放其内存,-稍后,由于垃圾回收,但是我不确定其优先级高于“函数完成-删除所有本地变量”]

未经测试:其他符号引用也可能指向原始的冻结/受限对象,例如设置为

myobj.subobj = window.tinymce;

在您开始操作之前。

这样的东西(myobj.subobj)可能会(尝试一下!)进一步指向冻结的原始(?)。

下一个概念:未经测试!

如何使用“代理”功能包装冻结/密封或受其他限制(可扩展性,...)对象的值获取/设置和其他行为(功能,...)?在GLOBAL范围内创建,例如p = new Proxy(target,handler); 或window.p =新代理(目标,处理程序);
//其中target是要包装以进行拦截/挂钩/监视的对象,例如“ window.tinymce”

代理主题的mdn-doc表示,保留了对包装对象的限制(冻结,...),但是这可能是指核心/原始对象本身(由代理包装),并且可能最终不参考代理人的模仿...

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


0

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


手动(首选)或lodash deepClone是更好的选择。JSON.parse(JSON.stringify(...))是执行深度复制的一种有缺陷的方法。另外,除非进行了深度冻结(递归应用冻结),{ ...item }否则可能只需要浅表副本即可。
TamusJRoyce

0

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

例如:

let obj = { objProp: "example" };

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

-3

您可以使用传播运算符解冻数组。

//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 

3
那只是创建一个副本。它不会解冻它。
杰克·吉芬
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.