0

For security purposes, I have a need for a "mirrored" object. i.e. if I create object A, and shallow-clone a copy of A and call it B, whenever a property of A changes, I hope to have B automatically update itself to reflect the changes, but not the other way around. In other words, one-way property syncing.

My question: is there already a solution out in the wild that I'm not aware of?

Was thinking of implementing a library with observe-js (https://github.com/polymer/observe-js), but thought I should ask around before proceeding. Thanks.

settinghead
  • 95
  • 1
  • 6
  • What target environments do you need to support? In particular, do you need to support IE8 and earlier? – T.J. Crowder Oct 03 '14 at 16:44
  • Somehow, this feels like it could be achieved through some clever use of the "prototype" property, but I'm not sure how. – Katana314 Oct 03 '14 at 16:48
  • @Katana314: The problem with that is that writing to B would hide A's value when you asked B for it later. I *think* that's not what the OP wants, but the OP seems to have asked-and-run, so... – T.J. Crowder Oct 03 '14 at 16:55
  • @T.J.Crowder Support for IE8 can be dropped if it proves to be a cumbersome endeavour. – settinghead Oct 03 '14 at 16:57
  • 1
    @settinghead: You basically can't do it in IE8 and earlier, because if I'm understanding the desired behavior, you need `Object.defineProperty`, which IE8 didn't properly support. (You want writes to `b` to be completely ignored, right? So if `b` is a front to `a`, `b.foo = "whatever"` shouldn't change what you get back from `b.foo`, right?) – T.J. Crowder Oct 03 '14 at 16:59
  • @T.J.Crowder that is correct. – settinghead Oct 03 '14 at 17:34
  • 1
    Obviously, the problem becomes a whole lot simpler if you're willing to demand everyone call a ".set(key, val)" function for every property. Since you're supporting old browsers it seems, I don't think you can complain too much about not getting to use neat, modern syntax for things. – Katana314 Oct 03 '14 at 17:56

2 Answers2

1

Assuming you don't need to support IE8 and earlier, you can use getters to do that on modern browsers.

function proxy(src) {
  var p = {};
  Object.keys(src).forEach(function(key) {
    Object.defineProperty(p, key, {
      get: function() {
        return src[key];
      }
    });
  });
  return p;
}

var a = {
  foo: "Original foo",
  bar: "Original bar"
};
var b = proxy(a);

console.log(b.foo);    // "Original foo"
a.foo = "Updated foo"; // Note we're writing to a, not b
console.log(b.foo);    // "Updated foo"
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • Doesn't look like this solution syncs object `b` with `a`. For instance, after executing the code in a browser console, when I do `a.foo = "Updated foo"`, `b.foo` remains the same as `"Original foo"`. – settinghead Oct 03 '14 at 16:54
  • 1
    @settinghead: Works for me: http://jsbin.com/yowiri/1 (stack snippets don't seem to be working at the moment...) – T.J. Crowder Oct 03 '14 at 16:56
  • With this solution, if `a` gets a new property, it wont show up in `b`, right? – Alex Wayne Oct 03 '14 at 17:47
  • 2
    @AlexWayne: Right. If that's a requirement, we should combine our two approaches somehow (hmmmm, not sure exactly how). Or wait for ES6's proxies, which are for *exactly* this kind of situation. – T.J. Crowder Oct 03 '14 at 18:16
0

You can setup the prototype chain for this:

var a = {};
var Syncer = function(){};
Syncer.prototype = a;
var b = new Syncer();

a.foo = 123;
b.foo; // 123

b.bar = 456;
a.bar // undefined

Any property not set on b directly will be looked for on the prototype object, which is a.

You can even wrap this up in a convenience function:

var follow = function(source) {
  var Follower = function(){};
  Follower.prototype = source;
  return new Follower();
}

var a = {};
var b = follow(a);

a.foo = 123;
b.bar = 456;

a.foo; // 123
b.foo; // 123

a.bar; // undefined
b.bar; // 456
Alex Wayne
  • 145,435
  • 42
  • 271
  • 302