Why Functions Deserve First-Class Citizenry Too
Attributes of a First-Class Function
- Be stored in a variable;
- Be passed as arguments to functions;
- Be returned by functions;
- Be stored in some data structure; and,
- Hold their own properties and methods.
As an example, consider the following snippet, in which we shall attempt each of the above-described operations on a function.
The above snippet shows us that functions are indeed more flexible that you might first have imagined. Let’s briefly walk through each operation.
Storage in a Variable
On line 2 we declare our function
speak() and subsequently demonstrate its invocation on line 5. So far things are looking pretty standard. Then, on line 8 we define a variable
talk and assign in the value of
speak. When we call
talk as a function on line 9 we get the same functionality as when calling
speak! This shows that you can store a function in a variable. Not only that, but in this case both
talk refer to the same function, and the expression
speak === talk will return
Passage as an Argument to a Function; Return Value from a Function
On line 13 we declare another function, self-consciously called
functionReturner, which serves the somewhat silly purpose of receiving a function as an argument and simply returning it. And indeed, on line 16 when we pass
functionReturner our function
talk as an argument and assign its return value to the variable
chat, we get exactly the same functionality as we did with
speak. This is a very arbitrary example, but it shows that functions can indeed be passed as arguments to other functions, and that they can be the return value from other functions too— just like any other first-class object.
Storage in a Data Structure
Not only can functions be stored in variables, but they can also be stored in data structures. In this example, on line 20, we store our function
talk inside an array called
myFuncs. On line 21, when we attempt to access
myFuncs, which we expect to be a function, and call it, we do in fact get the functionality we are expecting. A function can similarly be stored on other data structures, such as an object, in which case we generally refer to it as a method of that object.
Owner of a Property
And finally, on line 24 we assign our function
talk an arbitrary property called
myProperty and store the value
“bananas” inside it (because, you know, why not?) Just like with any other object, we can directly access this property and we do so on line 25.
Uses of a First-Class Function
In this snippet, we first define a variable
myNums which contains an array of numbers. We also define a function
doubleNum which accepts a number as an argument and returns double the value of that number. We then demonstrate higher-order functions using built-in function
Array.prototype.map, which iterates over an array, transforming each value and returning a new array with those values. On line 8, in the first example, we provide
map with an anonymous function that doubles each value in the array. On line 14, we pass
map our predefined function
doubleNum, which it then uses in the transformation process. In both cases we get the same result.
Partial Function Application
On line 1 we define our own higher-order function called
applicator, which takes a function and some value as its arguments.
applicator then returns a new function, which itself calls the previously provided function using the previously provided value as its argument. We do just this on line 11 where we define a variable
sayHello and assign it the return value of
applicator, passing in the
speak function we used earlier and the value
“Hello”. This gives us a function that always logs
“Hello” when called (as in on line 12.)
So why is this so powerful? Well, not only does it let us create new functions with pre-applied values, but we can do so over and over, as much as needed. In this case we made a
sayHello function (which can say
“hello” without any parameters). But if we wanted we could use
applicator to make a
sayHi function, a
sayGoodbye function, or whatever else we want.
Another common use of first-class function is when an asynchronous callback is necessary. This is often the case in web applications where some function must wait to be called until a server has returned some value or a promise has been fulfilled. We won’t get into that here but we can simulate delaying a function call using the built-in function
setTimeout, which accepts a callback and a delay as values. Here is an example using our trusty speak function.
In this snippet, on line 5 we define a new higher-order function called
delayedFunction, which accepts a function as a an argument and then returns a new function, which accepts a value and a delay. The new function uses
setTimeout to wait
delay length before executing the original function we provided to
delayedFunction with the provided value as its argument. We then use
delayedFunction on line 13, passing in our
speak function, to create a new function called
delayedSpeak. Now we can use
speak with some delay! Here we have not only used first-class functions, and higher-order functions, but also seen how to asynchronously execute our code.