Functional and Object-oriented Programming in JavaScript - Functional Programming in JavaScript (2015)

Functional Programming in JavaScript (2015)

Chapter 7. Functional and Object-oriented Programming in JavaScript

You will often hear that JavaScript is a blank language, where blank is either object-oriented, functional, or general-purpose. This book has focused on JavaScript as a functional language and has gone to great lengths to prove that it is. But the truth is that JavaScript is a general-purpose language, meaning it's fully capable of multiple programming styles. Like Python and F#, JavaScript is multi-paradigm. But unlike those languages, JavaScript's OOP side is prototype-based while most other general-purpose languages are class-based.

In this final chapter, we will relate both functional and object-oriented programming to JavaScript, and see how the two paradigms can complement each other and coexist side-by-side. In this chapter the following topics will be covered:

· How can JavaScript be both functional and OOP?

· JavaScript's OOP – using prototypes

· How to mix functional and OOP in JavaScript

· Functional inheritance

· Functional mixins

Better code is the goal. Functional and object-oriented programming are just means to this end.

JavaScript – the multi-paradigm language

If object-oriented programming means treating all variables as objects, and functional programming means treating all functions as variables, then can't functions be treated like objects? In JavaScript, they can.

But saying that functional programming means treating functions as variables is somewhat inaccurate. A better way to put it is: functional programming means treating everything as a value, especially functions.

A better way still to describe functional programming may be to call it declarative. Independent of the imperative branch of programming styles, declarative programming expresses the logic of computation required to solve the problem. The computer is told what the problem is rather than the procedure for how to solve it.

Meanwhile, object-oriented programming is derived from the imperative programming style: the computer is given step-by-step instructions for how to solve the problem. OOP mandates that the instructions for computation (methods) and the data they work on (member variables) be organized into units called objects. The only way to access that data is through the object's methods.

So how can these two styles be integrated together?

· The code inside the object's methods is typically written in an imperative style. But what if it was in a functional style? After all, OOP doesn't exclude immutable data and higher-order functions.

· Perhaps a purer way to mix the two would be to treat objects both as functions and as traditional, class-based objects at the same time.

· Maybe we can simply include several ideas from functional programming—such as promises and recursion—into our object-oriented application.

· OOP covers topics such as encapsulation, polymorphism, and abstraction. So does functional programming, it just goes about it in a different way. So maybe we can include several ideas from object-oriented programming in our functional-oriented application.

The point is: OOP and FP can be mixed together and there are several ways to do it. They're not exclusive of each other.

JavaScript's object-oriented implementation – using prototypes

JavaScript is a class-less language. That's not to mean it is less fashionable or more blue-collar than other computer languages; class-less means it doesn't have a class structure in the same way that object-oriented languages do. Instead, it uses prototypes for inheritance.

Although this may be baffling to programmers with backgrounds in C++ and Java, prototype-based inheritance can be much more expressive than traditional inheritance. The following is a brief comparison between the differences between C++ and JavaScript:

C++

JavaScript

Strongly typed

Loosely typed

Static

Dynamic

Class-based

Prototype-based

Classes

Functions

Constructors

Functions

Methods

Functions

Inheritance

Before we go much further, let's make sure we fully understand the concept of inheritance in object-oriented programming. Class-based inheritance is demonstrated in the following pseudo-code:

class Polygon {

int numSides;

function init(n) {

numSides = n;

}

}

class Rectangle inherits Polygon {

int width;

int length;

function init(w, l) {

numSides = 4;

width = w;

length = l;

}

function getArea() {

return w * l;

}

}

class Square inherits Rectangle {

function init(s) {

numSides = 4;

width = s;

length = s;

}

}

The Polygon class is the parent class the other classes inherit from. It defines just one member variable, the number of sides, which is set in the init() function. The Rectangle subclass inherits from the Polygon class and adds two more member variables, length and width, and a method,getArea(). It doesn't need to define the numSides variable because it was already defined by the class it inherits from, and it also overrides the init() function. The Square class carries on this chain of inheritance even further by inheriting from the Rectangle class for its getArea() method. By simply overriding the init() function again such that the length and width are the same, the getArea() function can remain unchanged and less code needs to be written.

In a traditional OOP language, this is what inheritance is all about. If we wanted to add a color property to all the objects, all we would have to do is add it to the Polygon object without having to modify any of the objects that inherit from it.

JavaScript's prototype chain

Inheritance in JavaScript comes down to prototypes. Each object has an internal property known as its prototype, which is a link to another object. That object has a prototype of its own. This pattern can repeat until an object is reached that has undefined as its prototype. This is known as the prototype chain, and it's how inheritance works in JavaScript. The following diagram explain the inheritance in JavaScirpt:

JavaScript's prototype chain

When running a search for an object's function definition, JavaScript "walks" the prototype chain until it finds the first definition of a function with the right name. Therefore, overriding it is as simple as providing a new definition on the prototype of the subclass.

Inheritance in JavaScript and the Object.create() method

Just as there are many ways to create objects in JavaScript, there are also many ways to replicate class-based, classical inheritance. But the one preferred way to do it is with the Object.create() method.

var Polygon = function(n) {

this.numSides = n;

}

var Rectangle = function(w, l) {

this.width = w;

this.length = l;

}

// the Rectangle's prototype is redefined with Object.create

Rectangle.prototype = Object.create(Polygon.prototype);

// it's important to now restore the constructor attribute

// otherwise it stays linked to the Polygon

Rectangle.prototype.constructor = Rectangle;

// now we can continue to define the Rectangle class

Rectangle.prototype.numSides = 4;

Rectangle.prototype.getArea = function() {

return this.width * this.length;

}

var Square = function(w) {

this.width = w;

this.length = w;

}

Square.prototype = Object.create(Rectangle.prototype);

Square.prototype.constructor = Square;

var s = new Square(5);

console.log( s.getArea() ); // 25

This syntax may seem unusual to many but, with a little practice, it will become familiar. The prototype keyword must be used to gain access to the internal property, [[Prototype]], which all objects have. The Object.create() method declares a new object with a specified object for its prototype to inherit from. In this way, classical inheritance can be achieved in JavaScript.

Note

The Object.create() method was introduced in ECMAScript 5.1 in 2011, and it was billed as the new and preferred way to create objects. This was just one of many attempts to integrate inheritance into JavaScript. Thankfully, this method works pretty well.

We saw this structure of inheritance when building the Maybe classes in Chapter 5, Category Theory. Here are the Maybe, None, and Just classes, which inherit from each other just like the preceding example.

var Maybe = function(){};

var None = function(){};

None.prototype = Object.create(Maybe.prototype);

None.prototype.constructor = None;

None.prototype.toString = function(){return 'None';};

var Just = function(x){this.x = x;};

Just.prototype = Object.create(Maybe.prototype);

Just.prototype.constructor = Just;

Just.prototype.toString = function(){return "Just "+this.x;};

This shows that class inheritance in JavaScript can be an enabler of functional programming.

A common mistake is to pass a constructor into Object.create() instead of a prototype object. This problem is compounded by the fact that an error will not be thrown until the subclass tries to use an inherited member function.

Foo.prototype = Object.create(Parent.prototype); // correct

Bar.prototype = Object.create(Parent); // incorrect

Bar.inheritedMethod(); // Error: function is undefined

The function won't be found if the inheritedMethod() method has been attached to the Foo.prototype class. If the inheritedMethod() method is attached directly to the instance with this.inheritedMethod = function(){...} in the Bar constructor, then this use of Parent as an argument ofObject.create()could be correct.

Mixing functional and object-oriented programming in JavaScript

Object-oriented programming has been the dominant programming paradigm for several decades. It is taught in Computer Science 101 classes around the world, while functional programming is not. It is what software architects use to design applications, while functional programming is not. And it makes sense too: OOP makes it easy to conceptualize abstract ideas. It makes it easier to write code.

So, unless you can convince your boss that the application needs to be all functional, we're going to be using functional programming in an object-oriented world. This section will explore ways to do this.

Functional inheritance

Perhaps the most accessible way to apply functional programming to JavaScript applications is to use a mostly functional style within OOP principles, such as inheritance.

To explore how this might work, let's build a simple application that calculates the price of a product. First, we'll need some product classes:

var Shirt = function(size) {

this.size = size;

};

var TShirt = function(size) {

this.size = size;

};

TShirt.prototype = Object.create(Shirt.prototype);

TShirt.prototype.constructor = TShirt;

TShirt.prototype.getPrice = function(){

if (this.size == 'small') {

return 5;

}

else {

return 10;

}

}

var ExpensiveShirt = function(size) {

this.size = size;

}

ExpensiveShirt.prototype = Object.create(Shirt.prototype);

ExpensiveShirt.prototype.constructor = ExpensiveShirt;

ExpensiveShirt.prototype.getPrice = function() {

if (this.size == 'small') {

return 20;

}

else {

return 30;

}

}

We can then organize them within a Store class as follows:

var Store = function(products) {

this.products = products;

}

Store.prototype.calculateTotal = function(){

return this.products.reduce(function(sum,product) {

return sum + product.getPrice();

}, 10) * TAX; // start with $10 markup, times global TAX var

};

var TAX = 1.08;

var p1 = new TShirt('small');

var p2 = new ExpensiveShirt('large');

var s = new Store([p1,p2]);

console.log(s.calculateTotal()); // Output: 35

The calculateTotal() method uses the array's reduce() function to cleanly sum together the prices of the products.

This works just fine, but what if we need a dynamic way to calculate the markup value? For this, we can turn to a concept called Strategy Pattern.

Strategy Pattern

Strategy Pattern is a method for defining a family of interchangeable algorithms. It is used by OOP programmers to manipulate behavior at runtime, but it is based on a few functional programming principles:

· Separation of logic and data

· Composition of functions

· Functions as first-class objects

And a couple of OOP principles as well:

· Encapsulation

· Inheritance

In our example application for calculating product cost, explained previously, let's say we want to give preferential treatment to certain customers, and that the markup will have to be adjusted to reflect this.

So let's create some customer classes:

var Customer = function(){};

Customer.prototype.calculateTotal = function(products) {

return products.reduce(function(total, product) {

return total + product.getPrice();

}, 10) * TAX;

};

var RepeatCustomer = function(){};

RepeatCustomer.prototype = Object.create(Customer.prototype);

RepeatCustomer.prototype.constructor = RepeatCustomer;

RepeatCustomer.prototype.calculateTotal = function(products) {

return products.reduce(function(total, product) {

return total + product.getPrice();

}, 5) * TAX;

};

var TaxExemptCustomer = function(){};

TaxExemptCustomer.prototype = Object.create(Customer.prototype);

TaxExemptCustomer.prototype.constructor = TaxExemptCustomer;

TaxExemptCustomer.prototype.calculateTotal = function(products) {

return products.reduce(function(total, product) {

return total + product.getPrice();

}, 10);

};

Each Customer class encapsulates the algorithm. Now we just need the Store class to call the Customer class's calculateTotal() method.

var Store = function(products) {

this.products = products;

this.customer = new Customer();

// bonus exercise: use Maybes from Chapter 5 instead of a default customer instance

}

Store.prototype.setCustomer = function(customer) {

this.customer = customer;

}

Store.prototype.getTotal = function(){

return this.customer.calculateTotal(this.products);

};

var p1 = new TShirt('small');

var p2 = new ExpensiveShirt('large');

var s = new Store([p1,p2]);

var c = new TaxExemptCustomer();

s.setCustomer(c);

s.getTotal(); // Output: 45

The Customer classes do the calculating, the Product classes hold the data (the prices), and the Store class maintains the context. This achieves a very high level of cohesion and a very good mixture of object-oriented programming and functional programming. JavaScript's high level of expressiveness makes this possible and quite easy.

Mixins

In a nutshell, mixins are classes that can allow other classes to use their methods. The methods are intended to be used solely by other classes, and the mixin class itself is never to be instantiated. This helps to avoid inheritance ambiguity. And they're a great means of mixing functional programming with object-oriented programming.

Mixins are implemented differently in each language. Thanks to JavaScript's flexibility and expressiveness, mixins are implemented as objects with only methods. While they can be defined as function objects (that is, var mixin = function(){...};), it would be better for the structural discipline of the code to define them as object literals (that is, var mixin = {...};). This will help us to distinguish between classes and mixins. After all, mixins should be treated as processes, not objects.

Let's start with declaring some mixins. We'll extend our Store application from the previous section, using mixins to expand on the classes.

var small = {

getPrice: function() {

return this.basePrice + 6;

},

getDimensions: function() {

return [44,63]

}

}

var large = {

getPrice: function() {

return this.basePrice + 10;

},

getDimensions: function() {

return [64,83]

}

};

We're not limited to just this. Many more mixins can be added, like colors or fabric material. We'll have to rewrite our Shirt classes a little bit, as shown in the following code snippet:

var Shirt = function() {

this.basePrice = 1;

};

Shirt.getPrice = function(){

return this.basePrice;

}

var TShirt = function() {

this.basePrice = 5;

};

TShirt.prototype = Object.create(Shirt.prototype);

TShirt..prototype.constructor = TShirt;

Now we're ready to use mixins.

Classical mixins

You're probably wondering just how these mixins get mixed with the classes. The classical way to do this is by copying the mixin's functions into the receiving object. This can be done with the following extension to the Shirt prototype:

Shirt.prototype.addMixin = function (mixin) {

for (var prop in mixin) {

if (mixin.hasOwnProperty(prop)) {

this.prototype[prop] = mixin[prop];

}

}

};

And now the mixins can be added as follows:

TShirt.addMixin(small);

var p1 = new TShirt();

console.log( p1.getPrice() ); // Output: 11

TShirt.addMixin(large);

var p2 = new TShirt();

console.log( p2.getPrice() ); // Output: 15

However, there is a major problem. When the price of p1 is calculated again, it comes back as 15, the price of a large item. It should be the value for a small one!

console.log( p1.getPrice() ); // Output: 15

The problem is that the Shirt object's prototype.getPrice() method is getting rewritten every time a mixin is added to it; this is not very functional at all and not what we want.

Functional mixins

There's another way to use mixins, one that is more aligned with functional programming.

Instead of copying the methods of the mixin to the target object, we need to create a new object that is a clone of the target object with the mixin's methods added in. The object must be cloned first, and this is achieved by creating a new object that inherits from it. We'll call this variationplusMixin.

Shirt.prototype.plusMixin = function(mixin) {

// create a new object that inherits from the old

var newObj = this;

newObj.prototype = Object.create(this.prototype);

for (var prop in mixin) {

if (mixin.hasOwnProperty(prop)) {

newObj.prototype[prop] = mixin[prop];

}

}

return newObj;

};

var SmallTShirt = Tshirt.plusMixin(small); // creates a new class

var smallT = new SmallTShirt();

console.log( smallT.getPrice() ); // Output: 11

var LargeTShirt = Tshirt.plusMixin(large);

var largeT = new LargeTShirt();

console.log( largeT.getPrice() ); // Output: 15

console.log( smallT.getPrice() ); // Output: 11 (not effected by 2nd mixin call)

Here comes the fun part! Now we can get really functional with the mixins. We can create every possible combination of products and mixins.

// in the real world there would be way more products and mixins!

var productClasses = [ExpensiveShirt, Tshirt];

var mixins = [small, medium, large];

// mix them all together

products = productClasses.reduce(function(previous, current) {

var newProduct = mixins.map(function(mxn) {

var mixedClass = current.plusMixin(mxn);

var temp = new mixedClass();

return temp;

});

return previous.concat(newProduct);

},[]);

products.forEach(function(o){console.log(o.getPrice())});

To make it more object-oriented, we can rewrite the Store object with this functionality. We'll also add a display function to the Store object, not the products, to keep the interface logic and the data separated.

// the store

var Store = function() {

productClasses = [ExpensiveShirt, TShirt];

productMixins = [small, medium, large];

this.products = productClasses.reduce(function(previous, current) {

var newObjs = productMixins.map(function(mxn) {

var mixedClass = current.plusMixin(mxn);

var temp = new mixedClass();

return temp;

});

return previous.concat(newObjs);

},[]);

}

Store.prototype.displayProducts = function(){

this.products.forEach(function(p) {

$('ul#products').append('<li>'+p.getTitle()+': $'+p.getPrice()+'</li>');

});

}

And all we have to do is create a Store object and call its displayProducts() method to generate a list of products and prices!

<ul id="products">

<li>small premium shirt: $16</li>

<li>medium premium shirt: $18</li>

<li>large premium shirt: $20</li>

<li>small t-shirt: $11</li>

<li>medium t-shirt: $13</li>

<li>large t-shirt: $15</li>

</ul>

These lines need to be added to the product classes and mixins to get the preceding output to work:

Shirt.prototype.title = 'shirt';

TShirt.prototype.title = 't-shirt';

ExpensiveShirt.prototype.title = 'premium shirt';

// then the mixins got the extra 'getTitle' function:

var small = {

...

getTitle: function() {

return 'small ' + this.title; // small or medium or large

}

}

And, just like that, we have an e-commerce application that is highly modular and extendable. New shirt styles can be added absurdly easily—just define a new Shirt subclass and add to it the Store class's array product classes. Mixins are added in just the same way. So now when our boss says, "Hey, we have a new type of shirt and a coat, each available in the standard colors, and we need them added to the website before you go home today", we can rest assured that we'll not be staying late!

Summary

JavaScript has a high level of expressiveness. This makes it possible to mix functional and object-oriented programming. Modern JavaScript is not solely OOP or functional—it is a mixture of the two. Concepts such as Strategy Pattern and mixins are perfect for JavaScript's prototype structure, and they help to prove that today's best practices in JavaScript share equal amounts of functional programming and object-oriented programming.

If you were to take away only one thing from this book, I would want it to be how to apply functional programming techniques to real-world applications. And this chapter showed you how to do exactly that.