2013-06-19 16 views
5

ich für diese um gesucht zu funktionieren, aber so haben bisher nicht gelungen, ein Duplikat zu finden, die ich die falschen Schlüsselwörter werden kann ...Referenz Speicherung in Objekt/Variable

ich vorübergehend bin versucht zu ändern Funktion, die in einem Objekt gespeichert ist, aber Probleme haben, es auf das zurückzusetzen, was es vorher war.

Bedenken Sie:

// Set the options object 
var options = { 
    success: function(){ 
     console.log('Original Function Called'); 
    } 
} 

// Save the options 
$('#foo').data('bar',options); 

Und dann in einer anderen Funktion:

// Get the options 
var options = $('#foo').data('bar'); 

// Store the old options 
var old_options = options; 

// Temporarily change the success function 
options.success = function(){ 
    console.log('Temporary Function Called'); 
} 

// Save the options 
// This allows the other functions to access the temporary function 
$('#foo').data('bar',options); 

// Do stuff here that uses the new options 

// Reset the options to include the original success function 
$('#foo').data('bar',old_options); 

Ich würde die Temporary Function Called nur Anzeige einmal hat jedoch zu erwarten, scheint es völlig den alten success Rückruf zu ersetzen der temporäre Rückruf.

Kann mir jemand sagen, warum und wie ich das umgehen kann?

UPDATE

Ich dachte, dass extend dieses Problem beheben würde, aber es scheint, dass das Problem ein wenig tiefer sein kann. Ich habe beschlossen, dieses Mal einen Ausschnitt meines eigentlichen Codes zu posten. Bitte beachten Sie die folgenden vor dem Lesen:

  1. SM ziemlich viel ist nur ein Alias ​​von jQuery, bitte ignorieren.
  2. success und error sind Parameter an die Funktion geliefert

Hier ist mein Code:

// Get the properties 
var properties = $(form).data('autosave'); 
switch(parameter){ 
    case 'save': 
     var old_properties = $.extend({},properties); 
     // Set the new callbacks if they have been supplied 
     properties.options.success = typeof success!=='undefined' ? success : old_properties.options.success; 
     properties.options.error = typeof error!=='undefined' ? error : old_properties.options.error; 
     // Save the properties 
     $(form).data('autosave',properties); 
     // Call the save method before setting the interval 
     SM(form)._as_save(); 
      properties = $.extend({},old_properties); 
     // Save the old properties 
     $(form).data('autosave',properties); 
     // Clear the current interval 
     clearInterval(properties.interval); 
     // Call the save method periodically 
     properties.interval = setInterval(function(){ 
     SM(form)._as_save(); 
     },properties.options.interval); 
    break; 
} 
// Save the properties 
$(form).data('autosave',properties); 
+0

Sie sagen, dass nach '$ ('# foo'). Data ('bar', old_options);' Sie immer noch 'Temporary Function Called' sehen? – crush

+0

@crush Genau! :-) –

+0

@crush Ich habe in der Tat –

Antwort

9

Wenn Sie diesen Code ausführen:

var old_options = options; 

Sie kein Kopie machen des gesamten Objekts options, das Sie später wiederherstellen können. Sie speichern lediglich eine Referenz an die gleiche Objekt. Mit anderen Worten, ist old_options das gleiche Objekt wie options, so dass, wenn Sie einen neuen Wert in options.success zuweisen, sind Sie es in beide optionsundold_options — ändern, weil sie das gleiche Objekt sind.

Um dies zu beheben, können Sie eine Objektklonfunktion verwenden, um eine Kopie des Objekts zu erstellen, die Sie später wiederherstellen können.

var old_options = $.extend(true, {}, options); 

Wenn Sie jetzt options.success ändern, sind Sie nur in der options Objekt zu ändern: Da Sie jQuery verwenden, können Sie die Zeile oben ändern. old_options ist nicht betroffen, so dass Ihr späterer Aufruf wird es erfolgreich wiederherstellen:

$('#foo').data('bar',old_options); 

Interessanterweise kann dies noch OK auch funktionieren, wenn options.success ein asynchronen Rückruf ist (die aus dem Namen wahrscheinlich klingt).Das ist, weil, was auch immer Code diese .success() Methode später aufrufen, sollten sie immer noch auf eine Referenz auf Ihre modifizierten options Objekt — halten, auch wenn Sie die alte wieder in die Daten des Elements in der Zwischenzeit wiederhergestellt haben. Zumindest konnte man darauf hoffen; Wenn der andere Code in den $().data() zurückfindet, um den Rückruf .success zu finden, dann würden Sie in Schwierigkeiten geraten.

Der obige Aufruf $.extend() führt eine "tiefe" (rekursive) Kopie des Objekts options. Das heißt, wenn eine der Eigenschaften innerhalb von options selbst ein Objekt ist, klont sie auch dieses Objekt, anstatt nur einen Verweis darauf zu kopieren.

Wenn Sie das true Argument auslassen, tut $.extend() eine flache Kopie statt:

var old_options = $.extend({}, options); 

Dies noch ein neues Objekt und Kopien über alle Eigenschaften aus einem bestehenden Objekt erstellt, aber wenn eine dieser Eigenschaften ist selbst ein Objekt, es klont das Objekt nicht, es kopiert nur eine Referenz. Dies ist effizienter, wenn es mit der Struktur des von Ihnen verwendeten Objekts zusammenarbeitet, andernfalls können Sie die Tiefenkopie verwenden.

Wenn die Eigenschaften/Methoden, die Sie zum Speichern und Wiederherstellen benötigen, direkt untergeordnete Objekte des Hauptobjekts sind, sollte eine flache Kopie ausreichen. Hier ist ein Fall, in dem Sie auf jeden Fall eine tiefe Kopie bräuchten:

{ 
    url: 'test', 
    events: { 
     success: function(data) { 
      // ... 
     } 
    } 
} 

Hier haben wir ein Objekt mit einer events Eigenschaft, und diese Eigenschaft ist selbst ein Objekt mit einigen Eigenschaften/Methoden ihres eigenen (in diesem Beispiel einer . events.success() Methode wenn Sie eine flache Kopie dieses Objekts zu tun, das Original und die Kopie wird Aktien ein gemeinsames events Objekt Also, wenn Sie so etwas wie dies tat.

options.events.success = function(...) {...}; 

Sie tatsächlich zu aktualisieren würde, dass in sowohl options als auch old_options. Nein g ood. Das ist, wo eine tiefe Kopie benötigt wird.

+0

Das ist, was ich dachte, ich könnte tun. Sollte ich nur die jQuery 'extend'-Methode verwenden? –

+0

Perfekt, danke !! War nicht sicher, wie man die Frage formuliert, aber ich dachte, dass dies der Grund sein könnte. Danke für Ihre Zeit –

+0

Es ist ein wenig komplizierter als das, deshalb habe ich meinen Code nicht gepostet. Betrachten Sie es als eine jQuery Erweiterung (es ist mehr als das, aber nicht wirklich relevant). Wenn Sie '$ ('# foo'). Bar (options)' aufrufen, erzeugt er die Instanz und die Instanz der Leiste. Dann können Sie '$ ('# foo'). Bar ('save', function() {})' aufrufen, die die Speichermethode ausführen und den Callback nur einmal aufrufen und dann zu dem im Original spezifizierten Callback zurückkehren Beispiel 'Optionen' –

2

Ihr Problem ist, dass Sie mit Objekten arbeiten. Sie übergeben Referenzen auf das Objekt. Das zugrunde liegende Objekt ist das gleiche. Sie haben lediglich eine Variable hinzugefügt, die auf die gleiche Adresse zeigt.

Sie müssen das Objekt klonen. ZB erstellen Sie ein neues Objekt mit den gleichen Eigenschaften und kopieren Sie es.

Diese post kann Ihnen nützlich sein.

3

Das Problem ist, dass options Referenzsemantik hat:

// Store the old options 
var old_options = options; 

Dieser Kommentar liegt. Sie haben keine Kopie der alten Optionen; Stattdessen haben Sie einen weiteren Verweis auf das gleiche Optionsobjekt (ein anderer Name, mit dem Sie darauf verweisen können). Wenn Sie also options.success überschreiben, ist diese Änderung auch unter old_options sichtbar. Der Code, der .data verwendet, um den Wert der Optionen zu speichern und zurückzusetzen, ist redundant.

Das einzige, was Sie tun müssen, ist dies:

var old_success = options.success; 
options.success = function() { /* whatever */ }; 

// code that uses the new success callback 

options.success = old_success; // restore original value 
+0

@MichaelGeary hat es, aber nur einen Kommentar zu Ihrer Antwort. Ich habe diese Methode ursprünglich ausprobiert, aber es endete mit der Ausführung von 'options.success' ... –

+0

Auch wenn sich die Lösung als anders herausstellte, bekommst du ein herzhaftes +1 für diese Notiz: **" Dieser Kommentar liegt. " Das ist ein so wichtiger Punkt: Oftmals wird ein Fehler verursacht, weil wir denken, dass der Code tut, was ein Kommentar sagt - ob das ein expliziter Kommentar oder ein mentaler "Kommentar" ist - also unser Verständnis des Codes. Es ist allzu leicht, einen Fall zu übersehen, in dem wir denken, dass der Code eine Sache macht, aber tatsächlich eine andere. Das habe ich schon oft gemacht, so weiß ich es :-) –

2

Wenn Sie diese var old_options = options tun bist du nicht options Werte, sondern eine Referenz, um es zu kopieren. Von jetzt an zeigen old_options und options auf den gleichen Speicherpunkt und Änderungen an jedem von ihnen betreffen beide.

Verwandte Themen