1

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!

Community
  • 1
  • 1
WilliamAmateur
  • 181
  • 2
  • 9
  • 1
    While possible, I would generally say no with respect to the context of `this`, and I'm not sure exactly why you would want to do this? – Xotic750 Sep 14 '16 at 19:12
  • You should take a close look at primitives are always passed by value and the difference between functions and methods. A function becomes a method when it depends on `this`, which none of your functions do. –  Sep 14 '16 at 19:29
  • @Xotic750 So the functions I'm wrapping are a bunch of audio filters. They get executed in sequence. But I'd like to randomise their order and settings, so I wanted to wrap them in something generic. Some of their arguments need to be dynamic though. I'm hoping to wrap each one in an object so that object would be the context I think – WilliamAmateur Sep 14 '16 at 19:38
  • Using your own object as the context should be fine. – Xotic750 Sep 14 '16 at 19:50

2 Answers2

2

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.

Bernardo Amorim
  • 343
  • 1
  • 6
  • Ah thanks, no that makes sense. I realise I should have mentioned that ideally I'd keep sayStuff the same (have just updated the question) as it's a function that already exists, and if possible I'd like to keep it's arguments intact. If that's not possible then using an object like this will definitely work - thanks! – WilliamAmateur Sep 14 '16 at 19:23
1

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();
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
  • @Xotic750 Sorry for my english, could you simplify your idea? I'm sure you are saying something usefull. So, I'm interested to learn. Thank you so much. – gaetanoM Sep 14 '16 at 18:55
  • The `this` can be `undefined` try wrapping the code in [`(function () {...}());`](https://jsfiddle.net/r159866L/) Extra work would be required, so this is not really a general solution to the OP question, but does work for their very specific example. – Xotic750 Sep 14 '16 at 18:59
  • Note that I also set the Javascript option to `no wrap - in ` in jsfiddle. If you just paste your code into a fresh [jsfiddle](https://jsfiddle.net/Lh6jmsko/) you will also find it doesn't work with the default `onload`. – Xotic750 Sep 14 '16 at 19:15
  • Thank you so much for the explanation. I'm glad for your sharing – gaetanoM Sep 14 '16 at 19:21
  • 1
    No problem, it's not a great explanation in just a couple of comments. The subject is much broader than that. – Xotic750 Sep 14 '16 at 19:23