I want to write a class, that deals with undefined properties. I also want to return this to be able to chain methods to create a domain specific language (DSL).
I return a Proxy from the constructor, to handle undefined properties. Now when testing the instance, it does happen, that return this does not prove to be identical with the instance. I fear bugs resulting from this, although I can chain the methods as intended.
This is a mocha chai test to show the behaviour. Replace o.that().should.not.equal(o); with o.that().should.equal(o); in the last instruction to see how it fails.
require('chai').should();
describe('chai testing classes using Proxy', () => {
it('asserts object identity, if an instance method returns this', () => {
const o = new class NormalClass{ }
o.that = function() { return this; }
o.that().should.equal(o);
});
it('observes the same behaviour for constructors returning a dummy Proxy', () => {
const o = new class ProxyClass{
constructor() { return new Proxy(this, {}); }
}
o.that = function() { return this; }
o.that().should.equal(o);
});
it('requires deep.equal on the other hand, if the Proxy handles get', () => {
const o = new class ProxyClassPlusGet{
constructor() {
return new Proxy(this, {
get: function(target, prop) { return target[prop]; },
});
}
}
o.that = function() { return this; }
o.that().should.deep.equal(o);
o.that().should.not.equal(o);
});
});
Advertisement
Answer
Your implementation works insofar as o.that() === o yields true.
But it does not work with getters, which interferes with chai’s should. You can reproduce this with
const o = new Proxy({
get self() { return this; },
that() { return this; },
}, {
get(target, prop) { return target[prop]; },
});
console.log(o.self === o);
console.log(o.that() === o);Why is that? Because your get trap is broken, ignoring the receiver of the property access. It will hold the proxy, o, but when you do return target[prop] then target will be the receiver. You can fix it by using Reflect.get:
const o = new Proxy({
get self() { return this; },
that() { return this; },
}, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
// ^^^^^^^^
},
});
console.log(o.self === o);
console.log(o.that() === o);