gnarois
gnarois

Reputation: 339

Detecting changes in a JavaScript class property?

Assume you have this class:

class Foo {
  constructor() {
    this.count = 0
  }
  increaseCountEventually() {
    setTimeout(() => {
      this.count += 1
    }, Math.floor(Math.random() * 1000))
  }
}

Then you create an instance of this class:

const foo = new Foo()

Then you call the increaseCountEventually method which will, at some random point in time, increment the value of its property this.count:

foo.increaseCountEventually()

So, in a few milliseconds count will increase by 1. Is there a trick to listen for changes in the count property of the foo instance? Something like an event listener, where the event would be the change of the count property. Something like this (pseudo-code):

// A bit like DOM events
foo.addEventListener('property count changes', () => console.log('it changed!')) 

// Or, in other terms:
foo.listenForCountChange(() => console.log('it changed!')

I could use setInterval and check every millisecond or so to see if count has changed, but I'm looking for a more efficient way. I read about using a "Proxy" but not sure how that would work.

Upvotes: 2

Views: 1997

Answers (2)

traktor
traktor

Reputation: 19301

Another fairly way to monitor property updates is to store the external property under another name internally, say _count for count, and then use the external property name to set up a setter/getter pair. The setter can take whatever action is needed when the value is updated. E.G.:

class Foo {
  constructor() {
    this._count = 0; // internal property name
  }
  increaseCountEventually() {
    setTimeout(() => {
      this.count += 1
    }, Math.floor(Math.random() * 1000))
  }
  set count(value) { // external property name
    this._count = value;
    console.log( `count setter set _count to ${value}`);
  }
  get count() {
    return this._count;
  }
}

const foo = new Foo();
console.log("foo.count = %s", foo.count);
foo.increaseCountEventually()
setTimeout( ()=>console.log("foo.count = %s", foo.count), 2000);

By default the _count property would be enumerable on class instances, and not write protected. The constructor could add _count as a non-enumerable property if so desired, along the lines of:

constructor() {
  // make _count non enumerable:

  Object.defineProperty(this, '_count', {
     value: 0,
     enumerable: false,
     writable: true
  });
}

Upvotes: 1

Aditya Menon
Aditya Menon

Reputation: 774

Javascript Proxy are meant to be used on Objects. If its okay to convert count into an object or keep an object called data with property count then you could add a proxy with a set trap

In your case however, you could modify increaseCountEventually to accept a function like so

increaseCountEventually(callback) {
    setTimeout((callback) => {
      this.count += 1,
      callback();
    }, Math.floor(Math.random() * 1000))
  }

Upvotes: 1

Related Questions