Breakpoint on property change

Viewed 75205

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?

8 Answers

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.

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

Decided to write my own version of this solution, save it in a snippet in Chrome's DevTools, and wrapped it in an IIFE that should support both Node and Browsers. Also changed the observer to use a scope variable rather than a property on the object, such that there is no possibility of name clashes, and any code that enumerates keys will not "see" the new "private key" that is created:

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

Building on the excellent solution by Alexandos Katechis, here is a version of the snippet that does not disturb the original value of the property. I renamed it to better match what I'm thinking when I use it.

Usage:

  1. Add the snippet via Sources -> Snippets
  2. When needed, press Command-O and choose to run the breakOnChange snippet
  3. Call breakOnChange(anyObject, 'propertyName') in the console
  4. Take the action that causes the change
  5. Stops in debugger

This is very helpful for spotting things like a global library such as jQuery being stomped on by a third-party script.

(function (global) {
  global.breakOnChange = (obj, prop) => {
    let value = obj[prop]

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

      set: function (newValue) {
        debugger
        value = newValue
      },
    })
  }
})(typeof process !== 'undefined' ? process : window)
Related