The JavaScript community is becoming flooded with articles pushing to move toward functional programming or at least more toward composition over inheritance. For a long time we’ve tried to standardize inheritance without the huge mess that comes with the verbosity of the prototype syntax, and now that we have a standard class
keyword in ES2015, people are trying harder than ever to tell us we don’t need it. Those people are, for the most part, correct.
Of course, they can’t be wrong. The source of truth for all Object-Oriented programmers is Design Patterns: Elements of Reusable Object-Oriented Software by the “Gang of Four”, which itself says to prefer composition over inheritance. It seems like most people don’t understand that though. They’re taught about inheritance and then try to do everything with that, but that’s not nearly as powerful or scaleable.
Back to JavaScript, which can take lessons from the design patterns book, but it’s a much different language than the one that that book was written for. Besides using prototypes rather than true classes, it is also filled with many bits of functional programming features. I’m not going to say “don’t use the new class
keyword or inheritance,” or anything like that. I just want you to use the best tool for the job that you understand how to use. I want to say that going all out functional can be a great way to program, however, it’s not the simplest concept (at least, not if you dive in deep), so please just do what makes sense for you.
That being said, I’d like to show you some great examples of composition to help you learn how to use it and to show you where it can be helpful.
Function Composition
We’ll start with composition of functions, cuz why not? Let’s say you have the following very simple functions:
1 | function addAndLog (a, b) { |
This seems quite simple, but it can actually broken down into 2 completely operations: retrieving the result of an operation and logging the result. This means that if you want to just get the result of the operation without logging it, you’re out of luck, so let’s split the operation into a separate function:
1 | function add (a, b) { |
Great, now the addition operation can be used anywhere apart from the logging, but that addAndLog is still hard coded to log the result of the add
operation rather than being generalized to use the result of any operation. So let’s split the logging functionality out into its own function:
1 | function log (value) { |
I added the return
statement at the end so that we can add, e.g.:
1 | add(1,2); // returns 3... but we want to log the result too |
Heck, one of the biggest reasons we can’t just stick with using console.log
in this case is because it simply returns undefined
. Anyway, these nested invocations of functions are one of the things I like least about functional programming practices because it is essentially read from right to left, which is now how we westerners tend to read.
So, one of the things we can do about it is convert log
into a higher order function. A higher order function is a function that returns a function (simplified definition). The new function, which we’ll call logWrapper
will be able to accept a function as an argument, then return a new function that invokes the function you passed in, plus do the logging, plus return the result.
1 | function logWrapper (operation) { |
So now we can create our old addAndLog
function like this:
1 | var addAndLog = logWrapper(add); |
Or we can combine it with any other operation, so it’s nice and generic.
That’s composition! You created flexibility by allowing the log function to be composed of any operation plus the logging functionality. Of course, now logWrapper
is stuck with the logging functionality. There are several ways to generalize this even further by making a function that can take any number of functions and compose them together for you, but I think you get the idea. There are plenty of other tutorials out there about chaining or currying or piping or composing out there. I just wanted to give you an example.
View / Component Composition
I could just talk about normal object composition, but everybody has done that already. Instead let’s talk about composition of views and components (as in React components). Why views and components? Mostly just because everyone is using some sort of framework with views and/or components in them, so it can be more relevant.
React Component Composition
Let’s start with React, despite the fact that I’ve never written about React on this blog. A common example used for mixins is modals or overlays, whatever you want to call them. I think modals can be handled better with composition though:
1 | const Modal = React.createClass({ |
Since you are using props.children
, you can just nest your view directly inside the Modal
component:
1 | ReactDOM.render(<Modal> <MyView/> </Modal>, mountNode); |
Or you can use what is called a “higher order component”, which is a function that returns a component that wraps your component for you:
1 | function modalify(WrappedComponent) { |
Now if you want your component to be inside a modal, you can pass your component into a call to modalify
and you’ll receive a modal component that will display your component.
1 | ReactDOM.render(modalify(<MyView/>), mountNode); |
modalify
uses the JSX spread syntax to pass all props and state down automatically, though it may be more useful to use something like Lodash’s omit
function to strip out the modal-specific properties. The interesting thing about this higher order component pattern is that you can the wrapped component access to the life-cycle methods, or any other functionality that the modal has access to. For example, if the wrapped component is a form, you may want to close the modal once the form is submitted successfully, so you can pass the closeModal
(not actually shown in the example code above) method to WrappedComponent
as a property so it can call closeModal
once the form is submitted.
You can technically pass access to those methods to MyView
in the first nested components example like this:
1 | const Modal = React.createClass({ |
Instead of just using {this.props.children}
, we use React.Children.map
and React.cloneElement
to augment the child views with modal functionality.
If you’d like some more examples of ways React can be composed rather than using inheritance or mixins, check out the post titled “Mixins Considered Harmful” by Dan Abramov. That post is actually what gave me inspiration for this post, since it primarily dealt with React and I wanted to go further and demonstrate it with Backbone too, which is what we’ll do now.
Backbone View Composition
You can do pretty much the same thing with Backbone as what we did with React, except Backbone doesn’t have that JSX syntax or as clean a way to pass child views around, but we can still do the same thing with options
.
1 | const ModalView = Backbone.view.extend({ |
Then you can use it like this:
1 | let myView = new MyView(); |
You could also use a “higher order view” pattern like we did with React, but I personally believe that nested views make more sense in this case. The higher order view and higher order component patterns is generally more useful if you’re only adding functionality without wrapping the component in more HTML, such as adding an moveTo(x,y)
method that animates the positioning of the wrapped component:
1 | function makeAnimatable(WrappedView) { |
This pretty much does it. You’ll probably want to find a way to delegate all method calls to this.wrapped
, though. Probably a simple way of doing this, though would be to just create a utility function that can be called from anywhere, rather than making moveTo
a method:
1 | function moveTo(view, x, y) { |
But that would be too easy. ;) This is actually one of the advantages to having a language that is both object-oriented (not class-based in the traditional sense, but still object-oriented) and functional. Lone functions can often significantly reduce complexity versus trying to accomplish things with wrappers or inheritance or mixins, etc.
Conclusion
That’s all I’ve got for you today. I hope you’ve learned something useful: Composition, or even just plain functions as shown at the end, can be used to untangle some of the nastiest patterns in object-oriented programming. Just remember: composition over inheritance… and keep is simple, no matter which way you go. God bless and happy coding!