WilliamAmateur
WilliamAmateur

Reputation: 201

How do I wrap a javascript function with dynamic arguments?

I'd like to wrap some dynamically created javascript functions, similar to Daniel's accepted answer here: How do I store javascript functions in a queue for them to be executed eventually

// Function wrapping code.
// fn - reference to function.
// context - what you want "this" to be.
// params - array of parameters to pass to function.
var wrapFunction = function(fn, context, params) {
    return function() {
        fn.apply(context, params);
    };
}

The difference is I'd like the argument values to be dynamic at time of execution - is it possible to pass a reference to a variable in the arguments, which could be updated after it is wrapped?

Here's what I'd like to do:

// I have a function to be wrapped
var sayStuff = function(a,b) {
    console.log(a);
    console.log(b);
}

// Variables I'd like to pass
var randomNumberA = 0;
var randomNumberB = 0;

// Wrap the function
var fun = wrapFunction(sayStuff, this, [*reference randomNumberA*,*reference randomNumberB*]);

// variables get changed
randomNumberA = Math.random()*100;
randomNumberB = Math.random()*100;

// Execute the function using current values of randomNumberA & randomNumberB
fun();

If possible I'd like to do this without changing sayStuff, I have a lot of existing functions like this I'm hoping to wrap, which also get used outside of the wrapping, so ideally I'd like to not replace the arguments with an object. Hope that makes sense, Thanks!

Upvotes: 1

Views: 4891

Answers (2)

Bernardo Amorim
Bernardo Amorim

Reputation: 343

If the function and the variable will be created in the same scope you can just use that:

var randomNumber = 0;
var fun = function(){ alert(randomNumber); }

randomNumber = 10;

// Now this will alert 10, because when fun is executed
// JS looks in his scope to find what randomNumber is.
fun();

This happens because functions in javascript works as Closures, they carry their environment with them. See: https://en.wikipedia.org/wiki/Closure_(computer_programming)

So if randomNumber will be changed out of the scope where you bind that function, you need to use an object, this is because in javascript we don't have "pointers" or references to pass by. One way is using a object.

function giveMeAFunction(){
  var params = { randomNumber: 0 }
  var fun = function(){ alert(scope.randomNumber); }
  return {fun: fun, scope: scope};
}

var paramsAndFun = giveMeAFunction()
// Now you can change the variables in the scope and call the function
paramsAndFun.params.randomNumber = 10;
paramsAndFun.fun(); // Will alert 10

// Now if you replace the entire params object it will not work
// This is because you will replacing it with a new object while
// The one that is referenced in the scope where fun was created is
// the old one.
paramsAndFun.params = { randomNumber: 15 };
paramsAndFun.fun(); // will still alert 10

Now let's get to binding part of the problem.

There is already Function.prototype.bind function to help you with that.

For example:

var sayStuff = function(opts) {
    alert(otions.randomNumber);
}

var options = { randomNumber: 0 };
var fun = sayStuff.bind(this, options);

options.randomNumber = 10;

fun(); // Will print 10

There is a lot going on here. Sorry if I made everything confuse.

Upvotes: 2

gaetanoM
gaetanoM

Reputation: 42044

If the dynamic arguments are defined in the context argument, a solution can be based passing the name of the variables and then, at execution time, calculate its current value:

var wrapFunction = function(fn, context) {
  var xArg = arguments;
  return function() {
    var argsArray = [];
    for (var i = 2; i < xArg.length; i++) {
      argsArray.push(context[xArg[i]]);
    }
    fn.apply(context, argsArray);
  };
}

var sayStuff = function() {
  for (var i = 0; i < arguments.length; i++) {
    console.log('sayStuff func: ' + arguments[i]);
  }
}

var randomNumber1 = 0;
var randomNumber2 = 0;

var fun = wrapFunction(sayStuff, this, 'randomNumber1', 'randomNumber2');

randomNumber1 = Math.random()*100;
randomNumber2 = Math.random()*100;

console.log('randomNumber1: ' + randomNumber1);
console.log('randomNumber2: ' + randomNumber2);

fun();

Upvotes: 1

Related Questions