In some purely functional programming languages, a function is not described as being called or invoked, but rather it's applied. In JavaScript, we have the same thing we can apply a function using the method Function.prototype.apply()
, because functions in JavaScript are actually objects and they have methods.
Here's an example of a function application:
// define a function var sayHi = function (who) { return "Hello" + (who ? ", " + who : "") + "!"; }; // invoke a function sayHi(); // "Hello" sayHi('world'); // "Hello, world!" // apply a function sayHi.apply(null, ["hello"]); // "Hello, hello!"
As you can see in the example, both invoking a function and applying it have the same result. apply()
takes two parameters: The first one is an object to bind to this
inside of the function; the second is an array of arguments, which then becomes the array-like arguments
object available inside the function. If the first parameter is null
, then this
points to the global object, which is exactly what happens when you call a function that is not a method of a specific object.
When a function is a method of an object, there's no null
reference passed around (as in the previous example). Here the object becomes the first argument to apply()
:
var alien = { sayHi: function (who) { return "Hello" + (who ? ", " + who : "") + "!"; } }; alien.sayHi('world'); // "Hello, world!" sayHi.apply(alien, ["humans"]); // "Hello, humans!"
In the preceding snippet, this inside of sayHi()
points to alien
. In the previous example, this
points to the global object.
As the two examples demonstrate, it turns out that what we think of calling a function is not much more than syntactic sugar, equivalent to a function application.
Note that in addition to apply()
, there's a call()
method of the Function.prototype
object, but it's still just syntax sugar on top of apply()
. Sometimes it's better to use the sugar: When you have a function that takes only one parameter, you can save the work of creating arrays with just one element:
// the second is more efficient, saves an array sayHi.apply(alien, ["humans"]); // "Hello, humans!" sayHi.call(alien, "humans"); // "Hello, humans!"
Partial Application
Now that we know that calling a function is actually applying a set of arguments to a function, is it possible to pass just a few of the arguments, not all of them? This is actually similar to how you would normally do it, if you were dealing with a math function manually.
Say you have a function add()
that adds two numbers together: x
and y
. The following snippet shows how you can approach a solution given that x
is 5 and y
is 4:
// for illustration purposes // not valid JavaScript // we have this function function add(x, y) { return x + y; } // and we know the arguments add(5, 4); // step 1 -- substitute one argument function add(5, y) { return 5 + y; } // step 2 -- substitute the other argument function add(5, 4) { return 5 + 4; }
In this snippet, the steps 1 and 2 are not valid JavaScript, but this is how you would solve that problem by hand. You take the value of the first argument and substitute the unknown x
with the known value 5 throughout the function. Then repeat with the same until you run out of arguments.
Step 1 in this example could be called "partial" application: We only applied the first argument. When you perform a partial application, you don't get a result (a solution), but you get another function instead.
The next snippet demonstrates the use of an imaginary partialApply()
method:
var add = function (x, y) { return x + y; }; // full application add.apply(null, [5, 4]); // 9 // partial application var newadd = add.partialApply(null, [5]); // applying an argument to the new function newadd.apply(null, [4]); // 9
As you can see, the partial application gives us another function, which can then be called with the other arguments. This would actually be equivalent to something like add(5)(4)
, because add(5)
returns a function that can then be called with (4)
. Again, the familiar add(5, 4)
may be thought of as not much more than syntactic sugar instead of using add(5)(4)
.
Now, back to Earth: There's no partialApply()
method and functions in JavaScript don't behave like this by default. But you can make them, because JavaScript is dynamic enough to allow this.
The process of making a function understand and handle partial application is called currying.