Skip to content

Instantly share code, notes, and snippets.

@tschneidereit
Last active December 19, 2015 14:29
Show Gist options
  • Save tschneidereit/5969286 to your computer and use it in GitHub Desktop.
Save tschneidereit/5969286 to your computer and use it in GitHub Desktop.
More principled implementation
LazySet_handlers = {
has: function(target, name) {
if (target.inHas) {
return true;
}
target.inHas = true;
var result = LazySet_installForwarder(target, name);
target.inHas = false;
target.hasCalled = true;
return result;
},
get: function(target, name, receiver) {
if (!LazySet_ensureForwarder(target, name)) {
return undefined;
}
return receiver[name];
},
set: function(target, name, val, receiver) {
if (!LazySet_ensureForwarder(target, name)) {
return undefined;
}
return receiver[name] = val;
}
}
function LazySet(sources /* : Array<Object> */) {
var target = {
sources: sources,
receiver: this
}
this.sources = sources;
this.__proto__ = Proxy(target, LazySet_handlers);
}
/*
* SpiderMonkey before FF 25 has a bug (862848) that causes the [[Has]] trap to be
* called before the [[Get]] or [[Set]] trap is, if the receiver has a proxy
* on its prototype chain. This works around that bug. Without that bug, only the
* [[Get]] and [[Set]] traps would even be required.
* (TODO: Test if this works once the bug is fixed.)
*/
function LazySet_ensureForwarder(target, name) {
if (target.hasCalled) {
target.hasCalled = false;
return true;
} else {
return LazySet_installForwarder(target, name);
}
}
function LazySet_installForwarder(target, name) {
var sources = target.sources;
for (var i = sources.length; i--;) {
if (name in sources[i]) {
Object.defineProperty(target.receiver, name, {
get: new Function("return this.sources['" + i + "']['" + name + "'];"),
set: new Function("v", "this.sources['" + i + "']['" + name + "'] = v;")
});
return true;
}
}
return false;
}
/* Testing code from here */
var a = {foo: 'bar'};
var b = {zorb: 'zap'};
var c = {cloob: 'barf'};
var rec = new LazySet([a, b, c]);
print("installing forwarders:");
print(rec.foo);
print(rec.zorb);
print(rec.cloob);
print(rec.notthere);
print("using forwarders:");
print(rec.foo);
print(rec.zorb);
print(rec.cloob);
print(rec.notthere);
print("modifying value:");
rec.foo = {toString: function() {return 'baz' + Math.random()}};
print(rec.foo, a.foo, rec.foo === a.foo);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment