Gegenüber von Object.freeze oder Object.seal in JavaScript


Antworten:


101

Es gibt keine Möglichkeit, dies zu tun. Sobald ein Objekt eingefroren wurde, gibt es keine Möglichkeit, es wieder einzufrieren.

Quelle

Das Einfrieren eines Objekts ist die ultimative Form der Sperrung. Sobald ein Objekt eingefroren wurde, kann es nicht mehr eingefroren oder in irgendeiner Weise manipuliert werden. Dies ist der beste Weg, um sicherzustellen, dass Ihre Objekte auf unbestimmte Zeit genau so bleiben, wie Sie sie verlassen haben


10
Dies ist technisch korrekt, sofern das vorhandene Objekt mutiert wird. Sie können das vorhandene Objekt jedoch jetzt kopieren / klonen und seine Eigenschaften ändern. Informationen dazu finden Sie in der Antwort von Andennour TOUMI ( stackoverflow.com/a/26752410/1251309 ).
Levi Roberts

5
Es wäre SWEEEEET, wenn das Objekt nicht eingefroren werden könnte, solange der Aufruf zum Auftauen im selben Bereich erfolgt. Dies wäre eine RIESIGE Einsparung für die Speichernutzung, um das Klonen zu vermeiden. feObject.freeze(obj); thirdPartyRoutine(obj); Object.unfreeze(obj); /*continue using obj*/
trusktr

Können Sie sagen, warum sie keinen Schritt zum Auftauen eines Objekts bereitgestellt haben?
Badarshahzad

3
Möglicherweise aus Sicherheitsgründen? Was wäre, wenn Sie ein globales Objekt hätten, das niemand berühren soll? Das Einfrieren wäre sinnlos, wenn der potenzielle Hacker nur Object.unfreeze (obj) ausführen und es umgehen könnte.
Jaden Baptista

1
Wenn es dort wäre, würden Ihre Mitarbeiter es unweigerlich für Objekte verwenden, auf deren Einfrieren Sie zählen. Solche Probleme entziehen sich häufig auch Unit-Tests.
erich2k8

15

Ich denke, Sie können dies mit einigen Tricks tun:

  • Erstellen Sie zunächst eine doppelte temporäre Variable des ursprünglichen Objekts
  • Setzen Sie dann die ursprüngliche Variable auf undefiniert
  • das setzt den Wert davon aus dem temporären zurück.

Code hier:

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

Hinweis: Beachten Sie eines, tun Sie es nicht tempObj = obj , dann funktioniert es nicht, da tempObjes dort auch eingefroren ist.

Geige hier: http://jsfiddle.net/mpSYu/


4
Ich verstehe, worauf du hinaus willst. Und das Kopieren der Werte ist eine Möglichkeit, ein Objekt "einzufrieren", aber am Ende obj != tempObjsind ihre Signaturen nicht mehr dieselben.
CodingIntrigue

Das eingefrorene Objekt kann jedoch gelöscht werden !! Dies kann also vorerst eine akzeptable Lösung sein. Wenn Sie das eingefrorene Objekt klonen und derselben Referenz zuweisen und danach das eingefrorene Objekt löschen, kehren Sie in den alten Zustand zurück, mit dem einzigen Nachteil, dass Sie es manuell klonen. Natürlich haben wir das Original verloren.
vivek_nk

3
@vivek_nk Nein, es ist nicht dasselbe. Nur weil die neue Variable denselben Namen hat, heißt das nicht, dass sie dieselbe Referenz (Zeigeradresse) ist. Sie haben das Eingefrorene überschrieben obj, jedoch nur im lokalen Bereich. ps Ich persönlich mag es, dass Objekte nicht ungefroren werden können.
Alexander Tsepkov

Dies hat den Nachteil, dass mehr Speicher zugewiesen wird. Wenn dies beispielsweise in jedem Frame einer Animation geschieht, kann es "ruckelig" werden. Es wäre SWEEEEET, wenn Objekte nicht eingefroren werden könnten, solange der Aufruf zum Auftauen im selben Bereich erfolgt. Oder Object.freeze könnte eine "Unfreeze" -Funktion zurückgeben, die sorgfältig weitergegeben werden kann (genau wie Promise-Auflösungs- und Ablehnungsfunktionen sorgfältig weitergegeben werden können).
Trusktr

11

Kabelgebundene Lösung :)

 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;
 }

Empfohlene Vorgehensweise :


 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
Dies sollte die akzeptierte Antwort sein. Obwohl die andere Antwort technisch korrekt ist, erfüllt dies die Aufgabe des OP.
Levi Roberts

41
Dies friert nicht auf, dies kopiert das Objekt, eine ganz andere Sache
Willem D'Haeseleer

2
Für die Aufzeichnung sind alle Antworten auf diese Frage falsch und beinhalten eine NEUE Kopie, die das noch eingefrorene Objekt übertrifft ...
Leathan

Dies ist ein Hack und wird in einer Reihe von verschiedenen Fällen fehlschlagen.
Trusktr

Wenn dies eine Kopie des Objekts ist, ist der Spread-Operator oder die Object.assignbessere Option. Allerdings ist eine Kopie / ein Klon auch nicht das, wonach ich suche
Ghiscoding

7

Sie können ein eingefrorenes Objekt nicht auftauen.

Sie können es jedoch so gestalten, dass lästige Bibliotheken in Zukunft nichts mehr einfrieren können, indem Sie die Object.freeze-Methode als No-Op überschreiben:

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

In den meisten Fällen reicht dies aus. Führen Sie einfach den obigen Code aus, bevor die Bibliothek geladen wird, und sie kann nichts mehr einfrieren. ;; )


BEARBEITEN : Einige Bibliotheken, z. B. immer@7.0.7, haben Probleme, es sei denn, die Object.isFrozen(obj)Rückgabe erfolgt truenach dem Object.freeze(obj)Aufruf.

Das Folgende ist somit eine sicherere Methode zum Blockieren des Einfrierens von Objekten: (Die primitiven Überprüfungen sind auf WeakSetFehler zurückzuführen, wenn Sie Primitive übergeben.)

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);
};

Dies erscheint mir vernünftig, wie eine umgekehrte Unterlegscheibe. Warum wurde das abgelehnt?
amay0048

12
@ amay0048 Ein Shim fügt Verhalten hinzu . Dadurch wird vorhandenes Verhalten entfernt. Dies ist eine schreckliche Idee und sollte definitiv nicht in der Produktion verwendet werden. Object.freezefunktioniert aus einem bestimmten Grund so.
CodingIntrigue

Es ist eine schreckliche Idee, eine Unterlegscheibe zu verwenden, die das Verhalten in der Produktion beseitigt. Wenn Sie dagegen hacken möchten, können Sie alles tun, um Ihr Ziel zu erreichen.
mjz19910

1
Sie können die Funktionsüberschreibung erweitern, indem Sie nach Kennzeichen des Objekts suchen, das Sie freigeben möchten, damit es überhaupt nicht eingefroren wird. Andere Objekte können noch eingefroren werden.
Mike de Klerk

Das hat wirklich nicht viel mit dem ursprünglichen Beitrag zu tun. Es ist wahrscheinlich besser, mit der "nervigen" Bibliothek so umzugehen, wie es die Benutzeroberfläche vorschlägt. Ich bin bereit anzunehmen, dass Sachen aus einem bestimmten Grund eingefroren werden. Dies verursacht wahrscheinlich mehr Probleme als es löst. Sie können ein Abzeichen für das Löschen Ihrer eigenen Antwort erhalten .... ;-)
theUtherSide

3

Sie können ein Objekt nicht auftauen (auftauen), aber wenn das Objekt einfach eine Sammlung von Grundelementen ist (keine Funktionen oder Klassen), können Sie einen aufgetauten Klon des Objekts wie folgt erhalten:

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

2
Ich frage mich, ob Sie dies als nicht gefroren betrachten können. Sie erstellen im Wesentlichen ein neues Objekt, eingefrorenes Objekt ist immer noch unveränderlich
Deepak Jha

1

Getestet in FF 52:

Soweit das (symbolische) 'Eltern'-Objekt des eingefrorenen Objekts (wo es symbolisch durch, neben / abgesehen von anderen symbolischen Verweisen in anderen Teilen des Codes auf dasselbe Objekt referenziert wird) NICHT GEFROREN ist (wie Fenster), kann man lösche es trotzdem durch den Löschoperator, - wie:

window.tinymce löschen;

selbst wenn window.tinymce VORHER von Object.freeze (window.tinymce) eingefroren wurde; (Andernfalls würde das 'Elternteil' selbst zu einer Art "eingefrorenem" Element, das eine nicht zerstörbare Objektreferenz enthält und das Symbol des NICHT eingefrorenen Elternteils nicht mehr löschbar macht ...)

Soweit man eine Kopie / Klon / Rekonstruktion / eigene Version / des Originalobjekts bereits vor dem Löschen / Entfernen erstellt hat, die keine / der ursprünglichen Einschränkungen (eingefroren, Erweiterbarkeit, Konfigurierbarkeit, Beschreibbarkeit usw.) beseitigt hat, man kann einen Verweis auf diese Kopie / Klon / Rekonstruktion / eigene Version / auf den ursprünglichen symbolischen Ort setzen / zuweisen - so:

window.tinymce = the_copy_clone_reconstruction_own_version_object;

Stellen Sie sicher, dass "copy_clone_reconstruction_own_version_object" im globalen Bereich vorhanden ist, damit es nicht gelöscht wird, nachdem Ihr Workaround-Code beendet wurde! [Eigentlich sollte das Objekt selbst gelöscht werden / es wird Speicher freigegeben / nur und nur dann, wenn der allerletzte Verweis darauf aus einem Bereich entfernt wurde - einige Zeit später aufgrund der Speicherbereinigung, aber ich bin mir nicht sicher, welche Priorität es hat höher als die 'Funktion beendet - alle lokalen Variablen löschen']

NICHT getestet: Andere symbolische Verweise KÖNNEN außerdem auf das ursprüngliche, eingefrorene / eingeschränkte Objekt verweisen - wie etwas, das als festgelegt wurde

myobj.subobj = window.tinymce;

bevor Ihre Operationen begannen.

Solche Sachen (myobj.subobj) werden wahrscheinlich (probieren Sie es aus!) Weiterhin auf das gefrorene Original (?) Zeigen.

nächste Vorstellung: NICHT getestet!

Was ist mit der 'Proxy'-Funktion, um Value-Get / Set und anderes Verhalten (Funktionen, ...) eines eingefrorenen / versiegelten oder anderweitig eingeschränkten (Erweiterbarkeit, ...) Objekts zu verpacken? Erstellt im GLOBAL-Bereich wie p = neuer Proxy (Ziel, Handler); oder window.p = neuer Proxy (Ziel, Handler);
// wobei target das Objekt ist, das zum Abfangen / Einhaken / Überwachen umbrochen werden soll, wie zum Beispiel "window.tinymce"

Das mdn-doc für das Proxy-Thema besagt, dass Einschränkungen (eingefroren, ...) des umschlossenen Objekts beibehalten werden, die sich jedoch auf das Core- / Original-Objekt selbst (vom Proxy umschlossen) beziehen und möglicherweise auch NICHT auf die Nachahmung durch den Proxy verweisen ...

Geltungsbereichsregeln können wie oben erwähnt gelten ...


0

Ich habe dieses Problem auch gelöst. Um dies zu beheben, habe ich die JavaScript-JSON-API verwendet, um mein Objekt freizugeben : const unfreezeObject = JSON.parse(JSON.stringify(freezeObject)). Danach habe ich alle Mutationen gemacht, die ich brauchte.


manuell (bevorzugt) oder lodash deepClone ist eine bessere Option. JSON.parse (JSON.stringify (...)) ist eine fehlerhafte Methode zum Ausführen einer tiefen Kopie. Wenn Sie nicht tiefgefroren haben (rekursiv einfrieren), ist { ...item }möglicherweise nur eine flache Kopie erforderlich.
TamusJRoyce

0

Ein wenig zu spät zur Party, aber Sie können auch ein Objekt in einer veränderlichen Variablen ( let) erstellen und das ursprüngliche Objekt der Variablen neu zuweisen, wenn Sie es zurücksetzen müssen.

Zum Beispiel:

let obj = { objProp: "example" };

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

-3

Sie können ein Array mit dem Spread-Operator auftauen.

//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
Das schafft nur eine Kopie. Es friert es nicht auf.
Jack Giffin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.