Polymorphism in JavaScript

Zell Liew 🤗 - Jul 29 '20 - - Dev Community

For the longest time, I thought that "Polymorphing" was about converting something into sheep (thanks to Warcraft). The sheep image stuck with me and made it hard to understand exactly what Polymorphism is.

Today I want to explore what Polymorphism actually is. (Fun fact: Most articles about Polymorphism in JavaScript covers less than 1/3 of what it actually is).

What is Polymorphism

Polymorphism comes from the word Polymorph.

  • Poly: Many.
  • Morph: Change from one form to another.

So Polymorphism is the ability to take on multiple forms.

*There are three kinds of Polymorphism in programming: *

  1. Adhoc Polymorphism
  2. Parametric Polymorphism
  3. Subtype Polymorphism

Most articles on Object Oriented Programming and Polymorphism explains the 3rd type only. They don't explain the other two.

Adhoc polymorphism

Adhoc is used to describe creation of something without previous planning. In other words, Adhoc Polymorphism means to change something from one form to another on the spot.

There are many forms of Adhoc Polymorphism

  1. Operator Overloading
  2. Function Overloading
  3. Coercion Polymorphism

Operator Overloading

Overloading means being able to do more than one thing.

Example:

The + operator in JavaScript does many things. You can use it to add numbers. You can also use it to concatenate strings.

// Adding numbers
1 + 1 // Results in 2

// Adding Strings
'Hello' + ' ' + 'World' // Results in 'Hello World'

// Adding Numbers to Strings
1 + 'up' // Results in '1up'
Enter fullscreen mode Exit fullscreen mode

Shows results of the code above inside a dev console.

The type of the result changes depending on what's being added.

  • Number + Number creates Number
  • Number + String creates String

The + operator in this case, allows you to change the values from one form (like Number) to another (like String).

Function Overloading

In some programming languages, Function overloading means creating two (or more) functions with the same name. Each function does something different depending on the arguments given to it.

Example from Wikipedia on calculating Volume in C++:

// Volume of a Cube.
int Volume(int s) {
  return s * s * s;
}

// Volume of a Cuboid.
long Volume(long l, int b, int h) {
  return l * b * h;
}
Enter fullscreen mode Exit fullscreen mode

Function Overloading in JavaScript is slightly different because we cannot produce two different functions with the same name.

We use one function, but we change the results according to the arguments we receive.

The above example could be rewritten as follows in JavaScript:

function volumeCuboid (length, breadth, height) {
  return length * breadth * height
}

function volumeCube (length) {
  return volumeCuboid(length, length, length)
}


// Overloading happens here
function calculateVolume (...args) {
  if (args.length === 3) return volumeCuboid(...args)
  return volumeCube(args[0])
}
Enter fullscreen mode Exit fullscreen mode

We don't need to depend on the number of arguments. We can also change the result depending on the value of each argument.

Example:

We can have a createShape function that returns different objects depending on value of shape. (The Factory Pattern uses this type of Polymorphism).

function createShape (size, shape) {
  if (shape === 'triangle') return new Triangle(/* ... */)
  if (shape === 'rectangle') return new Rectangle(/* ... */)
  if (shape === 'square') return new Square(/* ... */)
}
Enter fullscreen mode Exit fullscreen mode

(Fun Fact: I understood this version of Polymorphism from Martin Fowler's Refactoring: Improving the Design of Existing Code. It made me a little more curious about Polymorphism, which eventually led to this article you're reading!)

If we distill this theory down further, all if and switch statements result in Function Overloading.

function createEmoji (emotion) {
  if (emotion === 'happy') return '😃'
  if (emotion === 'sad') return '😞'
  return 😑
}
Enter fullscreen mode Exit fullscreen mode

Coercion Polymorphism

JavaScript has Type coercion. It converts value from one type to another while evaluating them.

For example, you can any expression inside an if statement. JavaScript converts expression into true or false. If the expression converts to true, the expression is said to be truthy. If the expression converts to false, the expression is said to be falsey.

const string = 'hello'
if (string) {
  console.log(string)
}
Enter fullscreen mode Exit fullscreen mode

Another example: You can compare strings and numbers with == (although this is generally not recommended).

22 == '22' // true
Enter fullscreen mode Exit fullscreen mode

Since type coercion happens on the spot, it is a form of adhoc polymorphism.

Variable overloading?

I'm not sure about this one.

Wikipedia defines Polymorphism as this:

Polymorphism is the provision of single interface to entities of different types, or the use of a single symbol to represent different types

"Use a single symbol to represent different types" feels like Variable overloading to me. (Variable Overloading is not an actual term. It's something I came up with).

We already overload variables in JavaScript since each variable can represent any value.

// Variables in JavaScript can represent any value
const str = 'string'
const num = 123
const bool = true
const array = []
const obj = {}
const nah = null
Enter fullscreen mode Exit fullscreen mode

Parametric Polymorphism

Parametric Polymorphism is polymorphism related to parameters... But that's not very useful so let's describe what it's about.

Parametric Polymorphism has two parts:

  1. Data that can contain many types of data
  2. Functions that can work with many types of data

Data that can contain many types of data

Everything in JavaScript is an Object. So Objects are parametric. It can be converted into other types of data.

Objects can also store multiple types. It doesn't care what values are stored.

const object = {
  str: 'hello',
  num: 123,
  bool: true
}
Enter fullscreen mode Exit fullscreen mode

Arrays are also parametric. It lets you store many types of data and it doesn't care what they are.

const array = ['hello', 123, true]
Enter fullscreen mode Exit fullscreen mode

Functions that can work with many types of data

Functions that can work with many types of data are called polymorphic functions. They don't care what comes in. They'll apply the transformation they're told to do, and they spit out a result.

map is a good example. It takes in an array and spits out another array. It doesn't care what goes in-between.

const doubled = [1, 2, 3].map(num => num * 2)
Enter fullscreen mode Exit fullscreen mode

You can use map to convert numbers into strings.

const toString = [1, 2, 3].map(num => `${num}`)
Enter fullscreen mode Exit fullscreen mode

Object.assign is another example. It takes in an object and spits out another object, but it doesn't care what goes inside each object.

Object.assign({}, { property: 'value'})
Enter fullscreen mode Exit fullscreen mode

Subtype Polymorphism

Subtype Polymorphism involves creating derivative objects from a parent object. It's can be called Inclusion Polymorphism, Subclassing, or Inheritance. (Inheritance is such a loaded word. I'll explain it another day).

Derivatives objects can then override a method from the parent and it'll still work.

Example:

Let's say you have a Human class with a sayHi method:

class Human {
  constructor(name) {
    this.name = name
  }

  sayHi() {
    console.log(`Hi! My name is ${name}`)
  }
}
Enter fullscreen mode Exit fullscreen mode

Then you create a Developer and Designer subclass from Human.

class Developer extends Human {/* ... */}
class Designer extends Human {/* ... */}
Enter fullscreen mode Exit fullscreen mode

We Designer and Developer to talk more about themselves, so we can override the sayHi method.

class Developer extends Human () {
  sayHi() {
    console.log(`Hi! My name is ${name}. I am a developer.`)
  }
}

class Designer extends Human () {
  sayHi() {
    console.log(`Hi! My name is ${name}. I am a designer.`)
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you have three different classes. Each one of them can sayHi. You can use sayHi normally and they'll all work, but they produce different results.

const zell = new Human('Zell')
const vincy = new Developer('Vincy')
const tim = new Designer('Tim')

zell.sayHi() // Hi! My name is Zell.
vincy.sayHi() // Hi! My name is Vincy. I am a developer.
tim.sayHi() // Hi! My name is Tim. I am a designer.
Enter fullscreen mode Exit fullscreen mode

That's it!

Wrapping up

There are the three kinds of Polymorphism.

  1. Adhoc Polymorphism
  2. Parametric Polymorphism
  3. Subtype Polymorphism

Chances are, you're already using Polymorphism without knowing it 😉. I hope this clears up Polymorphism for you!


Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .