I am looking for the best method of calling functions with variable names that all follow a specific rule. I am trying to avoid using eval() since there are so many problems about it online.
I have a system setup where different groups have a function that can be called based on their name EG Test group would have a function imported in called getValue_Test(params) and this function can be called for any group name EG getValue_Test2(params) would work if the group name was test2.
I have these functions being imported into the react component and just want to know the best way to call them without making giant switch statement since there will be hundreds of these functions.
Any help would be appreciated :) Thanks
If you've got some functions defined like this:
function foo() {}
function bar() {}
function baz() {}
You could put them in an object (or map):
const funcs = { foo, bar, baz };
Then you will be able to use a string to access and eventually call the function:
funcs["foo"](); // called foo
const key = "bar";
funcs[key](); // called bar
funcs.baz(); // called baz
However this is usually not recommended. It looks like you might want to use an array instead? Your function names differ only by a number that appears to be incrementing.
Related
I had a random, rather rudimentary question pop into my head as I was writing some JS about scopes and closures. (Please bear with me with the rather convoluted example.)
Let's say I have two functions, one that's more or less a decorator called "makeMagicFn". This thing takes in an object, looks up the randomFn method on it, and does something cool to it, attaching it as a property to a new object as someSpecialFn, returning the object.
The second function is where that magic happens. It has a few local variables inside of it, and then defines the "random" function for use in the makeMagicFn described above.
Here's the fun part. The randomFn, defined here as innerFn, looks up the variables defined on outerFn's scope (foo and baz), and does some stuff with them. At the end, the decorated innerFn is returned from outerFn to be used by its caller.
function makeMagicFn({ randomFn }) {
const someSpecialFn = makeSomethingAwesomeWithRandomFn(fn);
return {someSpecialFn};
}
function outerFn() {
const foo = "bar";
const baz = "qux";
const insideFn = () => {
console.log(foo, baz);
}
const {someSpecialFn} = makeMagicFn({ randomFn: insideFn });
return someSpecialFn;
}
Now for my question.
Does JS allow me to define innerFn outside of outerFn, in such a way that allows access to outerFn's scope, so it can look up the variables without throwing a ___ is not defined error? (E.g. if I wanted to import it from a separate file instead.) Like using .bind, except setting the context not on outerFn's this, but its place in memory instead.
No, this is not possible. Local variables are local, and scope is lexical. To create a closure over them, you must define the function where they are in scope.
You can do lots of trickery if you don't make foo and baz local variables but properties of an object, which you then can pass around (in the easiest case, as an argument to makeMagicFn).
Normally, in Javascript, when I want to pass an anonymous/inline function as an argument to another function, I do one of the following.
someFunctionCall(function() {
//...
});
someFunctionCall( () => {
//...
});
However, I've recently inherited a codebase that uses named function as inline arguments, like this
someFunctionCall(function foo() {
//...
});
I've never seen this syntax before. The function still seems to be anonymous -- there's no foo function defined in either the calling or called scope. Is this just a matter of style, or can using a named function (foo above) as an anonymous function change the behavior or state of that program?
This is specifically for a NodeJS (not a browser based program) program, and I'm specifically interested in behavior specific to using functions as parameters. That said information from behavior across platforms and runtimes is welcome.
There are at least three advantages of using named function expressions instead of anonymous function expressions.
Makes debugging easier as the function name shows up in call hierarchy.
The function name is accessible in the inner scope of the function, so it can be used for recursion
The function name itself acts like a self documentation of what the function is doing instead of reading the code.
Using those "named anonymous functions" won't change the behavior but will show the function name in stack traces which is very useful. Also the function gets callable within itself that way.
I'll give an example
Case 1:
var obj = {count: 0, counter: ()=> {this.count+=1;}}
If you do console.log(obj.count) you'll get 0
Case 2:
var obj = {count: 0, counter (){this.count+=1;}}
In 2nd case if you do console.log(obj.count) value will be one.
Hope you understood by now. Lamda expressions cannot access values with reference of this object. It can only access variables with global reference.
In case 1 if you want to make it work with lamba you have to use obj.count+=1 with name has reference.
And rest of the JavaScript function implementation remains same there is not much difference.
I want to be able to take a string and use that to retrieve the value of a constant that has that string's name. In particular, the constant will equal an arrow function.
const foo = (bar) => { return bar }
I expected that it would show up in the window object, but it doesn't:
let fn = window['foo'] // undefined
Right now, the only way I can figure out how to do this is to use the older function syntax:
function foo(bar) { return bar }
let fn = window['foo'] // function foo(bar) { return bar }
Is there a way I can get the function from the string of its name, when the function is stored within a constant?
Edit:
I feel the need to clarify why I would want to start defining functions like this, as most responses have seemed to find the distinction important. It essentially comes down to scope and mutability.
The way arrow functions handle the this keyword is a little easier to grok with arrow functions and I believe should be the standard behavior unless the old behavior is explicitly required.
Second, the way mutability works with JavaScript, I think that defining functions as constants is a bit safer. Example:
function foo(bar) { return bar }
// Elsewhere
let foo = 4;
// Back at the ranch
foo("return this string please") // foo is not a function.
Ultimately, I think this just reduces mutability, and also causes fewer surprises when using this.
Only global variables get assigned a matching property on the global object. Nothing else, including constants, does.
The value of a constant won't automatically appear anywhere except in the constant itself.
If you want to access it via a string, then store it as a property of an object (preferably not the global object though, that's a disorganised mess with a significant risk of clashing with other code).
I'm trying to figure out (not successfully yet) if it's possible to check if js function I'm trying to mock with spyOn (jasmine) was called with the exact parameters I expected, for example consider function:
foo = function(date, array) {};
So far I can only make sure that the array was indeed passed like this:
spyOn(bar, 'foo').and.returnValue(somePromise.promise);
somePromise.resolve;
//call some function that calls 'foo' eventually...
expect(bar.foo).toHaveBeenCalledWith('10/12/2100', []);
I need to check if the array passed to this function was empty, how can I achieve this? Also if it's possible, how can I check if array passed to this function contained the string I wanted?
Thanks!
Replace and.returnValue with and.callFake, then pass in a function that stores the arguments before returning the promise. Then the arguments can be used for later expect tests.
I don't know Jasmine, so this will probably not be the answer you're looking for.
On the other hand, it is a doable work around that you can implement in the mean time while you await your real answer.
If you have control over foo, then in the foo method you can tell it to set a global variable to its input parameter:
var fooParam;
var foo = function(array){
fooParam = array;
}
Then in your other code, you can do:
if (fooParam != null){
if (fooParam.contains(yourString)){
//I'm not sure if "contains" is actually a method or not,
//but that's really a separate issue.
}
}
So you set the global variable fooParam in the foo method and then you can use it later to do checking.
So there's a potential problem in that you might not have control over foo, such as if it's a system method. To work around that, replace all calls to foo with something like foo2, which you would define to have the same parameters and all it does is call foo and set the global variable. But if you have control of foo, then you don't need to do this.
The other potential problem is that foo will be called multiple times (such as if it's called within a loop). To circumvent this, you might make the global variable be an array list and have foo add its parameters to the array list instead of setting the variable. But if it's not called within a loop, or not called multiple times before it can be processed, then you don't need to do this.
I hope this helps while you're looking for the real answer
After looking around a bit, I couldn't find an answer to this question, though I suspect it's been asked before. The closest I could find is this Function pointers in objects though it doesn't get quite what I'm after.
Suppose we have a piece of code like this:
var foo = function () { /* some code */ };
now, I'm assuming that during evaluation time the name foo is bound in it's environment to an internal representation of the function, that is then looked up during application time.
Now suppose we have , a little later in the program, an object:
var obj = {};
obj.func = foo;
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func. But let's say I don't want two copies of that function, but rather have obj.func point back towards foo--is there a way to do this?
This is what I've come up with:
var obj = {};
obj.func = function () { foo(); };
Would this work? Is there a better way?
Many thanks
EDIT FOR CLARIFICATION
Apologies--I don't think I was being clear enough. I hope this helps.
I'm not sure how the Javascript evaluation process works, but let's say it keeps track of some internally represented environment, which we'll represent abstractly here as a set of key value pairs.
Now, when the evaluator sees some code such as
var foo = function () { /* some code */ };
it constructs this in it's environment: ['foo' : [function object]]. And then, later on, when it sees something like this:
var bar = foo;
it looks up foo in the environment to retrieve it's value, a function object, and then augments the environment like so. ['foo' : [function object], 'bar' : [function object]].
Now the same function really is there twice in the environment. Is this how javascript sets up its environment? If not, then how does it do so?
My motivation for asking is as follows. Let's say we define some functions, and then later an object that we export that functions as an interface to those functions, and which we know we're going to instantiate many times. It would be more efficient if the object contained pointers to functions, rather than the functions themselves.
JS variables, object properties, etc do not hold objects directly; they hold references to objects. When you say var foo = function() { };, foo is assigned a reference to that function you just defined. (Not a pointer, by the way. The difference can be subtle, but important. JS has no user-visible pointers, so it's not as much of an issue. But the languages that do, use them differently. Don't make yourself have to unlearn stuff later.)
And later in the program, when you say obj.func = foo;, you're copying the reference into obj.func. Not the function -- there's still only one function object, with two references to it.
So obj.func = foo; already does what you were saying. When you call obj.func(), it's actually calling the exact same function that's referenced by foo. There's one huge difference, though, and this may be what's causing you trouble: obj.func is a "method call", and JS sets this to obj inside the function. If foo was using this for anything, assuming it was some other object, it's going to be disappointed.
If that's a problem, you might do something like
var that = this;
var foo = function() { /* use `that` instead of `this` here */ };
Then, whether you call the function as foo or obj.func, it's not affected by the changed this.
If this doesn't help, you'll need to clarify the question.
Have you tried running your example?
Yes, that does work, however be aware that if you change the foo variable it will also change the functionality of objc.func().
I don't know any other way to do this.
This isn't true:
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func. But let's say I don't want two copies of that function, but rather have obj.func point back towards foo
If you assign things that way then obj.func and foo point to the same thing. I don't even think it's possible to copy a function.
Just to elaborate a bit:
functions are first class citizens or objects in javascript. They are used just like any other objects.
var foo = func() {
//some code
};
is the analogous to
var foo = {
//some code
};
In Javascript, all objects are passed by reference. As far as I know there is no concept of a pointer or pass by value for javascript objects.
So when you assign two references to the same function, the function object is not copied. However, the internal state of the function remains the same for both the references. In your case foo and obj.func()
At this point, there should be two copies of the same function object in the environment, one bound to foo, the other bound to obj.func.
You think so? Just out of curiosity, if the code said:
var obj = {};
obj.func = 5;
How many copies of 5 are floating around?
No. All code is translated to functions, which are essentially constants. You can assign several variables to the same constant without any trouble.
As for "binding", binding occurs when the function is called. That is, the this parameter is set to obj and whooosh...