Composition Is King

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
2
3
4
5
function addAndLog (a, b) {
let result = a + b;
console.log(result);
return result;
}

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
2
3
4
5
6
7
8
9
function add (a, b) {
return a + b;
}

function addAndLog (a, b) {
let result = add(a, b);
console.log(result);
return result;
}

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
2
3
4
function log (value) {
console.log(value);
return value;
}

I added the return statement at the end so that we can add, e.g.:

1
2
3
4
add(1,2); // returns 3... but we want to log the result too

// so we wrap it:
log(add(1,2)); // logs 3 AND returns 3 so the result can still be used elsewhere

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
2
3
4
5
function logWrapper (operation) {
return function (...args) {
return log(operation(...args));
}
}

So now we can create our old addAndLog function like this:

1
2
3
var addAndLog = logWrapper(add);

addAndLog(1,2); // logs 3 and returns 3

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Modal = React.createClass({
render() {
return (
<div class="modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>this.props.title</h3>
</div>
<div class="modal-body">
{this.props.children}
</div>
</div>
);
},

... // all the life-cycle stuff
});

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function modalify(WrappedComponent) {
return React.createClass({
render: function() {
return (
<div class="modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>this.props.title</h3>
</div>
<div class="modal-body">
<WrappedComponent {...this.props} {...this.state} />
</div>
</div>
)
},

... // all the life-cycle stuff
});
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Modal = React.createClass({
render() {
return (
<div class="modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>this.props.title</h3>
</div>
<div class="modal-body">
{
React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
closeModal: this.closeModal,
...
});
})
}
</div>
</div>
);
},

... // all the life-cycle stuff
});

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const ModalView = Backbone.view.extend({
attributes: {
class: 'modal'
},

init: function() {
_.extend(this.options.childView, {
closeModal: this.closeModal,
...
});
},

render: function() {
// Ignore the fact that I'm not using a template. Please!
this.$el.html(
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h3>' + this.options.title +</h3>' +
'</div>' +
'<div class="modal-body"></div>'
)
.find('.modal-body').append(this.options.childView.render());

return this.$el; // Assume this practice for all `render` methods
},

... // all the life-cycle stuff
});

Then you can use it like this:

1
2
3
let myView = new MyView();
let modal = new ModalView({childView: myView});
$('body').append(modal.render());

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function makeAnimatable(WrappedView) {
return Backbone.View.extend({
initialize: function(options) {
this.wrapped = new WrappedView(options);
},

moveTo: function(x, y) {
this.wrapped.$el.animate({
top: y,
left: x
});
},

render: function() {
return this.wrapped.render();
}
});
}

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
2
3
4
5
6
function moveTo(view, x, y) {
view.$el.animate({
top: y,
left: x
});
}

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!

Author: Joe Zimmerman

Author: Joe Zimmerman Joe Zimmerman has been doing web development ever since he found an HTML book on his dad's shelf when he was 12. Since then, JavaScript has grown in popularity and he has become passionate about it. He also loves to teach others though his blog and other popular blogs. When he's not writing code, he's spending time with his wife and children and leading them in God's Word.