Does JavaScript really have polymorphism?

The answer is yes, but the reason isn’t what you expect.

Photo by Tumisu on Pixabay.com

Wikipedia defines Polymorphism as:

The provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types

Don’t worry! I’ll give you a plain English translation of that definition — but first, I want to tell you a story.

These past few months, I’ve been working on a project called BuddyViewer, my personal solution to the problem of watching YouTube videos simultaneously with friends remotely. My friends and I love watching YouTube, but we live very far apart right now, and we needed an alternative to the existing websites that allow us to watch videos together.

So, I had my friends create a short list of things they felt were missing from the existing websites — and sure enough, the ability to watch videos from sites outside of YouTube came up. That problem in particular has no current elegant solutions. What if you want to go from watching Minecraft on YouTube to watching anime on crunchyroll?

This presented me with an interesting problem: I needed to write code that would allow a user to interact with a video, but I had no way of knowing the specific type of video beforehand. This meant I had no way of knowing the particular code implementation each video would need for users to be able to interact with it.

For example, YouTube has an iFrame API, which allows you to control the official YouTube video player directly in JS, but requires you to embed an iFrame rather than an HTML5 Video Tag. The code require to interact with a youtube player embedded in an iFrame is unique, and has nothing to do with the code necessary to run an HTML5 video. Similarly, Vimeo and Spotify’s players have their own unique implementations, each with their own API’s that have frustratingly slight differences in wording.

To play a video with the YouTube API, you have to call player.play(). But to do the same in Vimeo, you call player.playVideo(). Worse, Vimeo’s player returns a promise with each function call— so any code you want to run immediately after doing a function like player.seek(time) or player.pauseVideo() must be put in a .then(), which also makes the implementation details finicky and particular.

As far as the user is concerned, though, they just want to press the play button and make the video play. When they click on the video progress bar, they expect the video to seek to the time corresponding to where they clicked on the bar.

Everything else is my problem as the developer.

So I went about building classes for each of the different video player types. To start with, I only wanted to cover YouTube, Vimeo and… Other. “Other” referred to any .mp4 video from any source, because that could easily be wrapped in a <video> tag.

With my three classes created, all I had to do was write a bunch of if-then statements in my code that would detect what type of player the user was watching and call the appropriate functions when the buttons were pressed, right? If it’s Vimeo, call player.playVideo(). If it’s YouTube or Other, call player.play(). Right?

//Create a player based on the url the user entered, then play it:

player = new Player(urlEnteredByUser);

if(player.type == “Vimeo”) player.playVideo();

else player.play()

Well… no.

Code such as that is not only extremely slow to produce, but tough to read and worse to maintain. Any particular changes I want to make to how I handle, say, playing a video now must be duplicated in both the if-statement where it is a Vimeo video and the else-statement where it isn’t. This will be the same for any features that are unique to any players. Not ideal.

The solution? Polymorphism, of course!

What is Polymorphism

Photo by Matese Fields on Unsplash

Remember that Wikipedia quote? Let’s look at it again. According to Wikipedia, Polymorphism is:

The provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types

The plain English version of that statement would be, “Polymorphism is the concept of using one interface to manipulate objects with many different implementations.”

Essentially, if we turn back to the problem I’ve described with video players, polymorphism is the perfect solution.

The initial situation is, our user wants to interact with all videos in one straightforward way. Press the play button to play the video, press the mute button to mute it, and so on. The means by which the user interacts with the player is the interface. They’re using the user interface, which are the buttons. The buttons are calling code that uses a class interface, which are the functions an object of the class has for manipulating the values the object contains.

But different video players need different code to make them do what the user wants, and yield different results — this is called the implementation, and the user doesn’t care about it at all. The user wants the video to play when they press play. They don’t care how that works. Just do it. They want their interface — the UI, which uses video player class interfaces — to do what they say, regardless of the implementation.

Think of it like when you were a child, and an adult might have told you, “Get your homework done! I don’t care how you do it, but get it done!”

Polymorphism is the concept of creating an interface that allows you to ignore implementation details. Basically, you can set up your class so that player.play() will always play the video, whether it’s Vimeo, YouTube or Other, because player.play() figures out which type of player we’re dealing with, and what type of code needs to be run, without any other parts of the code needing to know.

player = new Player(urlEnteredByUser);

player.play();

This is only possible through inheritance. If Class A is the super class, and classes B and C are the subclasses, then we can perform polymorphism by having B and C inherit methods from class A, then implementing those methods differently. Some pseudo-code:

Class A{

getName(){ return ‘A’}

}

Class B extends A{

getName(){return ‘B’}

}

In the above code, if you wrote

const TestObject = new B();

and then wrote

TestObject.getName();

the return value would be ‘B’. But if you then wrote

TestObject = new A();

you will now get ‘A’ if you call

TestObject.getName();

This is basic polymorphism. No if-statements and no switches.

I created a BuddyPlayer class, and then created the YouTubePlayer and VimeoPlayer classes, which extended BuddyPlayer. Then I defined all the most basic functions in the BuddyPlayer class, and implemented the specific details for each player in their own classes. Then I write buddyPlayer = new BuddyPlayer(url) in the default case, and when a youtube video is added, buddyPlayer = new YouTubePlayer(). As a result, when the user presses the play button, all it does is call buddyPlayer.play(), and it doesn’t care what exactly that means.

So, that settles it then, right? JS has polymorphism! We demonstrated it!

Well… there’s a bit more going on under the hood than that.

Clear polymorphism

Photo by Markus Spiske on Unsplash

When I first started learning programming, I began with C++. This was a bad choice for so many reasons, but a really good choice for just as many reasons. C++ is a high-level language by Computer Science standards, but by modern software developer standards, it’s extremely low-level.

Think of C++ as the grouchy grandpa of JavaScript. JavaScript says you can put anything you want in an array, and it can be as long as you want.

array = [1, ‘2’, {three: 3}]

array.push(‘four’);

C++ says an array’s length is determined when it is initialized. No bigger and no smaller. But also, it can only hold one type of data.

int array[3] = {1, 2, 3};

array[3] = 4; //index reference error. array has a max length of 3.

arr[0] = ‘string’; //another error

What you get in exchange for all the pickiness of C++ is not only faster performance, but clearer code. This is because C++ trades JS’s flexibility for specificity. You must be specific in what you want your data to do, and how you want your data to be used.

In C++, most things are not objects; in fact, a value is only an object, in most cases, if you declare it as one. This has many disadvantages for productivity and flexibility, as you can imagine (Definitely no obj.toString() happening in C++), but two of its advantages are that you can be clear and specific in what you are telling your compiler, and that you can trust your compiler to be doing what you meant as well as what you said.

I mean, we can’t ignore the fact that in JS, even with all the work I did to define BuddyPlayer, YouTubePlayer and VimeoPlayer as classes, I could easily decide one day to write

buddyPlayer = new YouTubePlayer(urlEnteredByUser);

buddyPlayer.play(); //YouTubePlayer’s play() is called.

buddyPlayer = {play: ()=>console.log(“I’m pretending to play a video.”)};

buddyPlayer.play(); // I’m pretending to play a video.

JS doesn’t actually care, because I can’t force buddyPlayer to be an object only of type BuddyPlayer and its subclasses. As far as JS is concerned, an object is an object. This example might seem contrived, but it actually applies to any methods we might want to call on the buddyPlayer object. If I accidentally assign an unrelated object with a getUrl() method on it to buddyPlayer, when I call buddyPlayer.getUrl(), I’ll get results I don’t expect and that are extremely difficult to debug.

But in C++ and other languages like it, the fact that you must declare a type for your data, and most things are not objects by default, means you can actually implement specific polymorphism for your classes. Initializing a buddyPlayer object would look something like this:

BuddyPlayer * buddyPlayer;

buddyPlayer = new YouTubePlayer(urlEnteredByUser);

buddyPlayer->play(); // calls play() from YouTubePlayer

buddyPlayer = new VimeoPlayer(urlEnteredByUser);

buddyPlayer->play(); // VimeoPlayer’s play.

Because we can define the buddyPlayer object as being specifically of type BuddyPlayer, we’re safe from any unexpected behavior. We implement polymorphism not as a mere concept, but also as a strategy or technique.

I would argue that in most cases, when the term polymorphism is used, it’s referring to the technique rather than the pure concept. This is because people want to use polymorphism to expand the specificity of their object-oriented code. They want a little of what JS has, but not so much that they lose control of what is or isn’t acceptable data to be handled by their code.

Wait, so if that’s Polymorphism, then JS doesn’t have it, right?

Photo by Tachina Lee on Unsplash

Well, actually, the answer is that JS does have polymorphism — but strictly as a concept.

There is no way to specifically apply polymorphism to parts of your code in JS, because it’s happening all the time. Because so many datatypes in JS are objects by default, and because all JS objects inherit from the global Object, you are free to, at any point, define an object literal and give it any methods you please, which might coincidentally have the same name as a method you’re expecting to find on a completely different object.

If that case occurs, your code will call the method and move on, and your only way of knowing anything went wrong is by debugging or using console.log(). The compiler will not stop you, and the app may not even crash at runtime.

That right there is polymorphism, and there’s no way to stop it or turn it off. Other than closures and other means of managing scope, there’s no thorough way to tell JS you only want certain classes of objects to be acceptable at certain times, which means you cannot stop any object from being anywhere it happens to be sent, accidental or not.

While this adds a great deal of power to JS, it also creates an obvious lack of security. It also means you can never truly experience the polymorphism dream in JS. But in most cases, the tradeoff is well worth it. Whether it’s worth it in your use case or not is up to you.

In my situation, I would have really loved the specificity of C++. But hey, BuddyViewer works anyway!*

*There’s actually one major bug I need to fix, but shh.

Get in touch with me on twitter at https://twitter.com/shaquilhansford

Check out buddyviewer to watch youtube videos with your friends!

https://buddyviewer.com

I'm a full stack web developer currently available for contract, freelance or full-time job opportunities. Follow me at https://twitter.com/shaquilhansford

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store