Search code examples
javascriptgetter-setter

Using getter and setter on an object property that has his own properties


I'm fairly new to getters and setters and am looking for a way to listen for changes in an object to store the data immediately, without calling a Save() function everytime a value gets changed. This is how I do it right now:

var myObject = {
    Data: {
        enabled: true,
        show: false
    },
    Save: function () {
        //store myObject.Data to local storage
    },
    Load: function () {
        //load data from local storage and assign it to myObject.Data
    },
    doSomething: function () {
        myObject.Load();
        if (myObject.Data.enabled) {
            myObject.Data.show = true;
            myObject.Save();
        }
    }

Now I would like to optimize this code so everytime a property in myObject.Data is changed, myObject.Save() is executed. The problem I'm experiencing is that it seems only possible to define a getter for a property that has just one value, but not for a property that is an object itself.

var myObj = {
    _Data: {
        a: 0,
        b: 1,
        c: 3
    },
    set Data (a) {
        console.log(a);
    }
};

myObj.Data.a = 2;

This obviously doesn't work since myObj.Data is not an object and doesn't have the same properties as myObj._Data.

Thanks in advance for any help.


Solution

  • You are likely interested in the Proxy object.

    I used a very simple debounce function callHandler in order to avoid calling the onSet method dozens of times during array modifications. Otherwise, [1, 2, 3].splice(0, 1) would call the set handler once per item in the original array.

    'use strict';
    var myObject = {
        Data: {
            a: [1, 2, 3],
            b: {c: ['test']}
        },
        Save: function() {
            console.log('Save called');
        },
    }
    
    function recursiveProxy(target, onSet) {
        // For performance reasons, onSet will only be called one millesecond
        // after the set handler has last been called.
        var timeout;
        function callHandler() {
            clearTimeout(timeout);
            timeout = setTimeout(onSet, 1);
        }
    
        var recursiveHandler = {
            get: function(target, property) {
                // If the property is something that could contain another object,
                // we want to proxy it's properties as well.
                if (typeof target[property] == 'object' && target[property] != null) {
                    return new Proxy(target[property], recursiveHandler);
                }
    
                return target[property];
            },
            set: function(target, property, value) {
                console.log('Set called - queueing onSet');
                callHandler();
                target[property] = value;
                return true;
            }
        }
    
        return new Proxy(target, recursiveHandler);
    }
    
    myObject.Data = recursiveProxy(myObject.Data, myObject.Save);
    
    myObject.Data.a.splice(0, 1);
    myObject.Data.b.c[0] = 'test 2';