9

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.

Cxodael
  • 1,472
  • 4
  • 16
  • 22

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
  • 837,282
  • 173
  • 1,862
  • 1,933
  • 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. – Cxodael 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. – Cxodael 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. – Cxodael 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,462
  • 1
  • 16
  • 21
  • The thing is I can't use window because it works server-side with node. – Cxodael 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 ;). – Cxodael 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,022
  • 8
  • 16
  • 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` – Cxodael 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,689
  • 2
  • 24
  • 32