Point d'arrêt sur le changement de propriété

147

Firebug pour Firefox a une fonctionnalité intéressante, appelée "Break on property change", où je peux marquer n'importe quelle propriété de n'importe quel objet, et cela arrêtera l'exécution de JavaScript juste avant le changement.

J'essaie d'atteindre la même chose dans Google Chrome et je ne trouve pas la fonction dans le débogueur Chrome. Comment faire cela dans Google Chrome?

Arsen Zahray
la source
1
Si vous voulez faire cela avec des éléments HTML, voir stackoverflow.com/a/32686203/308851
chx

Réponses:

106

Si cela ne vous dérange pas de jouer avec la source, vous pouvez redéfinir la propriété avec un accesseur.

// 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
la source
2
y a-t-il un plug in qui ferait ça pour moi?
Arsen Zahray
3
@ArsenZahray, je sais pas. Cependant, vous pouvez en faire une fonction pratique et utiliser comme console.watch(obj, 'someProp').
katspaugh
5
Cela ne fonctionne pas pour les propriétés intégrées, par exemple window.locationpour des raisons de sécurité.
qJake
1
Pour déboguer les setters pour les éléments DOM, ce modèle doit être légèrement modifié. Voir mnaoumov.wordpress.com/2015/11/29/… pour plus de détails
mnaoumov
@katspaugh puis-je demander pourquoi vous en avez besoin obj._someProp = obj.someProp;, cela ne semble pas lié à ce que vous essayez d'archiver (probablement parce qu'il me manque quelque chose)
Victor
109

Edit 2016.03: Object.observeest obsolète et supprimé dans Chrome 50

Edit 2014.05: a Object.observeété ajouté dans Chrome 36

Chrome 36 est livré avec une Object.observeimplémentation native qui peut être exploitée ici:

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

Si vous ne le souhaitez que temporairement, vous devez stocker le rappel dans une variable et appeler une Object.unobservefois terminé:

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

Notez que lors de l'utilisation Object.observe, vous ne serez pas averti lorsque le devoir n'a rien changé, par exemple si vous avez écrit myObj.a = 1.

Pour voir la pile d'appels, vous devez activer l'option "Async call stack" dans Dev Tools:

pile d'appels asynchrones chrome


Réponse originale (2012.07):

Un console.watchsketch comme suggéré par @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;
       }
   });
}

Invocation:

console.watch(obj, "someProp");

Compatibilité:

  • Dans Chrome 20, vous pouvez le coller directement dans les outils de développement au moment de l'exécution!
  • Par souci d'exhaustivité: dans Firebug 1.10 (Firefox 14), vous devez l'injecter dans votre site Web (par exemple via Fiddler si vous ne pouvez pas éditer la source manuellement); malheureusement, les fonctions définies à partir de Firebug ne semblent pas fonctionner debugger(ou est-ce une question de configuration? veuillez me corriger alors), mais console.logfonctionne.

Éditer:

Notez que dans Firefox, console.watchexiste déjà, en raison du non-standard de Firefox Object.watch. Par conséquent, dans Firefox, vous pouvez surveiller les changements de manière native:

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

Cependant, cela sera bientôt (fin 2017) supprimé .

jakub.g
la source
1
À propos, il semble que l'impossibilité d'accéder au débogueur dans le code personnalisé soit une régression entre Firebug 1.8 et 1.9: problème 5757 -> duplicata du problème 5221
jakub.g
1
@ColeReed nous devons stocker la valeur quelque part pour la récupérer dans le getter; il ne peut pas être stocké dans oObj[sProp], car le getter entrerait dans une récursion infinie. Essayez-le dans Chrome, vous obtiendrez RangeError: Maximum call stack size exceeded.
jakub.g
1
Je "voudrais ajouter ceci, car la asynccase à cocher est si dorée avec cette approche: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp
1
@PhiLho, il est possible de voir la pile, avec la asynccase à cocher comme @cnp a écrit, voir ma mise à jour
jakub.g
1
Devrait mettre à jour cette réponse: Object.observeest obsolète et sera bientôt supprimée: voir: chromestatus.com/features/6147094632988672
Amir Gonnen
79

Il existe une bibliothèque pour cela: BreakOn ()

Si vous l'ajoutez aux outils de développement Chrome sous forme d'extrait (sources -> extraits -> clic droit -> nouveau -> collez -le ) , vous pouvez l'utiliser à tout moment.


Pour l'utiliser, ouvrez les outils de développement et exécutez l'extrait. Ensuite, pour interrompre le myObject.myPropertychangement, appelez ceci depuis la console de développement:

breakOn(myObject, 'myProperty');

Vous pouvez également ajouter la bibliothèque à la compilation de débogage de votre projet afin de ne pas avoir à appeler à breakOnnouveau chaque fois que vous actualisez la page.

BlueRaja - Danny Pflughoeft
la source
5

Cela peut également être fait en utilisant le nouveau proxy objet dont le but est exactement cela: intercepter les lectures et les écritures sur l'objet qui est encapsulé par le Proxy. Vous enveloppez simplement l'objet que vous souhaitez observer dans un proxy et utilisez le nouvel objet enveloppé au lieu de votre original.

Exemple:

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

Maintenant, utilisez wrappedObject où vous fourniriez originalObject à la place et examinez la pile d'appels lors d'une pause.

Dima Slivin
la source
Les mandataires setdoivent revenir truepour ne pas échouer pour les cas autres que suivis.
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
la source
1

J'ai décidé d'écrire ma propre version de cette solution, de l'enregistrer dans un extrait de code dans les DevTools de Chrome et de l'envelopper dans un IIFE qui devrait prendre en charge à la fois Node et Browsers. Également changé l'observateur pour utiliser une variable de portée plutôt qu'une propriété sur l'objet, de sorte qu'il n'y a aucune possibilité de conflits de noms, et tout code qui énumère les clés ne "verra" pas la nouvelle "clé privée" créée:

(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
la source
-2

Chrome intègre cette fonctionnalité dans les dernières versions https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints .

Donc plus besoin de bibliothèques et de solutions personnalisées, il suffit de cliquer avec le bouton droit sur l'élément DOM dans l'inspecteur et de choisir 'Break on' -> 'Attributs modifications' et c'est tout.

Ivica Puljic
la source
10
Il a demandé un changement de propriété (objet js), pas un changement de valeur d'attribut DOM
Z. Khullah
1
@Ivica C'est une bonne technique, mais ce n'est pas le bon endroit pour la mettre. Ce serait bien comme commentaire, mais pas comme réponse.
bnieland du