Haltepunkt bei Eigenschaftsänderung

147

Firebug für Firefox verfügt über eine nette Funktion namens "Bei Eigenschaftsänderung unterbrechen", mit der ich jede Eigenschaft eines Objekts markieren kann und die JavaScript-Ausführung unmittelbar vor der Änderung stoppt.

Ich versuche, dasselbe in Google Chrome zu erreichen, und kann die Funktion im Chrome-Debugger nicht finden. Wie mache ich das in Google Chrome?

Arsen Zahray
quelle
1
Wenn Sie dies mit HTML-Elementen tun möchten, siehe stackoverflow.com/a/32686203/308851
chx

Antworten:

106

Wenn es Ihnen nichts ausmacht, mit der Quelle herumzuspielen, können Sie die Eigenschaft mit einem Accessor neu definieren.

// original object
var obj = {
    someProp: 10
};

// save in another property
obj._someProp = obj.someProp;

// overwrite with accessor
Object.defineProperty(obj, 'someProp', {
    get: function () {
        return obj._someProp;
    },

    set: function (value) {
        debugger; // sets breakpoint
        obj._someProp = value;
    }
});
katspaugh
quelle
2
Gibt es einen Stecker, der das für mich tun würde?
Arsen Zahray
3
@ ArsenZahray, keine Ahnung. Sie können jedoch eine praktische Funktion daraus machen und like verwenden console.watch(obj, 'someProp').
Katspaugh
5
Dies funktioniert nicht für integrierte Eigenschaften, z. B. window.locationaus Sicherheitsgründen.
qJake
1
Um Setter für DOM-Elemente zu debuggen, sollte dieses Muster leicht geändert werden. Siehe mnaoumov.wordpress.com/2015/11/29/… für weitere Details
mnaoumov
@katspaugh kann ich fragen, warum Sie dies brauchen obj._someProp = obj.someProp;, es scheint nicht in Bezug auf das, was Sie zu erreichen versuchen (wahrscheinlich, weil ich etwas vermisse)
Victor
109

Edit 2016.03: Object.observeist in Chrome 50 veraltet und entfernt

Edit 2014.05: Object.observewurde in Chrome 36 hinzugefügt

Chrome 36 wird mit einer nativen Object.observeImplementierung geliefert, die hier genutzt werden kann:

myObj = {a: 1, b: 2};
Object.observe(myObj, function (changes){
    console.log("Changes:");
    console.log(changes);
    debugger;
})
myObj.a = 42;

Wenn Sie es nur vorübergehend möchten, sollten Sie den Rückruf in einer Variablen speichern und aufrufen, Object.unobservewenn Sie fertig sind:

myObj = {a: 1, b: 2};
func = function() {debugger;}
Object.observe(myObj, func);
myObj.a = 42;
Object.unobserve(myObj, func);
myObj.a = 84;

Beachten Sie, dass Sie bei der Verwendung Object.observenicht benachrichtigt werden, wenn die Zuordnung nichts geändert hat, z. B. wenn Sie geschrieben haben myObj.a = 1.

Um den Aufrufstapel anzuzeigen, müssen Sie die Option "Asynchroner Aufrufstapel" in den Entwicklungstools aktivieren:

Chrome Async Call Stack


Ursprüngliche Antwort (2012.07):

Eine console.watchSkizze wie von @katspaugh vorgeschlagen:

var console = console || {}; // just in case
console.watch = function(oObj, sProp) {
   var sPrivateProp = "$_"+sProp+"_$"; // to minimize the name clash risk
   oObj[sPrivateProp] = oObj[sProp];

   // overwrite with accessor
   Object.defineProperty(oObj, sProp, {
       get: function () {
           return oObj[sPrivateProp];
       },

       set: function (value) {
           //console.log("setting " + sProp + " to " + value); 
           debugger; // sets breakpoint
           oObj[sPrivateProp] = value;
       }
   });
}

Aufruf:

console.watch(obj, "someProp");

Kompatibilität:

  • In Chrome 20 können Sie es zur Laufzeit direkt in Dev Tools einfügen!
  • Der Vollständigkeit halber: In Firebug 1.10 (Firefox 14) müssen Sie es in Ihre Website einfügen (z. B. über Fiddler, wenn Sie die Quelle nicht manuell bearbeiten können). Leider scheinen die in Firebug definierten Funktionen nicht zu funktionieren debugger(oder ist es eine Frage der Konfiguration? Bitte korrigieren Sie mich dann), aber sie console.logfunktionieren.

Bearbeiten:

Beachten Sie, dass in Firefox console.watchbereits vorhanden ist, da Firefox nicht dem Standard entspricht Object.watch. Daher können Sie in Firefox nativ auf Änderungen achten:

>>> var obj = { foo: 42 }
>>> obj.watch('foo', function() { console.log('changed') })
>>> obj.foo = 69
changed
69

Dies wird jedoch bald (Ende 2017) entfernt .

jakub.g
quelle
1
Übrigens scheint es eine Regression zwischen Firebug 1.8 und 1.9 zu sein, Debugger im benutzerdefinierten Code nicht treffen zu können: Ausgabe 5757 -> Duplikat von Ausgabe 5221
jakub.g
1
@ColeReed Wir müssen den Wert irgendwo speichern, um ihn im Getter abzurufen. es kann nicht gespeichert werden oObj[sProp], da der Getter eine unendliche Rekursion eingeben würde. Probieren Sie es in Chrome aus RangeError: Maximum call stack size exceeded.
Jakub.g
1
Ich möchte dies hinzufügen, da das asyncKontrollkästchen bei diesem Ansatz so golden ist: html5rocks.com/de/tutorials/developertools/async-call-stack
cnp
1
@PhiLho es ist möglich, den Stapel zu sehen, mit dem asyncKontrollkästchen, wie @cnp schrieb, siehe mein Update
jakub.g
1
Sollte diese Antwort aktualisieren: Object.observeist veraltet und wird bald entfernt: siehe: chromestatus.com/features/6147094632988672
Amir Gonnen
78

Hierfür gibt es eine Bibliothek: BreakOn ()

Wenn Sie es auf Chrome Entwickler - Tools als Snippet hinzufügen (Quellen -> Snippets -> Rechtsklick -> Neu -> fügen diese ) , können Sie es jederzeit benutzen.


Öffnen Sie dazu die dev-tools und führen Sie das Snippet aus. Um zu brechen, wann myObject.myPropertygeändert wird, rufen Sie dies von der Dev-Konsole aus auf:

breakOn(myObject, 'myProperty');

Sie können die Bibliothek auch zum Debug-Build Ihres Projekts hinzufügen, damit Sie nicht breakOnjedes Mal erneut aufrufen müssen, wenn Sie die Seite aktualisieren.

BlueRaja - Danny Pflughoeft
quelle
5

Dies kann auch mithilfe des neuen Proxy- Objekts erfolgen, dessen Zweck genau darin besteht: das Abfangen der Lese- und Schreibvorgänge in das vom Proxy umschlossene Objekt. Sie verpacken einfach das Objekt, das Sie beobachten möchten, in einen Proxy und verwenden das neue verpackte Objekt anstelle Ihres ursprünglichen.

Beispiel:

const originalObject = {property: 'XXX', propertyToWatch: 'YYY'};
const watchedProp = 'propertyToWatch';
const handler = {
  set(target, key, value) {
    if (key === watchedProp) {
      debugger;
    }
    target[key] = value;
  }
};
const wrappedObject = new Proxy(originalObject, handler);

Verwenden Sie nun wrappedObject, wo Sie stattdessen originalObject angeben würden, und untersuchen Sie den Aufrufstapel bei Unterbrechung.

Dima Slivin
quelle
Proxy setmuss zurückkehren, truedamit es nicht für andere als verfolgte Fälle fehlschlägt.
Keaukraine
4
function debugProperty(obj, propertyName) {
  // save in another property
  obj['_' + propertyName] = obj[propertyName];

  // overwrite with accessor
  Object.defineProperty(obj, propertyName, {
    get: function() {
      return obj['_' + propertyName];
    },

    set: function(value) {
      debugger; // sets breakpoint
      obj['_' + propertyName] = value;
    }
  });
}
Roland Soós
quelle
1

Beschlossen, meine eigene Version dieser Lösung zu schreiben, sie in einem Snippet in den DevTools von Chrome zu speichern und in ein IIFE zu verpacken, das sowohl Node als auch Browser unterstützen soll. Außerdem hat der Beobachter geändert, dass eine Bereichsvariable anstelle einer Eigenschaft für das Objekt verwendet wird, sodass keine Möglichkeit von Namenskonflikten besteht und Code, der Schlüssel auflistet, den neu erstellten "privaten Schlüssel" nicht "sieht":

(function (global) {
  global.observeObject = (obj, prop) => {
    let value

    Object.defineProperty(obj, prop, {
      get: function () {
        return value
      },

      set: function (newValue) {
        debugger
        value = newValue
      },
    })
  }
})(typeof process !== 'undefined' ? process : window)
Alexandros Katechis
quelle
-2

In Chrome ist diese Funktion in den neuesten Versionen https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints integriert .

Sie brauchen also keine benutzerdefinierten Bibliotheken und Lösungen mehr. Klicken Sie einfach mit der rechten Maustaste auf das DOM-Element im Inspektor und wählen Sie "Unterbrechen" -> "Attributänderungen".

Ivica Puljic
quelle
10
Er bat um (js Objekt) Eigenschaftsänderung, nicht um Änderung des DOM-Attributwerts
Z. Khullah
1
@Ivica Dies ist eine gute Technik, aber dies ist der falsche Ort, um es auszudrücken. Es wäre gut als Kommentar, aber nicht als Antwort.
Bnieland