It’s a bit like a opening a present and finding another present inside… if you’re not expecting it, it can be a bit jarring. But if you know how to work with this idea, it can be fun and exciting!
There are a lot of great uses for higher order functions, including many common functions like “debounce” or “throttle” found in the underscore / lodash libraries.
In this example, a throttle function is used to prevent a window resize event in a browser, from constantly re-working the layout of the screen.
Build Your Own Bind Function
The basic idea is to pass in a function and a context object to represent “this” within that function.
Notice that the “bind” function itself doesn’t do anything more than return another function. Some higher order functions will do some calculations and setup some other code before returning the inner function, and others won’t. The important thing is that the bind function returns another function.
The inner function – the one that gets returned – is where the real magic happens in this case. When this function is returned, it will be assigned to a variable of your choosing. That variable is now pointing to a function that will:
- split the current arguments object into a proper array
- call the original function (passed as the “fn” parameter), and
- apply the “ctx” variable as the context (“this”) for the original function
This very simple example creates a “foo” function that logs “this.bar”. The bind function is used to create a function with an object literal specified as the context. Calling the resulting function will produce the expected “this is a test” console message.
Dynamically Constructing Object Methods
In a single page app, for example, you may have some code to render specific views onto the screen when someone clicks a link or a button in a menu. Each of the menu items produces a different view, but all of them must be shown within the same basic layout.
When the app is already up and running, you would render the new view in to the proper part of the layout. But, if the user hits the refresh button on their browser, you want to make sure the layout is in place before putting the view into it.
To manage this, you could have a “showLayout” function that returns a promise. Inside each method that shows a specific view, you can call this method and wait for the promise to resolve.
This works, but it starts to get ugly with all the .then calls. Also, if you need to change how the showLayout method is called, you have to change it in every one of these methods. Maybe that’s not a big deal, maybe it is.
Using higher order functions, though, this code can be simplified. Instead of putting the showLayout().then() code in every single function, use a higher order function to construct that behavior at runtime:
Here, the showLayout code is encapsulated in a single location – the “useLayout” function. This function returns functions that know about the MyApp object and assume they will be attached to that object.
The useLayout function may be less than re-usable outside of the MyApp object, but that’s ok. The purpose was to encapsulate the call to showLayout so that this call can be changed quickly and easily, as needed.
Reducing the amount of code duplication is nice, and it’s also allowed a reduction in the amount of code for each of the methods. They are easier to read and easier to modify.
But what happens if you need to pass additional parameters to the methods on MyApp at runtime? Say, an ID from a router?
Passing Args From The Function Call
If your app needs an ID or other data passed from a router, the useLayout function will have to account for this. It will still need to pass the layout along to the destination function, but it will also need to pass the id or any other parameters along as well.
To do this, modify the function that is returned from useLayout and have it track the arguments (using the …rest operator from ES6). Then, just before fn.apply is called, add the layout object to the args array – prepend or append doesn’t matter.
Now when the method on MyApp accepts a parameter, you will still get the layout and the parameter specified in the call:
The layout is injected as the first parameter to the editThing method implementation, but this is done behind the scenes. When calling editThing, only the ID needs to be specified. The rest of it is handled by the implementation of useLayout.
Put The Fun In Higher Order Functions
With a combination of functions returning functions and a fair understanding of arguments, “this”, and .apply, you can easily compose objects out of re-useable behaviors with very little code. This means less code to write, less code to maintain, and easier changes to the implementation of the common behaviors.