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);