Why has Object.observe() been deprecated

Is there an alternative approach?

Is there another way to do change detection in object?

There is the Proxy method, but can anyone tell me how can I achieve this using Proxy:

var obj = {
  foo: 0,
  bar: 1
};

Object.observe(obj, function(changes) {
  console.log(changes);
});

obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]

obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]

Solution 1:

You can achieve this with getters and setters.

var obj = {
  get foo() {
    console.log({ name: 'foo', object: obj, type: 'get' });
    return obj._foo;
  },
  set bar(val) {
    console.log({ name: 'bar', object: obj, type: 'set', oldValue: obj._bar });
    return obj._bar = val;
  }
};

obj.bar = 2;
// {name: 'bar', object: <obj>, type: 'set', oldValue: undefined}

obj.foo;
// {name: 'foo', object: <obj>, type: 'get'}

Alternatively, in a browser with support for Proxies, you can write a more generic solution.

var obj = {
  foo: 1,
  bar: 2
};

var proxied = new Proxy(obj, {
  get: function(target, prop) {
    console.log({ type: 'get', target, prop });
    return Reflect.get(target, prop);
  },
  set: function(target, prop, value) {
    console.log({ type: 'set', target, prop, value });
    return Reflect.set(target, prop, value);
  }
});

proxied.bar = 2;
// {type: 'set', target: <obj>, prop: 'bar', value: 2}

proxied.foo;
// {type: 'get', target: <obj>, prop: 'bar'}

Solution 2:

Disclaimer: I'm the author of the object-observer library suggested below.

I'd not go with getters/setters solution - it's complicated, not scalable and not maintainable. Backbone did their two-way binding that way and the boilerplate to get it working correctly was quite a piece of a code.

Proxies is the best way to achieve what you need, just add to the examples above some callbacks registration and management and execute them upon a changes.

As regarding to the polyfill libraries: some/most of these implemented utilizing 'dirty check' or polling technique - not efficient, not performant. Occasionally, this is the case of the polyfill pointed out by Nirus above.

I'd recommend to pick up some library that does observation via Proxies. There are a few out there, object-observer being one of them: written for this use-case exactly, utilizes native Proxies, provides deep-tree observation etc.

Solution 3:

@Dan Prince solution should be the first choice always.

Just in case for some reason if you want to support browsers that are quite older, i would suggest you to go for any polyfill libraries available on Github or use Object.defineProperties API which is supported in IE 9 to emulate the same.

var obj = Object.defineProperties({}, {
    "foo":{
        get:function(){
            console.log("Get:"+this.value);
        },
        set:function(val){
            console.log("Set:"+val);
            this.value = val;
        }
    },

    "bar":{         
        get:function(){
            console.log("Get:"+this.value);
        },
        set:function(val){
            console.log("Set:"+val);
            this.value = val;
        }
    }
 });

Note: This is not a scalable solution. Make an educated decision whether to use the above API for larger data objects and computation intensive requirements.