Punkt przerwania przy zmianie właściwości

147

Firebug dla Firefoksa ma fajną funkcję o nazwie „Przerwij przy zmianie właściwości”, w której mogę oznaczyć dowolną właściwość dowolnego obiektu i zatrzyma wykonywanie JavaScript tuż przed zmianą.

Próbuję osiągnąć to samo w Google Chrome i nie mogę znaleźć funkcji w debugerze Chrome. Jak to zrobić w Google Chrome?

Arsen Zahray
źródło
1
Jeśli chcesz to zrobić z elementami HTML, zobacz stackoverflow.com/a/32686203/308851
chx

Odpowiedzi:

106

Jeśli nie masz nic przeciwko mieszaniu się ze źródłem, możesz przedefiniować właściwość za pomocą akcesorium.

// 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
źródło
2
czy jest wtyczka, która zrobiłaby to za mnie?
Arsen Zahray
3
@ArsenZahray, nie wiem. Możesz jednak zrobić z tego poręczną funkcję i użyć podobnego console.watch(obj, 'someProp').
katspaugh
5
Nie działa to w przypadku właściwości wbudowanych, na przykład window.locationze względów bezpieczeństwa.
qJake,
1
Aby debugować metody ustawiające elementy DOM, należy nieco zmodyfikować ten wzorzec. Więcej szczegółów można znaleźć na stronie mnaoumov.wordpress.com/2015/11/29/ ...
mnaoumov
@katspaugh czy mogę zapytać, dlaczego tego potrzebujesz obj._someProp = obj.someProp;, wydaje się to niezwiązane z tym, co próbujesz zarchiwizować (prawdopodobnie dlatego, że czegoś brakuje)
Victor
109

Edycja 2016.03: Object.observezostała wycofana i usunięta w przeglądarce Chrome 50

Edycja 2014.05: Object.observezostała dodana w Chrome 36

Chrome 36 jest dostarczany z natywną Object.observeimplementacją, którą można wykorzystać tutaj:

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

Jeśli chcesz to tylko tymczasowo, powinieneś przechowywać callback w zmiennej i wywołać Object.unobservepo zakończeniu :

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

Zwróć uwagę, że podczas używania Object.observenie zostaniesz powiadomiony, gdy przydział niczego nie zmienił, np. Jeśli napisałeś myObj.a = 1.

Aby zobaczyć stos wywołań, musisz włączyć opcję „async call stack” w Dev Tools:

stos wywołań asynchronicznych chrome


Oryginalna odpowiedź (2012.07):

console.watchSzkic jak sugeruje @katspaugh:

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

Wezwanie:

console.watch(obj, "someProp");

Zgodność:

  • W Chrome 20 możesz wkleić go bezpośrednio w narzędziach deweloperskich w czasie wykonywania!
  • Dla kompletności: w Firebug 1.10 (Firefox 14) musisz wstrzyknąć go do swojej witryny (np. Przez Fiddlera, jeśli nie możesz ręcznie edytować źródła); niestety, funkcje zdefiniowane w Firebug nie wydają się działać debugger(czy jest to kwestia konfiguracji? proszę mnie w takim razie poprawić), ale console.logdziałają.

Edytować:

Zauważ, że w Firefoksie console.watchjuż istnieje z powodu niestandardowego Firefoksa Object.watch. Dlatego w Firefoksie możesz natywnie obserwować zmiany:

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

Jednak wkrótce (koniec 2017 r.) Zostanie to usunięte .

jakub.g
źródło
1
Nawiasem mówiąc, wydaje się, że nie można trafić do debuggera w niestandardowym kodzie, co jest regresją między Firebug 1.8 a 1.9: wydanie 5757 -> duplikat numeru 5221
jakub.g
1
@ColeReed musimy gdzieś przechowywać wartość, aby pobrać ją w getterze; nie może być przechowywany w oObj[sProp], ponieważ metoda pobierająca przeszłaby w nieskończoną rekurencję. Wypróbuj w Chrome, otrzymasz RangeError: Maximum call stack size exceeded.
jakub.g
1
Chciałbym to dodać, ponieważ pole asyncwyboru jest tak złote w tym podejściu: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp
1
@PhiLho można zobaczyć stos, z asynccheckboxem jak napisał @cnp, zobacz moją aktualizację
jakub.g
1
Należy zaktualizować tę odpowiedź: Object.observejest przestarzała i wkrótce zostanie usunięta: patrz: chromestatus.com/features/6147094632988672
Amir Gonnen
79

Jest do tego biblioteka: BreakOn ()

Jeśli dodasz go do narzędzi deweloperskich Chrome jako fragment (źródła -> fragmenty -> kliknij prawym przyciskiem myszy -> nowy -> wklej to ) , możesz go użyć w dowolnym momencie.


Aby z niego skorzystać, otwórz narzędzia deweloperskie i uruchom fragment. Następnie, aby przerwać myObject.myPropertyzmianę, wywołaj to z konsoli programisty:

breakOn(myObject, 'myProperty');

Możesz również dodać bibliotekę do kompilacji debugowania projektu, aby nie trzeba było wywoływać breakOnponownie za każdym razem, gdy odświeżasz stronę.

BlueRaja - Danny Pflughoeft
źródło
5

Można to również zrobić za pomocą nowego serwera proxy obiektu którego celem jest dokładnie to: przechwytywanie odczytów i zapisów do obiektu opakowanego przez serwer proxy. Po prostu zawijasz obiekt, który chcesz obserwować, do Proxy i używasz nowego opakowanego obiektu zamiast oryginalnego.

Przykład:

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

Teraz użyj wrappedObject, w którym zamiast tego możesz podać originalObject i zbadać stos wywołań po przerwaniu.

Dima Slivin
źródło
Pełnomocnik setmusi zwrócić, trueaby nie zawiódł w innych przypadkach niż śledzone.
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
źródło
1

Postanowiłem napisać własną wersję tego rozwiązania, zapisać go we fragmencie w Chrome DevTools i umieścić w IIFE, który powinien obsługiwać zarówno węzeł, jak i przeglądarki. Zmieniono także obserwatora, aby używał zmiennej zakresu zamiast właściwości obiektu, tak że nie ma możliwości kolizji nazw, a żaden kod wyliczający klucze nie „zobaczy” nowego utworzonego „klucza prywatnego”:

(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
źródło
-2

Chrome ma tę funkcję wbudowaną w najnowsze wersje https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints .

Więc koniec z niestandardowymi bibliotekami i rozwiązaniami, po prostu kliknij prawym przyciskiem myszy element DOM w inspektorze i wybierz „Przerwij” -> „modyfikacje atrybutów” i to wszystko.

Ivica Puljic
źródło
10
Poprosił o zmianę właściwości (obiekt js), a nie zmianę wartości atrybutu DOM
Z. Khullah
1
@Ivica To dobra technika, ale to niewłaściwe miejsce, aby ją umieścić. Byłby w porządku jako komentarz, ale nie jako odpowiedź.
bnieland