10

I want to dynamically call a function from a string like "User.find". A script would call the function find() in the object User if the function exists. Here's what I tried:

 var User = {};
 User.find = function(){
     return 1;
 }

 var input = 'User.find';
 var some_data_array = {name: 'John Doe'};
 var method = input.toString().split('.');
 var nameObj = method[0].substring(0,1).toUpperCase() + method[0].substring(1);
 var methodToCall = method[1];

 nameObj.call(methodToCall, some_data_array);

But it always returns:

 nameObj.call(methodToCall, some_data_array);
 TypeError: Object User has no method 'call'

Any idea? I can't use window since it is a node.js problem, the script is not executed in the browser.

Madnx
  • 1,532
  • 4
  • 16
  • 23
  • `nameObj` is a *string*, not a function? – Bergi Oct 12 '14 at 18:06
  • when it comes to dynamic, try using `eval`. – King King Oct 12 '14 at 18:06
  • That's why I'm asking here. I don't know how to call a function from a dynamic object. – Madnx Oct 12 '14 at 18:06
  • 4
    @KingKing: No; don't use `eval`. – SLaks Oct 12 '14 at 18:07
  • 3
    Yes I want to avoid using eval. – Madnx Oct 12 '14 at 18:07
  • @SLaks it's usable in some cases and in fact without it, you cannot solve the problem (I don't mean in this case, but probably in some case). – King King Oct 12 '14 at 18:08
  • @Codel96 the method name is a string (input from user), but I'm not sure what exactly the object is here? (you want to convert `"User"` string to an object? weird without any context). – King King Oct 12 '14 at 18:11
  • The thing is User exists as an object, and so does its function "find". I just edited my first post. – Madnx Oct 12 '14 at 18:13
  • This question is tagged nodejs. Does it have to be node or is a browser-centric solution acceptable? – slebetman Oct 12 '14 at 18:13
  • It is a node issue because it isn't running in the browser. I want to call the function from the server-side. – Madnx Oct 12 '14 at 18:23
  • you can try defining some root object. Define the `User` property in that object, then you can access the `User` like this `root["User"]`, next you can access the `find` like this `root["User"]["find"](...)` – King King Oct 12 '14 at 18:24
  • Have a look [over here](http://stackoverflow.com/a/14397052/1048572) – Bergi Oct 12 '14 at 21:07

4 Answers4

14

You're completely misunderstanding call().

call() lets you call a method with a different this.

You want to get as property by name:

object[methodName](arg1, arg, ...);
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Thank's for answering but it doesn't work for me : http://jsfiddle.net/74kmm6bp/ or maybe I didn't get how to use it. – Madnx Oct 12 '14 at 18:17
  • This is not what the OP wants. At least the OP's problem is unclear now. He does not even have any ***right*** object here, as well as some reference to the method (just method name). – King King Oct 12 '14 at 18:17
  • @Codel96: `nameObj` is a string. To get the global variable by that name, use the same syntax: `window[nameObj]`. – SLaks Oct 12 '14 at 18:18
  • I can't use window since the script is not executed in the browser but with node.js. I just edited with a more clear explanation of the problem. – Madnx Oct 12 '14 at 18:19
  • @Codel96: Then put the variables that may be accessed in a separate object, and use that. That's better for security, anyway. – SLaks Oct 12 '14 at 18:23
  • Ok I didn't really think about that. That way I can even check if the function really exists easily. – Madnx Oct 12 '14 at 18:25
  • 1
    @KingKing: That's because JSFiddle wraps the code in a function, so those are locals. – SLaks Oct 12 '14 at 18:33
2

You actually can achieve this. You first would need to get the scope, where your namespace/function/object is defined.

For example, in your code I would assume its window.

So, a little modification of your code would produce the desired result:

var User = {};
User.find = function(x){
    alert(x);
}

 var input = 'User.find';
 var some_data_array = {name: 'John Doe'};
 var method = input.toString().split('.');
 var nameObj = global[method[0]];
 var methodToCall = method[1];

 nameObj[methodToCall](some_data_array.name);

Mark the use of global[]. This is where it starts.

[edited] * Modified code to use global instead of window as being used in nodejs.

sudipto
  • 2,472
  • 1
  • 17
  • 21
  • The thing is I can't use window because it works server-side with node. – Madnx Oct 12 '14 at 18:31
  • @Codel96: That is why I wrote - you need to get the `scope` and my example was using `window` scope. You probably would have a different scope (namely `global`). I will change the code. – sudipto Oct 12 '14 at 18:34
  • Ok thank's for the answer. I'm gonna try that. I think I'll still go with SLaks answer because it's easier to manipulate data but it helped me ;). – Madnx Oct 12 '14 at 18:39
  • The scope generally is where you define `User` and in majority of the cases it is given by `this`. – sudipto Oct 12 '14 at 18:39
  • Notice that providing the client a possibility to call *arbitrary* methods on `global` and its descendants is as bad as using `eval`. – Bergi Oct 12 '14 at 21:04
  • Correct. That is why I had mentioned to get the scope. The code is to illustrate using @Codel96 's existing code which seems to be using global scope. – sudipto Oct 12 '14 at 21:07
0

What you want to do is access the global object.

This should work:

var User = {};
User.find = function(){
    return 1;
}

var input = 'User.find';
var some_data_array = {name: 'John Doe'};
var method = input.toString().split('.');
var nameObj = method[0].substring(0,1).toUpperCase() +method[0].substring(1);
var methodToCall = method[1];

"use strict";
var global = (1,eval)("this");
alert(global[nameObj][methodToCall](some_data_array));
EvilZebra
  • 1,072
  • 8
  • 18
  • It won't work in jsfiddle, run in console. Edit: I'm assuming this is because of how fiddle runs the JS, and it's not declaring `User` object within the global scope. – EvilZebra Oct 12 '14 at 18:27
  • Here's what I get: `TypeError: Property 'connect' of object # is not a function` – Madnx Oct 12 '14 at 18:29
0

There are a couple of ways to get the desired result. In this case it is very simple to just use an anonymous function, as follows, passing in the string parameter contained in objName.name:

var f = function(oName){
  if (oName == 'John Doe') {
     return 1;
  }   
 };


 var objName = {name: 'John Doe'};
 
console.log( f( objName.name ) );

If the OP prefers to use an object's call method, the following code also will suffice:

var myObj = {
  myFunc: function() {
    if (this.name == 'John Doe') {
      return 1;
    }
  }
}

var objName = {
  name: 'John Doe'
};

console.log(myObj.myFunc.call(objName));

A couple of things to note. myObj calls its method myFunc() passing in the object parameter objName. The purpose of this in the method is to read the property name, possessing that ability because of being bound to the object objName.

slevy1
  • 3,797
  • 2
  • 27
  • 33