JS method calling from object value - javascript

I'm working on an emulator. The task at hand is an incoming request on a certain endpoint. The request may contain 1-4 options in the req.body.options. The basic design idea is that an object contains the options and the corresponding method calls (as some sort of a sub-router).
let dataActions = {
option1: optionMethod(param1, param2),
option2: optionMethod2(param1, param2),
option3: optionMethod3(params),
option4: optionMethod4(params)
}
for (key in req.body.options) {
...
}
The for...in should fire the methods (decoupled in other files) when it finds matching in the request with the dataActions keys. Is there a semantical way, or a detailed design pattern to make this work?

The problem is that you already fire the methods yourself.
let dataActions = {
option1: optionMethod(param1, param2) // <-- this is a function call
}
Doing it this way you assign the result of optionMethod() to option1. The above is effectively shorthand for
let dataActions = {};
dataActions.option1 = optionMethod(param1, param2);
If that helps making it more obvious.
You don't want to call the methods immediately. You want to store them for later use. Either store them directly:
let dataActions = {
option1: optionMethod // <-- this is a function reference
}
...or store a function that calls them in some specific way:
let dataActions = {
option1: function () {
return optionMethod('some', 'parameters');
}
}
now you can use them at a separate time, for example like this
Object.keys(dataActions).filter(a => a in req.body.options).forEach(a => {
var optionMethod = dataActions[a];
optionMethod();
});

Related

Why does this getter method has been called several times in React?

I have a Store which will be provided to the component. In this Store file, there are several getter function. But I find only this getter function will be executed three times since this.rawMonthlyImpacts will be only changed once when the api get response from backend. I am so confused because other getter function in this file will be only executed once. During every execution, this.rawMonthlyImpacts is always same. Because this function is time-consuming, so I want to figure out why this happens. Hope you can give me some advice. Thanks!
get Impacts(){
const monthlyImpacts = new Map<string, Map<string, number>>();
if (this.rawMonthlyImpacts) {
this.rawMonthlyImpacts.forEach((impact) => {
if (impact.Impact > 0) {
const month = TimeConversion.fromTimestampToMonthString(impact.Month);
const tenantId = impact.TenantId;
const tenantImpact = impact.Impact;
if (!monthlyImpacts.has(month)) {
const tenantList = new Map<string, number>();
monthlyImpacts.set(month, tenantList.set(tenantId, tenantImpact));
} else {
const tenantWithImpactMap = monthlyImpacts.get(month);
if (!tenantWithImpactMap.has(tenantId)) {
tenantWithImpactMap.set(tenantId, tenantImpact);
} else {
tenantWithImpactMap.set(tenantId, tenantWithImpactMap.get(tenantId) + tenantImpact);
}
monthlyImpacts.set(month, tenantWithImpactMap);
}
}
});
}
return monthlyImpacts;
},
Update: I have find that there are other two functions use this.Impacts. If I remove these two functions, the getter function will only be executed only once. I think the getter function uses the cache to store data, so once the data is calculated for the first time, subsequent calls to the getter function should not be re-executed, only the value in the cache needs to be retrieved. So I am very confused about why this getter function will be executed 3 times.
getImpactedTenants(month: string): string[] {
return Array.from(this.Impacts.get(month).keys());
},
get overallMonthlyImpactedTenants(): Map<string, number> {
return new Map<string, number>(
Array.from(this.Impacts)?.map((monthEntries) => {
const month = monthEntries[0];
const impactedTenants = monthEntries[1].size;
return [month, impactedTenants];
})
);
}
Hard to tell what exactly is happening without more context, but remember that with a get function, every single time you reference that property (.Impacts in this case) the get function will be called.
Assuming that each impact stored in this.rawMonthlyImpacts which you loop through is an instance of the class with this getter, then as far as I'm aware, you are calling the get function each time you reference impact.Impacts, such as in the conditional:
if (impact.Impact > 0) {
I might be way off though; I'm unfamiliar with React and so my answer is based only on my experience with vanilla JS.

Assign and Query Javascript Arrow Function for Metadata

The problem is rather simple. We need to imbue a function with a parameter, and then simply extract that parameter from the body of the function. I'll present the outline in typescript...
abstract class Puzzle {
abstract assign(param, fn): any;
abstract getAssignedValue(): any;
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
Let's set the scene:
Assume strict mode, no arguments or callee. Should work reasonably well on the recent-ish version of v8.
The function passed to assign() must be an anonymous arrow function that doesn't take any parameters.
... and it's alsoasync. The assigned value could just be stored somewhere for the duration of the invocation, but because the function is async and can have awaits, you can't rely on the value keeping through multiple interleaved invocations.
this.getAssignedValue() takes no parameters, returning whatever we assigned with the assign() method.
Would be great to find a more elegant solution that those I've presented below.
Edit
Okay, we seem to have found a good solid solution inspired by zone.js. The same type of problem is solved there, and the solution is to override the meaning of some system-level primitives, such as SetTimeout and Promise. The only headache above was the async statement, which meant that the body of the function could be effectively reordered. Asyncs are ultimately triggered by promises, so you'll have to override your Promise with something that is context aware. It's quite involved, and because my use case is outside of browser or even node, I won't bore you with details. For most people hitting this kind of problem - just use zone.js.
Hacky Solution 2
class HackySolution2 extends Puzzle {
assign(param: any, fn: AnyFunction): AnyFunction {
const sub = Object(this);
sub["getAssignedValue"] = () => param;
return function () { return eval(fn.toString()); }.call(sub);
}
getAssignedValue() {
return undefined;
}
}
In this solution, I'm making an object that overrides the getAssignedValue() method, and re-evaluates the source code of the passed function, effectively changing the meaning of this. Still not quite production grade...
Edit.
Oops, this breaks closures.
I don't know typescript so possibly this isn't useful, but what about something like:
const build_assign_hooks = () => {
let assignment;
const get_value = () => assignment;
const assign = (param, fn) => {
assignment = param;
return fn;
}
return [assign, get_value];
};
class Puzzle {
constructor() {
const [assign, getAssignedValue] = build_assign_hooks();
this.assign = assign;
this.getAssignedValue = getAssignedValue;
}
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
const puzzle = new Puzzle();
puzzle.test();
Hacky Solution 1
We actually have a working implementation. It's such a painful hack, but proves that this should be possible. Somehow. Maybe there's even a super simple solution that I'm missing just because I've been staring at this for too long.
class HackySolution extends Puzzle {
private readonly repo = {};
assign(param: any, fn) {
// code is a random field for repo. It must also be a valid JS fn name.
const code = 'd' + Math.floor(Math.random() * 1000001);
// Store the parameter with this code.
this.repo[code] = param;
// Create a function that has code as part of the name.
const name = `FN_TOKEN_${code}_END_TOKEN`;
const wrapper = new Function(`return function ${name}(){ return this(); }`)();
// Proceed with normal invocation, sending fn as the this argument.
return () => wrapper.call(fn);
}
getAssignedValue() {
// Comb through the stack trace for our FN_TOKEN / END_TOKEN pair, and extract the code.
const regex = /FN_TOKEN_(.*)_END_TOKEN/gm;
const code = regexGetFirstGroup(regex, new Error().stack);
return this.repo[code];
}
}
So the idea in our solution is to examine the stack trace of the new Error().stack, and wrap something we can extract as a token, which in turn we'll put into a repo. Hacky? Very hacky.
Notes
Testing shows that this is actually quite workable, but requires a more modern execution environment than we have - i.e. ES2017+.

Why immer.js doesn't allow setting dynamic properties on draft?

//I want my action to dispatch payload like
// {type:'update',payload:{'current.contact.mobile':'XXXXXXXXX'}}
//In reducer dynamically select the segment of state update needs to be applied to
//Below code doesn't work as expected though, draft always remains at same level
draft = dA.key.split('.').reduce((draft, k) => {
return draft[k]
}, draft);
//Or an ideal syntax may look like below line
draft['current.contact.mobile'] = dA.value;
//Code that works
draft['current']['contact']['mobile'] = dA.value;
I want my action to dispatch payload like
{type:'update',payload:{'current.contact.mobile':'XXXXXXXXX'}}
And in reducer dynamically select the segment of state that needs to be updated.
Is there something fundamentally wrong in doing this, I believe this could make life easier. Is there something that can done to achieve this ?
In your case, this code returns a primitive value like a string or number which is immutable.
draft = dA.key.split('.').reduce((draft, k) => {
return draft[k]
}, draft);
"Immer" is using Proxy to implement all this magic. The proxy could work only on objects for example Object, Array, Function etc.
so to fix your problem you can use code like this
import produce from "immer";
describe("Why immer.js doesn't allow setting dynamic properties on draft?", function() {
it("should allow set dynamic properties", function() {
const path = "foo.bar.zoo";
const state = { foo: { bar: { zoo: 1 } } };
const nextState = produce(state, draft => {
const vector = path.split(".");
const propName = vector.pop();
if (propName) {
draft = vector.reduce((it, prop) => it[prop], draft);
draft[propName] += 1;
}
});
expect(nextState.foo.bar.zoo).toEqual(state.foo.bar.zoo + 1);
});
});
In the code above, we get destination object and update the property of this object.
Some note about string and number.
Javascript has constructors for string and number which return objects not primitive values. But this is a very rare case when someone uses it explicitly.
Usually, we deal with it implicitly when writing something like this dA.key.split('.'). In this case, the interpreter would create a string object and call method "split" on it. Usually, this behavior is referred to as "Boxing"

Is it a good idea to overcharge a method?

I have 3 classes, all extend the previous one.
Entity -> Body -> Player
Each one has a die() method which do very different things.
Entity.die() will call the db
Body.die() will animate the body
Player.die() will call the UI and play special sound.
I don't want to manually call Entity.die() inside Body.die method, mainly because I have many classes and many common methods and I don't want to forget something.
I wrote this little piece of code which does exactly this, the Error stack is easy to understand and points to the correct lines.
function overLoadMethods (parent, children) {
const methods = {}
for (let [fname, fn] of Object.entries(parent)) {
if (typeof fn === 'function') {
if (children[fname]) {
methods[fname] = function () {
fn()
children[fname]()
}
Object.defineProperty(methods[fname], 'name', { value: fname })
} else {
methods[fname] = fn
}
}
}
return methods
}
function createEntity () {
return {
die: () => {
console.log(new Error().stack)
console.log('entity die')
}
}
}
const bodyMethods = {
die: () => {
console.log(new Error().stack)
console.log('body die')
}
}
function createBody () {
const entity = createEntity()
const overLoadedMethods = overLoadMethods(entity, bodyMethods)
return {
...entity,
...bodyMethods,
...overLoadedMethods
}
}
const playerMethods = {
die: () => {
console.log(new Error().stack)
console.log('player die')
}
}
function createPlayer () {
const body = createBody()
const overLoadedMethods = overLoadMethods(body, playerMethods)
return {
...body,
...playerMethods,
...overLoadedMethods
}
}
const player = createPlayer()
// will call Entity.die() then Body.die() then Player.die()
player.die()
Everything is working fine but I never saw this pattern before and I guess there is a good reason which I'm unaware of.
Could someone point the weakness of this pattern if there is one (pretty sure there is) ?
Common Lisp has something similar. When you define a method in a derived class you can decide whether this method should be executed:
:before (i.e. the base method will be called automatically after specialized one)
:after (i.e. the base method will be called automatically before the specialized one)
:around (i.e. only the specialized method will be called, but inside its body you can call the base method with call-next-method that is a special syntax that allows calling base method with either the parameters specified by the caller or the parameters that you want to pass instead).
For example C++ only has around available for general methods (but without the ability to call the base version with original parameters) and forces instead use of before in constructor and after in destructors.
I understand the desire to not repeat code and create code that makes it hard to make mistakes and forget things. But you still have code the you need to remember to wire up. For example, instead of calling Entity.die() you need to call overLoadMethods(). I'm not sure that's an improvement over regular of classes and calling super.die().
You can get the chained method behavior using ES6 classes (you can also get it using prototypes). This has a lot of advantages:
• The pattern is baked into the language.
• It's very clear to see parent/child relationship
• There's a lot of commentary, theory, and examples of different patterns
class Entity {
die() {
// Entity-specific behavior
console.log('entity die')
}
}
class Body extends Entity {
die() {
super.die()
// Body-specific behavior
console.log('body die')
}
}
class Player extends Body {
die() {
super.die()
// Player-specific behavior
console.log('player die')
}
}
const player = new Player
// will call Entity.die() then Body.die() then Player.die()
player.die()

Passing metadata between functions

I created an API with Node.js, and I don't want the API to change nor do I want to add extra parameters to a function. However, the internal code in the library needs to now send some metadata between an internal API method and an external facing method.
Is there a way to pass (meta) data between functions somehow in JS that does not involve parameters/arguments?
TL;DR, it would be really useful to pass metadata between functions for the purposes of JS APIs, that should not change signatures.
(One trick is if the function is created everytime it is called, you can assign data onto the function object itself, but that is not true in this case (function is not being created everytime it is called).)
The trick I am currently using - and it is not a good one - there is an options {} object being used in the API. I am passing a hidden property in that objects object "__preParsed". The user will use that objects object as they normally would, behind the scenes I use it for some bookkeeping stuff that they don't need to know about.
Ok here is the code:
//public API
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
return beforeEach.apply(ctx, _args);
};
beforeEach = function (desc, opts, aBeforeEach) {
handleSetupComplete(zuite);
const _args = pragmatik.parse(arguments, rules.hookSignature);
const obj = { //have to do this until destructuring works
desc: _args[ 0 ],
opts: _args[ 1 ],
fn: _args[ 2 ]
};
handleBadOptionsForEachHook(obj.opts, zuite);
return 'there is more code but I omitted it';
};
as you can see the first method calls the second, or the second can be called directly, both are public APIs.
We need to parse the arguments in both calls, but as an optimization, we shouldn't have to parse them a second time if the second method was called by the first instead of directly.
The solution I will use for the moment is:
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
_args[ 1 ].__preParsed = true;
return beforeEach.apply(ctx, _args);
};
the opts options object is public, but the user won't know about the __preParsed property. The internal API will.
The problem with this is that the user can call the public API directly without an options object, and since the signature is very much varargs, then I really don't know until I have parsed it with my parse engine, which arg if any is the objects object!
You could abuse the this object to carry non-argument metadata in as follows by invoking your function using Function.prototype.call:
function inner (arg1, arg2) {
console.log('inner called with', arg1, arg2)
console.log('inner metadata', this._meta_count)
}
inner.call({_meta_count: 17}, 'ARG ONE', 'ARG TWO')
inner.call({_meta_count: 18}, 'ARG ONE B', 'ARG TWO B')
You could just add a new undocumented parameter to the end. JavaScript won't care and previous calls will still work, why is that a problem for you?
If you are checking parameter count and throwing errors, you could expect the hidden parameter to be an object with a magic property, if it's not, throw the error.
function go(a, b, c, _internal) {
if (_internal && ! _internal.hasOwnProperty('_magic')) {
throw new Error('invalid internal parameter passed');
}
}
You can get a little more paranoid and store the magic property as a Symbol, then the caller couldn't pass it by accident, they would have to be acting nefariously.
function go(a, b, c, _internal) {
if (_internal && !_internal.hasOwnProperty(go.internalParamProp)) {
throw new Error('invalid internal parameter passed');
}
console.log("Internal param", _internal && _internal[go.internalParamProp])
}
// Symbol for the magic property name to avoid accidental passing of internal param
go.internalParamProp = Symbol('');
// Passing the internal param
// Uses JS syntax that is not yet supported in some browsers
// If it's a concern, use or var obj={}; obj[go.internalParamProp] = 45
go(1, 2, 3, {
[go.internalParamProp]: 45
})
// Regular call
go(1, 2, 3)
// Invalid call
go(1, 2, 3, 4)

Categories

Resources