0

Is there a way to cause the debugger to pause whenever a certain object or a certain property of an object changes?

watch() has been deprecated. The recommendation (see here and here) is instead to use setters and getters or Proxy objects.

But how exaclty can Proxy objects be used for that? Do I have to replace some object by its proxy everywhere in the code, i.e. no way to debug pre-existing code as-is and immediately?

Same for the mentioned method using setters and getters?

breakOn() (see here) seems to break only on changes of HTML elements, not JavaScript objects.

root
  • 1,812
  • 1
  • 12
  • 26

1 Answers1

0

Do I have to replace some object by its proxy everywhere in the code, i.e. no way to debug pre-existing code as-is and immediately?

That might be easiest, but it isn't the only option.

If you know exactly which object, i.e. you have a reference to it, you can replace its property with a getter/setter:

const obj = { abc: 123 };

function watchProperty(obj, key) {
    const desc = Object.getOwnPropertyDescriptor(obj, key);
    let value = desc.value;
    Object.defineProperty(obj, key, {
        enumerable: desc.enumerable,
        get() {
            console.log('GET', key);
            if (desc.writable === undefined) {
                if (desc.get) return desc.get.call(obj);
                return undefined;
            } else {
                return value;
            }
        },
        set(val) {
            console.log('SET', key, val);
            if (desc.writable === undefined) {
                if (desc.set) desc.set.call(obj, val);
            } else if (desc.writable) {
                value = val;
            }
        },
    });
}

watchProperty(obj, 'abc');
console.log('obj.abc:', obj.abc);
obj.abc = 456;
console.log('obj.abc:', obj.abc);

Based on the ECMAScript 2022 specification.

Mind that to be able to use Object.defineProperty, the object shouldn't already have that property defined with configurable set to false. Luckily, this only happens if Object.defineProperty was used in the first place.

I used Object.getOwnPropertyDescriptor and made the property behave obj-specific. If your object has an inherited property, you'd have to change it in the prototype chain, but with my implementation of watchProperty that would lead to some weird interactions since value and obj are obj-specific.

That also brings me to another point: if you want to watch an (or every) instance of a class, you can watch the property on the prototype.

Kelvin Schoofs
  • 8,323
  • 1
  • 12
  • 31