Facts.
Fact: JavaScript is an object oriented language.
Fact: JavaScript does not have classes.
Fact: JavaScript is dynamic and expressive enough to create structures that emulate classes.
Beyond those three facts, everything else here will be theory or opinion.
Expansion of facts:
JavaScript is an object oriented language but it does not have classes. Some people see these two statements as conflicting, but object oriented does not necessarily equal classes. Classes are only one paradigm used in object oriented systems, one of the more common ones, yes, but not the only one. Object oriented merely means that you have objects which contain both data and behavior – or properties and methods if you like.
You can create structures in JavaScript that emulate classes. Yes, but should you? That’s today’s topic.
First of all, why do people try to create class-like structures in JavaScript? I think mainly because they are coming from some other class-based language and that’s what they are used to. It makes them feel at home and transforms JS into something they think they can understand better. But there are many drawbacks to such an approach. Read a couple of in-depth articles or books on the subject. It starts out simple. You create a constructor function and add methods to the prototype. No real problem there. But then you want to implement inheritance and things begin to get tricky. Every fix has various drawbacks which have more fixes. The code ends up being quite complex, especially when you think that the main benefit is to make the programmer more comfortable with the language by making it seem like another language. Complexity has various potential side effects, including code brittleness, learning curve, file size, performance, and memory use, to name a few.
But beyond the idea of programmer comfort, there are some valid problems that people are trying to solve using classes. In most languages, classes offer 3 main benefits:
Code re-use via inheritance.
Information hiding / encapsulation.
Data typing and polymorphism.
These are all very fine things to strive for in programming anything. Let’s look at how they apply.
1. Code re-use.
This means you’re not typing the same code more than once. The DRY principle. Say you have a sprite system where each sprite has an image it draws to a canvas in a draw method:
[php lang=“js”]
function render() {
this.canvas.draw( this.image, this.x, this.y );
}
[/php]
And you have a player object that has certain behavior and an enemy object that has certain behavior, but both need to render to the canvas. It would be a poor decision for each of them to have a duplicate render function. You would somehow want to define that function once and have both of them share it. Then, if you ever needed to change it, you’d only need to change it once, they’d never get out of sync, you wouldn’t find errors creep into one, but not another, etc. And futures sprite types could also share the same render method.
In a class based system, you’d have a base class where any common code would live. Say a sprite class, with the render method. Then both player and enemy could inherit from sprite and both get the single render method. This is what everyone is taught in week one of most computer courses that venture into OOP. But remember, the problem we are trying to solve is code re-use. Class-based inheritance is not the only way, or even necessarily the best way to reuse code. In fact, another key principle often espoused in software design is “favor composition over inheritance”.
2. Information hiding and encapsulation.
This is all about setting up a public interface or api for an object that other objects can use, and having an internal implementation that other objects cannot (or should not) use. This means that you should be able to change the internal implementation without breaking any code that is using the public facing api. In class base languages, this is usually done with access modifiers such as public, private, protected, etc. Some languages have additional modifiers such as internal, friend, etc. and the actual rights that these modifiers bestow or block can vary slightly from language to language.
JavaScript does not have any native access modifiers, but there are ways of using closures to create functions and properties that are only accessible by methods of an object and not by outside code, thus accomplishing information and encapsulation. Interestingly, though, many pseudo-class systems in JavaScript do not even attempt to implement privacy, whereas other non-class based systems, such as modules, do this as a matter of course.
3. Data typing and polymorphism.
This says that if I create a class called Dog in a strictly typed system, I can make a variable that only accepts instances of the Dog class. And I can make a function that only accepts an argument of the Dog class. Furthermore, I can make a class called Animal and classes Dog and Cat that extend Animal. Now I can have a variable or method parameter that states it only accepts Animal objects, to which I can pass either Dogs or Cats.
Since JavaScript is not a strictly typed language, this concept is almost completely useless. I cannot specify what type my variables should be, nor can I state what kind of object a method parameter can accept. Yes, I could code all my methods with checks to typeof or instanceof and throw runtime errors if the wrong type was passed, but … nah.
Summary.
So, what it comes down to in most cases of people creating these complex class emulation systems, is code reuse. A valid concern, but not one that I am convinced has pseudo-class-based inheritance as its best answer. In future posts, I’ll cover some of the different ways to accomplish code re-use as well as encapsulation in JavaScript.