I have the following method to map a command to a function inside an object:
const mapHandler = (str, obj) => {
if (!Reflect.has(obj, str)) {
return null;
}
const candidateHandler = Reflect.get(obj, str, obj);
if (typeof candidateHandler !== "function") {
return null;
}
return candidateHandler;
};
I have a simple class holding some commands I want to invoke using the handler:
class MyClass {
obj;
constructor() {
this.obj = { a: 1, b: 2 };
}
create() {
return this.obj.a;
}
remove() {
return this.obj.b;
}
}
I set things up and run:
const myobj = new MyClass();
const handler = mapHandler("create", myobj);
console.log("Found", handler);
const res = handler();
But I get this:
Found [Function: create]
/workspaces/sandbox/index.js:22
return this.obj.a;
^
TypeError: Cannot read properties of undefined (reading 'obj')
at create (/workspaces/sandbox/index.js:22:17)
at Object.<anonymous> (/workspaces/sandbox/index.js:35:13)
at Module._compile (node:internal/modules/cjs/loader:1241:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
at Module.load (node:internal/modules/cjs/loader:1091:32)
at Module._load (node:internal/modules/cjs/loader:938:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
at node:internal/main/run_main_module:23:47
I am clearly losing the reference to this
inside myobj
and most likely due to Reflect.get
. How can I fix this and make sure the handler mapper does not cause the returned function to lose its reference to this
?
When you reference a function it's not bound to any this
.
You either should use Function::call()
to make a bound call, or Function::bind()
before the call to invoke to function on a proper this
.
Note when using Function::bind()
you cannot rebind it later (Function::call()
with a different this
doesn't work either), since Function::bind()
makes something like:
Function.prototype.bind = function(ctx, ...args){
const self = this;
return function(...extraArgs){
return self.call(ctx, ...args, ...extraArgs);
}
}
class MyClass {
obj;
constructor() {
this.obj = { a: 1, b: 2 };
}
create() {
return this.obj.a;
}
remove() {
return this.obj.b;
}
}
const mapHandler = (str, obj) => {
if (!Reflect.has(obj, str)) {
return null;
}
const candidateHandler = Reflect.get(obj, str, obj);
if (typeof candidateHandler !== "function") {
return null;
}
// here we bind the func to allow calling without a context
return candidateHandler.bind(obj);
};
const myobj = new MyClass();
const handler = mapHandler("create", myobj);
console.log("Found", handler);
const res = handler();