Source: common/campaigns/utils.js

/**
 * Wrap object in a proxy, that calls callback
 * anytime a property is set, passing the property name as parameter.
 * Note that this doesn't modify any variable that pointer towards object,
 * so this is _not_ equivalent to replacing the target object with a proxy.
 */
function _watch(object, callback)
{
	return new Proxy(object, {
		"get": (obj, key) => {
			return obj[key];
		},
		"set": (obj, key, value) => {
			obj[key] = value;
			callback(key);
			return true;
		}
	});
}

/**
 * Inherit from AutoWatcher to make 'this' a proxy object that
 * watches for its own property changes and calls the method given
 * (takes a string because 'this' is unavailable when calling 'super').
 * This can be used to e.g. automatically call a rendering function
 * if a property is changed.
 * Using inheritance is necessary because 'this' is immutable,
 * and isn't defined in the class constructor _unless_ super() is called.
 * (thus you can't do something like this = new Proxy(this) at the end).
 */
class AutoWatcher
{
	constructor(method_name)
	{
		this._ready = false;
		return _watch(this, () => this._ready && this[method_name]());
	}
}