0

I'm trying to execute external JS functions in my control by passing a function name and using apply().

I'm using solution found on stack here

function executeFunctionByName(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
    }
    return context[func].apply(context, args);
}

Function usage:

var functionName = 'MyFunction';
executeFunctionByName(functionName, window, [e, data]);

Function MyFunction:

function MyFunction(e, data){
    // e <- this is an array [e, data]
    // data <- this is undefined
}

According to the documentation apply() should pass arguments as they are in function definition, not as an array.

Is there a way to make it work the way described in documentation?

Can it have something to do with the context set to window?

Community
  • 1
  • 1
Wojtek
  • 1,390
  • 1
  • 19
  • 38
  • 3
    `executeFunctionByName(functionName, window, e, data);` – Ja͢ck Dec 30 '14 at 08:31
  • 1
    The documentation for `apply` in that page does say that it has to be array `apply([thisObj[,argArray]])`, but when you call the function you should pass each argument, not an array. – elclanrs Dec 30 '14 at 08:32
  • The root cause, I guess, is you didn't get what [**`Array.prototype.slice`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) was doing. – Leo Dec 30 '14 at 08:45
  • I missed that nested array, thanks. – Wojtek Dec 30 '14 at 08:53

3 Answers3

2

after

var args = Array.prototype.slice.call(arguments, 2);

try

console.log(args); // [[e, data]], array in array... got it?

So there are 2 ways:

1)

executeFunctionByName(functionName, window, [e, data]);

to

executeFunctionByName(functionName, window, e, data);

2)

var args = Array.prototype.slice.call(arguments, 2);

to

var args = arguments[2]; //or name the third input parameter
Kirill Pisarev
  • 844
  • 4
  • 11
1

apply is just fine. The problem is that you have a doubly-nested array.

When you call var args = Array.prototype.slice.call(arguments, 2);, that itself returns an array, containing another array.

You're passing [[e, data]] to apply, not [e, data].

There are two solutions, depending on how you want to fix the problem:

  1. If you want to keep invoking your method this way:

    executeFunctionByName(functionName, window, [e, data]);
    

    Then you have absolutely no need for Array.prototype.slice on arguments. You have a predictable number of arguments, it's always 3. Just write your function this way:

    function executeFunctionByName(functionName, context, args) {
      var namespaces = functionName.split(".");
      // ....
      return context[func].apply(context, args);
    }
    
  2. If you want to keep using Array.prototype.slice in your function, you need to change how you invoke the function. Your arguments should not be wrapped in an array:

    executeFunctionByName(functionName, window, e, data);
    
user229044
  • 232,980
  • 40
  • 330
  • 338
0

In your example you should be able to call it with either the e or the data, but not both.

I would do it something like this:

function MyFunction(data){
   //Some action with your data
}

and call it with:

executeFunctionByName(functionName, window, [data]);

Where data is some array of data. I think the documentation on Mozilla's page explains it more clearly, it can be found at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

Alex J
  • 1,029
  • 6
  • 8