-->

Breakpoint on property change

2019-01-02 19:46发布

问题:

Firebug for Firefox has a nice feature, called "Break on property change", where I can mark any property of any object, and it will stop JavaScript execution right before the change.

I'm trying to achieve the same in Google Chrome, and I can't find the function in Chrome debugger. How do I do this in Google Chrome?

回答1:

If you don't mind messing around with the source, you could redefine the property with an accessor.

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


回答2:

Edit 2016.03: Object.observe is deprecated and removed in Chrome 50

Edit 2014.05: Object.observe was added in Chrome 36

Chrome 36 ships with native Object.observe implementation that can be leveraged here:

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

If you want it only temporarily, you should store callback in a variable and call Object.unobserve when done:

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

Note that when using Object.observe, you'll not be notified when the assignment didn't change anything, e.g. if you've written myObj.a = 1.

To see the call stack, you need to enable "async call stack" option in Dev Tools:


Original answer (2012.07):

A console.watch sketch as suggested by @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");

Compatibility:

  • In Chrome 20, you can paste it directly in Dev Tools at runtime!
  • For completeness: in Firebug 1.10 (Firefox 14), you have to inject it in your website (e.g. via Fiddler if you can't edit the source manually); sadly, functions defined from Firebug don't seem to break on debugger (or is it a matter of configuration? please correct me then), but console.log works.

Edit:

Note that in Firefox, console.watch already exists, due to Firefox's non-standard Object.watch. Hence in Firefox, you can watch for changes natively:

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

However, this will be soon (late 2017) removed.



回答3:

There is a library for this: BreakOn()

If you add it to Chrome dev tools as a snippet (sources --> snippets --> right-click --> new --> paste this), you can use it anytime.


To use it, open the dev-tools and run the snippet. Then to break when myObject.myProperty is changed, call this from the dev-console:

breakOn(myObject, 'myProperty');

You could also add the library to your project's debug-build so you don't need to call breakOn again every time you refresh the page.



回答4:

This can also be done by using the new Proxy object whose purpose is exactly that: intercepting the reads and writes to the object that is wrapped by the Proxy. You simply wrap the object you would like to observe into a Proxy and use the new wrapped object instead of your original one.

Example:

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

Now use wrappedObject where you would supply originalObject instead and examine the call stack on break.



回答5:

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


回答6:

Chrome has this feature built-in in latest versions https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints.

So no more needs for custom libraries and solutions, just right click on DOM element in the inspector and choose 'Break on' -> 'attribute modifications' and that's it.



标签: