Really Good JavaScript: Part 3/3

Craig Ambrose

Craig Ambrose

Crafter of code, and idealistic change-maker.

When and How to use Object-Oriented Programming in JavaScript

In Part 1 and Part 2, we looked at how we can apply object-oriented techniques to our JavaScript. For many JavaScript developers, the jQuery library provided their first hint that functional programming (FP) provided a different approach. Whether we chose to steer clear of it, or embrace it fully, the prevalence of FP idioms in modern JS development means that a senior JavaScript developer needs to understand functional programming well enough to make informed decisions about it.

Out of the university and into the trenches

Functional programming is often associated with academia, because it’s roots can be found in lambda calculus. Functional languages such as Lisp, OCaml, Haskell might seem obscure and only relevant to the computer scientists who study them, not software engineers tasked with getting things done.

There are, however, many real-world benefits to functional programming languages that make them the best choice for certain applications. Erlang, for example, is a functional language developed at Ericsson and used to develop distributed and fault-tolerant software to run telephone exchanges. A more modern example of Erlang is WhatsApp, which is famously only had 35 engineers at the point where it was serving 450 million users and sold to Facebook for $19 billion.

That example not enough? Here are four more reasons to try functional programming.

  1. No side effects make it easy to run in parallel

For me, this is the #1 reason to try Function Programming. It’s a constraint of most functional languages that data is immutable (cannot be changed) and so it’s totally safe to have multiple operating system threads running the same program in parallel across multiple CPU cores or even multiple machines. Despite being my top reason, this doesn’t apply to JavaScript, so it’s here only to remind you that if this is why you’re applying FP techniques to your code, you need to choose another language.

2. FP techniques are a good way to structure asynchronous code

While we probably aren’t going to run our JavaScript app across multiple cores, we certainly are going to be writing asynchronous code as part of a fundamentally event-based system. Much of the complexity in JS programs comes from this area. The traditional procedural approach of passing function pointers around gets confusing in complex cases, and so something else is often needed. The latest thing in JS for this is async/await, which lets us stick with a fairly procedural coding style, but promises have also become very popular, which is a more Functional Programming solution to this problem.

3. Pure functions are easier to test

In functional languages, most functions take an import, return an output, and don’t modify anything. This can be true in procedural code too, but is less often true in object oriented code where functions (methods) often change member variables. Separating code which causes side effects from pure functions which calculate a result is a good technique that I do like to see in a good JavaScript code-base.

4. FP techniques are a readable way to use collections

The FP techniques used by jQuery are primarily based around operating declaratively on collections of objects without having to manually iterate through each one. It provides composable functions which can be chained and lazily evaluated in the way you would expect from a functional language. This sort of chaining of operations is one of the most readable ways to perform multiple operations on a collection.

So, there are a lot of compelling reasons to use at least some Functional Programming techniques in JavaScript, and the language provides just enough support to get away with them. The tension lies in the fact that if you want to go all-in on Functional Programming, you just can’t get the full benefits without switching to a dedicated functional language.

Writing good FP style code

Functional programming is not the same as the procedural code we have discussed above. It’s at its best when it’s more declarative. Declarative code can be a great way to express certain problems. Take a look at components in React, for example. They declare the current state of the DOM rather than describing every little change required to get there, and as a result, are much more readable.

The danger with this sort of code is that it can be a mystery to someone who doesn’t know how it works. React code may look simple, but it would make no sense if you didn’t know that it a framework was comparing it to the DOM and applying changes. Each type of declaration tends to have something in the background that does the work and only make sense once you know of its existence.

This example, from the React Hooks documentation, shows examples of declarative coding, both in the useState hook, and in the JSX template.

Here are a few things to focus on with your functional code.

  1. Immutability

In pure functional languages, objects are never modified, a new version of them is returned by the function. JavaScript can’t guarantee this for us, and so we can’t multi-core our code. However, a little focus on immutability tends to make our code better is small ways. Knowing for sure that a function doesn’t modify its inputs allows developers to have more confidence and make fewer mistakes.

2. Composability

When we have reusable bits of code, we often want to assemble them in different ways. In object oriented code, a common approach is to use inheritance, but a downside of this is that it’s must be a tree structure. If we consider an inheritance tree for classes relating to different types of vehicles, we might divide them up one way, such as whether they travel through land or water, and then find we want to split another way, such as whether they use propellers or rockets.

If we put all the code relating to propeller-driven planes in the subclass of air-vehicle, it’s hard to re-use it for propeller driver boats. Maybe we should have sub-classed on that first, but then we’d have the opposite problem.

In functional programs, if we make our functions composable in a variety of different ways, we can often create the ability to augment a data structure without ruling out the ability have other functions augment it further. For example, if we consider some jquery code, each of the functions can be chained together in any order and transforms the result passed into it.

$(“#p1”).css(“color”, “red”).slideUp(2000).slideDown(2000);

3. Readability

Function programs look different to object oriented ones. At a simple level, they are often centred around the verbs (operations), rather than the nouns (objects). Conventional OO thinking might be that this isn’t as good. Objects provide us with an ability to structure our code by bundling it all up into classes that make sense in our problem domain.

Some problems though, don’t seem to map as well to an object model. Perhaps you’re building something is much more of a process than a thing, such as a service that processes and resizes images. An OO approach to the problem might end up with a lot of classes with rather abstract names, like ImageResizingService. If the goal of the program is to ensure that inputs flow through transformations to outputs, and object oriented code-base can actually make it harder for a new person to read and understand this flow.

Consider how we can make this most understandable to our reader in a functional program. Perhaps our image tool is like a conveyor belt in a factory. Files move through and have different transformations applied in several steps. Readable code should show the action of the conveyor belt, possibly using functions chained together. To see how the steps work or test them individually, dive down deeper into the functions.

For this sort of code, ensure that it’s read in the same direction as it executes, never be tempted to write code like.

const result = stepThree(stepTwo(stepOne(input)))

Anyone of the following ways would look better:

Wrapping up

Functional programming is a great tool to have in your tool-belt. If you have only ever written Object-Oriented code, don’t expect functional programming to come naturally. It’s a learned skill, and it may be a little different to what you’re used to. JavaScript is a language that can support functional programming, but it’s not a very good language to use for learning it, because it doesn’t enforce functional programming, and often doesn’t even make it very easy or natural. To get started with it, I recommend learning by doing some basic functional programming in a dedication functional language. Elm is a fun one for toy projects, or if you don’t like strong typing, perhaps Elixir or Clojure/ClojureScript.

Once you’ve tried that, come back and try the new techniques you’ve learned in JavaScript.

Craig enjoying another ice-cream sandwich in Korea

Related posts

Anwesha Chatterjee

Remote Pairing – Do’s and Don’ts

Whilst pairing has been a fairly consistent part of my years as a software developer, Cogent was where I first started remote pairing. I’ve found the remote aspect of the experience amplifies certain challenges so in this post, I share some of the lessons I’ve learnt.

Read More
Sam Chalela

Deploying micro frontends with module federation and Next.js

Micro frontends are an architectural pattern that has become a hot topic in recent years for many good reasons. It has helped organisations break complex UI applications into smaller, more manageable pieces, as well as enabling teams to work end-to-end across a well-defined product or business domain whilst maintaining a consistent user experience across the entire app.

Read More