Raynos
Raynos

Reputation: 169451

Javascript object encapsulation that tracks changes

Is it possible to create an object container where changes can be tracked

Said object is a complex nested object of data. (compliant with JSON).

The wrapper allows you to get the object, and save changes, without specifically stating what the changes are

Does there exist a design pattern for this kind of encapsulation

Deep cloning is not an option since I'm trying to write a wrapper like this to avoid doing just that.

The solution of serialization should only be considered if there are no other solutions.

An example of use would be

var foo = state.get();
// change state
state.update(); // or state.save();
client.tell(state.recentChange());

A jsfiddle snippet might help : http://jsfiddle.net/Raynos/kzKEp/

It seems like implementing an internal hash to keep track of changes is the best option.

[Edit]

To clarify this is actaully done on node.js on the server. The only thing that changes is that the solution can be specific to the V8 implementation.

Upvotes: 5

Views: 3116

Answers (5)

Raynos
Raynos

Reputation: 169451

It would appear that node-proxy implements a way of doing this by wrapping a proxy around the entire object. I'll look into more detail as to how it works.

https://github.com/samshull/node-proxy

Upvotes: 0

David Tang
David Tang

Reputation: 93684

Stripping away the javascript aspect of this problem, there are only three ways to know if something has changed:

  1. Keep a copy or representation to compare with.
  2. Observe the change itself happening in-transit.
  3. Be notified of the change.

Now take these concepts back to javascript, and you have the following patterns:

  1. Copy: either a deep clone, full serialization, or a hash.
  2. Observe: force the use of a setter, or tap into the javascript engine (not very applicable)
  3. Notify: modifying the code that makes the changes to publish events (again, not very applicable).

Seeing as you've ruled out a deep clone and the use of setters, I think your only option is some form of serialisation... see a hash implementation here.

Upvotes: 3

Na7coldwater
Na7coldwater

Reputation: 1414

You can use the solution that processing.js uses. Write the script that accesses the wrapped object normally...

var foo = state.get();
foo.bar = "baz";
state.update();
client.tell(state.recentChange());

...but in the browser (or on the server if loading speed is important) before it runs, parse the code and convert it to this,

var foo = state.get();
state.set(foo, "bar", "baz");
state.update();
client.tell(state.recentChange());

This could also be used to do other useful things, like operator overloading:

// Before conversion
var a=new Vector(), b=new Vector();
return a + b * 3; 

// After conversion
var a=new Vector(), b=new Vector();
return Vector.add(a,Vector.multiply(b,3));

Upvotes: 1

Gonzalo Larralde
Gonzalo Larralde

Reputation: 3541

You'll have to wrap all your nested objects with a class that reports you when something changes. The thing is, if you put an observer only in the first level object, you'll only receive notifications for the properties contained in this object.

For example, imagine you have this object:

var obj = new WrappedObject({
    property1: {
        property1a: "foo",
        property1b: 20,
    }
})

If you don't wrap the object contained in porperty1, you'll only receive a "get" event for property1, and just that, because when someone runs obj.property1.property1a = "bar" the only interaction that you'll have with obj, will be when it asks for the reference of the object contained in property1, and the modification will happen in an unobserved object.

The best approach I can imagine, is iterating over all the properties when you wrap the first object, and constructing recursively a wrapper object for every typeOf(property) == "Object".

I hope my understanding of your question was right. Sorry if not! It's my first answer here :$.

Upvotes: 2

jon_darkstar
jon_darkstar

Reputation: 16778

There's something called reactive programming that kind of resembles what you ask about, but its more involved and would probably be overkill.

It seems like you would like to keep a history of values, correct? This shouldn't be too hard as long as you restrit changes to a setter function. Of course, this is more difficult in javascript than it is in some other languages. Real private fields demand some clever use of closures.

Assuming you can do all of that, just write something like this into the setter.

function setVal(x)
{
    history.push(value);
    value = x;
}

Upvotes: 1

Related Questions