0

I want to pass a varying number of argument to varying functions. I set up this basic test:

function overview(arg1, arg2, arg3) {
    console.info('arg1 is ' + arg1);
    console.info('arg2 is ' + arg2);
    console.info('arg3 is ' + arg3);
}

function modules(method, args) {
    this[method].apply(null, args);
}

modules('overview', new Array('test1', 'test2'));​

So, I use 'apply' to pass an array with arguments via the 'modules' function to the 'overview' function. This works fine, except for the this[method] part. I read about this in this Q&A: Calling dynamic function with dynamic parameters in Javascript and it seems marvelous. However, I keep getting 'TypeError' errors and I can't figure out how to resolve this.

Of course, I could use a switch within the modules function to call the correct method, but that is unnecessary bulk (hopefully!). I've made a JSFiddle to 'fiddle' with: http://jsfiddle.net/QFpRc/. Hope anyone can solve and/or explain this.

3 Answers 3

1

this does not make sense in your context.

You have to define the functions in the global scope, or on another namespace, and use window, or the name of that namespace instead of this.

// Globally:    
function modules(method, args) {
    window[method].apply(null, args);
}
modules('overview', ['test1', 'test2']);​

// Locally:
var namespace = {};
namespace.overview = overview; // Defined previously
function modules(method, args) {
    namespace[method].apply(null, args);
}
modules('overview', ['test1', 'test2']);​

If you want to use this method locally, without predefining a namespace, there is no other option than using eval:

// Locally
function modules(method, args) {
    eval(method).apply(null, args);
}
modules('overview', ['test1', 'test2']);​

Expanding the implementation:

If you want to handle unknown functions without throwing errors, use:

function modules(method, args) {
    var ns = window; // or namespace (method 2)    or eval(method)  (method 3)
    if (ns.hasOwnProperty(method) && typeof ns.method === 'function') {
        ns.apply(null, args);
    } else {
        console.log(' Unknown method: ' + method);
    }
}

Demos

  1. Global: http://jsfiddle.net/QFpRc/4/
  2. Local + namespace: http://jsfiddle.net/QFpRc/3/
  3. Local + eval: http://jsfiddle.net/QFpRc/2/
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your quick response. When I use window instead of this, the result is the same. Also usage of this was suggested in the SO question I mentioned, and the rest of the code looks roughly the same. So why can 'they' use it and I not? The namespace and eval solutions both work great, though eval looks way simpler. But can't eval do evil here?
@kasimir Have a look at the demos at the bottom of my answer. Your code does not work, because you have selected the "onload" option in the checkbox. This wraps your code in window.onload = function() { ..your code.. }, causing your code to be local instead of global.
So it does. Wasn't aware of that, the whole namespace thing still seems tricky to me now and then. Thanks for elaborating.
Hm... I've implemented this like in demo 1, but I've discovered that this doesn't work in IE 8... Any ideas on a solution?
@kasimir I have successfully tested demo 1 (see jsfiddle.net/QFpRc/5) in IE8. Are you sure that you've implemented it correctly? (screenshot of demo 1 in IE8).
|
1

Your reference to this in the modules function declaration points to the modules objects. Since modules doesn't have an 'overview' property, there is no 'overview' method to invoke. As @Rob W stated, you could explicitly reference the global window object, where your functions are defined, or you could define your own object:

var myObj = {
    overview: function(arg1, arg2, arg3) {
        console.info('arg1 is ' + arg1);
        console.info('arg2 is ' + arg2);
        console.info('arg3 is ' + arg3);
    },

    modules: function(method, args) {
        this[method].apply(null, args);
    }
};

myObj.modules('overview', new Array('test1', 'test2'));

See http://jsfiddle.net/QFpRc/1/ for a live example.

1 Comment

Thanks, this gave me some interesting ideas for refactoring my code someday. But for now, Rob W's solution is more straightforward.
0

Your code will perform faster if you used Function.prototype.bind instead of passing parameters thru a proxy function. Bind will save processing time and memory by applying the context and routing arguments before the actual function call

function overview(arg1,arg2,arg3){
  console.info('arg1 is ' + arg1);
  console.info('arg2 is ' + arg2);
  console.info('arg3 is ' + (arg3 || this.foo));
}
var context = { foo:'bar' };
var contextOverview = overview.bind(context);
var modOverview = function(){}.apply.bind(overview.bind(context), context);

modOerview(['test1','test1'])

A closure'd variant might look something like:

var importModule = (function(){
  var mods = {
    overview:function(arg1,arg2,arg3){
      console.info('arg1 is ' + arg1);
      console.info('arg2 is ' + arg2);
      console.info('arg3 is ' + (arg3 || this.foo()));
      console.warn('This context is ' + this);
    }
  };
  return function exportMod(name, context){
    var mod = mods[name].bind(context);
    return mod.apply.bind(mod,null);
  }
})();

var myContext = (function(){
  var foo = 'bar';
  var overview;
  var context = {
    overview:function(){ return overview() },
    foo:function(){ return foo },
    toString:function(){ return 'my context!' }
  };
  overview = importModule('overview', context);
  return context;
})();

myContext.overview(['test1','test2']);

That being said, why don't you just call the function like normal? I mean, if you know the context and the function name.... .. .

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.