DEV Community

loading...

Why JavaScript is an OOP Language (Even Though It Doesn't Have Classes)

marinbenc profile image marinbenc🐧 ・1 min read

You probably know how OOP works. The class Dog inherits from class Animal, which means that if you have a Dog you access methods from Animal. This is actually just a subset of OOP. You can be object oriented without classes, and that's what JavaScript does.

A class is a blueprint. It contains information about what every instance of that class has. It tells you which methods and properties are there and how you can use this class of things.

The class itself doesn't actually contain any data. That's the job of an object. A class is the blueprint for a house, but an object is the actual house, with wood, tiles, bricks and all the weight of an actual house. An instance contains the actual data that is individual only to that instance. You might have used the same blueprint for your house as your neighbor, but that doesn't mean you can sleep in their bed.

The difference between instances and classes is fundamental in the way most people understand OOP. But it's not necessary for OOP. There's a way to do OOP without classes. After all, it's object oriented programming: objects are the star of the show, not classes.

The style of OOP where there is no difference between classes and objects is called prototype-based programming (to make our lives easier, we'll call it PBP).

In PBP, every object can be individual. It contains both its methods and data at the same time. Usually, you can add new properties and methods to an object whenever you feel like it, even while the program is running.

If this sounds like something only a few programmers use for their edge case problems, you'd be surprised. One of the most popular languages in the world is a PBP language: JavaScript.

In JavaScript, there are no classes in the class-based OOP sense of the word. JavaScript works with objects. If you want to encapsulate a few functions and properties together, you would create an object containing functions and properties, and not a class.

const animal = {
    numberOfLegs: 4,
    sleep: () => print("Zzz")
}

This is an object that has a property and a function that does some work. This is different from a class because the function itself is a piece of data that the object has. It's as mutable as the state of a property.

animal.sleep = null

Now, suddenly, animal doesn't have a function anymore. JavaScript doesn't have blueprints, it only has houses.

What About Inheritance, Though?

A fundamental property of a class is that it can inherit methods and properties from other classes. Both a House and Apartment can inherit from Residence to make sure we don't have to duplicate the same code in both classes.

But again, classes are not necessary for inheritance. In PBP inheritance is done completely by using objects.

I mentioned earlier that in PBP an object contains all of its methods and properties, as well as their actual state. So the only way to inherit all of those is by copying (or referencing) all those methods and properties. This is exactly what PBP languages do, and it's called prototypal inheritance.

If we wanted to make a dog object which would have access to the same methods as animal, we can simply make dog contain animal, since the methods are inside of animal.

const dog = {
    prototype: animal,
    bark: () => print("Woof!")
}

If we want to make the dog eat food, we can to this:

dog.prototype.eatFood(10)

Thankfully, JavaScript calls functions on the prototype automatically. If a function doesn't exist on that object, it will search the prototype for the function. The prototype can itself contain another prototype, so JS will search all the way up until it finds the function it's looking for.

The reason why they're called prototype based languages is because a prototype is, as opposed to a class, concrete. A prototype is a working thing, and not a blueprint. You might not want to sell the prototype to millions of customers, but it's a real thing that works. You then use that prototype to construct a bunch of copies that you will actually use. Just like you would in a factory.

The animal object is a prototype of an animal. It's an object like any other, but it will be used to create new, concrete animals, like a dog.

Is This Better Than Classes?

PBP is more straightforward than class-based OOP. It has fewer moving parts and is completely transparent. You can see how it works. Class-based OOP is a layer of abstraction on top of this. This means PBP has a lot of advantages, but also disadvantages.

The main advantage of PBP lies in its flexibility. Blueprints are something that has to be made ahead of time, and they have to be correct. If you're building a house, you will have a lot of trouble if you realize half-way trough building your roof that you forgot to add a window in the blueprint. Classes are similar: you create them before creating and using the objects. You have to know which methods and properties you'll need before you start using it. No matter how good you are at programming, you won't be able to predict everything.

If you don't have to make a class ahead of time, you can immediately start creating your objects. You can adapt them as you're using them without having to pay a large cost. This is very beneficial in programming, where requirements change all the time. You want the ability to change quickly and easily.

But quick and easy change comes with a big risk: correctness. Blueprints exist to plan out a house before it's built, so mistakes are caught early and workers don't get lost while building. If you try to build a house by just doing it and seeing where it leads you, you will probably end up with the house collapsing on top of you. The same goes for programming: you need to make sure your codebase is sound and correct. If everyone can just go in and start changing everything, it will quickly collapse on top of itself.

Like everything in programming, PBP and class-based OOP fall in a spectrum of tradeoffs. On one hand, PBP is flexible and easy to work with, leading to quicker development. On the other hand, class-based OOP is more rigid and sturdy, leading to fewer bugs. Different problems require different tools, and with the knowledge of PBP, you are now better equipped to solve problems that require it. Happy coding!

Note: This is a repost from programmingwords.com. It's a blog that showcases two new terms from computer science every month. Check it out if you're interested in compsci, compilers or programming languages! :)

References:

Prototype based programming

https://en.wikipedia.org/wiki/Prototype-based_programming

Inheritance and the prototype chain - JavaScript

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Discussion

pic
Editor guide
Collapse
eljayadobe profile image
Eljay-Adobe

I think the defining characteristics of object-oriented programming are: encapsulation, polymorphism, and inheritance.

JavaScript does not use classes. It has prototype based inheritance, rather than class based inheritance. (Yes, I know, ES6 has classes. But that's just syntactic sugar for JavaScript's prototype based inheritance. It isn't the class based inheritance as in C++, C#, or Java.)

That does not make JavaScript any less an OOP language. Does make it different (PBP, as describe). Some relationships are easier to express in JavaScript. Some are harder to express.

Regardless of a developers OOP language of choice, the onus is still upon the developer to use the language well to achieve encapsulation (or modularity), to appropriately use polymorphism, and to judiciously apply inheritance (class-based or prototype-based, depending on language).

In my experience, encapsulation is sometimes neglected causing high-coupling of parts, with very poor cohesion or poor modularity -- which makes unit testing arduous if not nigh impossible. Inheritance is often used where instead composition would be a far better choice. Inheritance is sometimes abused to achieve reuse of the parent (or prototype) functionality rather than used as a polymorphism facility by the caller of methods of a family of objects who provide the same interface (the same contract of behavior).

None of those kinds of issues is the fault of the programming language (JS, C++, C#, Java, or whatever)

DISCLOSURE: I programmed in TypeScript for 2 years. I've got some mileage under my belt in the JavaScript space.

Collapse
tofl profile image
Tom F.

Javascript (since ES6 I think) also has classes-based OOP. For example :

class Polygon {
  constructor() {
    this.name = "Polygon";
  }
}

var poly1 = new Polygon();

console.log(poly1.name);

I'm guessing that, behind the scenes, it's still PBP.

Collapse
andychiare profile image
Andrea Chiarelli

JavaScript classes are syntactic sugar for prototype-based OOP. See dev.to/andychiare/a-matter-of-class

Collapse
johannesvollmer profile image
Johannes Vollmer

That's why I think it was a horrible idea to introduce a 'class' keyword in ES6 - what a misleading "design" decision

Collapse
martinhaeusler profile image
Martin HĂ€usler

JS is object-oriented, yes. But it isn't class-oriented. The fact that there is a prototype chain is usually not very relevant in practice, but when it does make a difference, it's going to bite you in places you never realized that problems could emerge there. In particular modern web frameworks (React, Redux...) have shown how OOP is used in JavaScript, which is: not at all. 99% of the time you work with plain objects (as in: prototype is object, no methods, only public fields). If you receive a JSON string from an Ajax call, what do you do? Json.parse(...) it. Do you perform any DTO transformation? Or even set a prototype? Nah, too much of a hassle, "let's just use the data". To me this is a strong indication that OOP in JavaScript has way too many hassles and quirks.

With ES6, there was the opportunity of a lifetime: the introduction of a "class" keyword. But instead of starting from a clean table and performing a breaking change, classes are being faked while prototype chains are still merrily working in the background, except that there is now even more confusion among developers than before. Yikes.

Collapse
shubhamsinha profile image
Shubham Sinha

I feel the title of this article should be Javascript for OOP ... JS isn't entirely an OOP or Functional. So it shouldn't be taught as one.

Collapse
marinbenc profile image
marinbenc🐧 Author

I agree, but the same is true for any language. You can write Java using only static functions and immutable objects, and you can write Haskell functions that perform side effects. I admit the title of the article is a little bit clickbaity, but the point of the article is just to shine a light on prototypal inheritance and language classification. :)

Collapse
scott_yeatts profile image
Scott Yeatts

Yes, I think my biggest issue with this is the clickbait... It got me to read it because I initially thought "JavaScript's main draw is that it is a 'any paradigm' oriented language... let's see why marinbenc thinks it's an oop language", but I was let down because it's really just an analysis of how to use JS in a OO manner.

Many, MANY people make the mistake of calling it an OO language because its base element is an Object, which is understandably confusing. This is a great breakdown of how prototypal inheritance works and how it can be used in place of the traditional Class syntax from other languages/TypeScript/JS syntactic sugar. It would have been nice to see the other two pillars of OO addressed like Eljay-Adobe mentioned.

You could talk about things like using the module pattern for encapsulation(Revealing module specifically is very encapsulated, where you export/return only the public methods to a caller) and directly point to poolymorphism when talking about the animal/dog inheritance chain (The object IS A dog which IS An animal... polymorphic objects have 2 or more 'IS A' relationships. This is in opposition to composed objects which are 'HAS A' relationships like you might see in Functional programming. It's good to mention both paradigms.... because technically you can use both (and procedural too!) in one application in JavaScript, which is why so many people use the language for things other than web-dev.

Functional Programming in Java would be awkward and clumsy, probably with one super class that everything 'is' and just... bad code. Not as familiar with Haskell, but you would probably have to do similar language hacks to get OOP working in it.

In JavaScript, both just work, but the writer needs to understand what they are doing to keep it from becoming a mess if they use booth approaches equally in one application (and maybe... since engineering is a team sport... don't ever do this to your team-members haha), so it's good to contrast the two any time you're talking about how to do one or the other.

DISCLOSURE: Old guy. (Not making fun of Eljay... I just wanted to make an old guy joke at myself :D)

Collapse
jcarlosweb profile image