We are often unaware of the number of assumptions we make when we communicate with other people in our native languages. If we told you to "count to three", we would expect you to say or think the numbers one, two, and three. We assumed you would know to start with "one" and end with "three". With programming, we're faced with needing to be more explicit with our directions to the computer. Here's how we might tell the computer to "count to three":
1 2 3
In programming, we can accomplish abstraction by writing functions. In addition to allowing us to reuse our code, functions help to make clear, readable programs. If you encountered
countToThree() in a program, you might be able to quickly guess what the function did without having to stop and read the function's body.
There is another way to add a level of abstraction to our programming: higher-order functions. Higher-order functions are functions that accept other functions as arguments and/or return functions as output. This enables us to build abstractions on other abstractions, just like "We hosted a birthday party" is an abstraction that may build on the abstractions "We made a cake".
Functions as Data
JS functions behave like any other data type in the language; we can assign functions to variables, and we can reassign them to new variables.
Below, we have an annoyingly long function name that hurts the readability of any code in which it's used. Let's pretend this function does important work and needs to be called repeatedly!
1 2 3
1 2 3
busy is a variable that holds a reference to our original function. If we could look up the address in memory of
busy and the address in memory of
announceThatIAmDoingImportantWork they would point to the same place. Our new
busy() function can be invoked with parenthesis as if that was the name we originally gave our function.
Notice how we assign
announceThatIAmDoingImportantWork without parentheses as the value to the
busy variable. We want to assign the value of the function itself, not the value that it returns when invoked.
In JS, functions are first class objects, which means that, like other methods we have encountered, JS functions can have properties and methods.
Since functions are a type of object, they have properties such as
.name and methods such as
.toString(). You can see more about the methods and properties of functions in the following documentation.
Functions are special because we can invoke them, but we can still treat them like any other type of data.
Functions as Parameters
Since functions can behave like any other type of data in JS, it might not surprise you to learn that we can also pass functions (into other functions) as parameters. A higher-order function is a function that either accepts functions as parameters, returns a function, or both! We call the functions that get passed in as parameters and invoked callback functions because they get called during the execution of the higher-order function.
When we pass a function in as an argument to another function, we don't invoke it. Invoking the function would evaluate to the return value of that function call. With callbacks, we pass the function itself by typing the function name without the parentheses (that would evaluate to the result of the function called):
1 2 3 4 5 6 7 8 9 10
We wrote a higher-order function,
timeFuncRuntime(). It takes in a function as an argument, saves a starting time, invokes the callback function, records the time after the function was called, and returns the time the function took to run by subtracting the starting time from the ending time.
This higher-order function could be used with any callback function which makes it a potentially powerful piece of code.
We then invoked
timeFuncRuntime() first with the
addOneToOne() function - note how we passed in the
addOneToOne and did not invoke it.
1 2 3 4 5
timeFuncRuntime()with an anonymous function that counts backwards from 10. Anonymous functions can be arguments too!
Abstraction allows us to write complicated code in a way that’s easy to reuse, debug, and understand for human readers
We can work with functions the same way we would any other type of data including reassigning them to new variables
Functions can be passed into other functions as parameters
A higher-order function is a function that either accepts functions as parameters, returns a function, or both