JavaScript functions can take all sorts of things as arguments: numbers, strings, arrays, objects…even other functions. A function passed to another function as an argument is a callback function. This is, however, distinct from invoking a function in an argument.
Let’s say we have a function, roll, which simulates rolling a sides-sided die throws-number of times and summing the result:
function roll(throws, sides) {
let result = 0;
for (let i = 0; i < throws; i++) {
let seed = Math.random();
while (seed === 0) {
seed = Math.random();
}
result = result + Math.ceil(seed * sides);
}
return result;
}
Now let’s say we have an object, RATHGAR . We want to assign RATHGAR a property with a key of str and a value determined by simulating rolling a six-sided die three times and summing the results. We might do so in the following way:
function strAssign(character, method) {
character.str = method();
}
strAssign(RATHGAR, () => roll(3, 6));
This approach uses a callback function since the anonymous function that is passed into strAssign as an argument calls roll when it is invoked by method().
Another way to get the same result would be to invoke roll in the argument itself:
function strAssign(character, value) {
character.str = value;
}
strAssign(RATHGAR, roll(3, 6));
In this case, when strAssign is invoked, roll is invoked as well and the return value of roll is passed into strAssign.
These two approaches give the same result in this case but that doesn’t mean they’re identical. To see the difference between the two, let’s say we have an array, ABILITIES, and we want to use that array to populate RATHGAR with keys. For each item we pull from the array as a key, we want to use our function roll to determine the key’s value. We might try to do so as follows:
const ABILITIES = ['str', 'int', 'wis', 'dex', 'con', 'chr'];
function statAssign(character, stats, method) {
for (const STAT of stats) {
character[STAT] = method;
}
}
statAssign(RATHGAR, ABILITIES, roll(3, 6));
Here we’ve invoked roll in the argument, but if you check the properties of RATHGAR you’ll find that each key, e.g., str and dex, has been assigned the same value. With this approach, roll is only invoked once and its return value is then passed into statAssign where it is used multiple times.
If, on the other hand, we employed roll as a callback function, this problem is solved:
function statAssign(character, stats, method) {
for (const STAT of stats) {
character[STAT] = method();
}
}
statAssign(RATHGAR, ABILITIES, () => roll(3, 6));
By using roll as a callback function, it is passed into statAssign and invoked multiple times by method(). Consequently, a different random value is assigned to each key in RATHGAR.