DEV Community

Cover image for Is JS an OOP Language?
John Peters
John Peters

Posted on • Updated on

Is JS an OOP Language?

JS Supports both OOP and Functional Programming Styles

Summary

This article does not extoll OOP to be better than Functional programming styles. It does; however, show how quickly OOP can go wrong if a single simple rule isn't followed when talking about inheritance. It also shows how OOP and Functional Styles can co-exist.

It then discusses Composition; which, allows for polymorphic traits using properties.

Finally, it discusses the Single Responsibility Principle, where OOP and Functional styles meet. At this layer there's no difference in the two styles because good OOP winds up at Functional styles anyway.

JS OOP Syntax is the exact1 same as C#,C++ and Java

Since ES6, Javascript OOP syntax is the exact same syntax as C++, Java and C#. I know this may surprise some JavaScript folks, especially those with more than 4 years experience.

1Javascript has no types, so the OOP syntax is the exact same pattern (minus types). For Typescript, it is the same syntax.

The JavaScript pattern for inheritance is

  class Parent extends BaseClass

The C# pattern is

  class Parent:BaseClass

A JavaScript Class

class Person{
  lastName;
  firstName;
}
// typescript equivalent with type checking
class Person{
  lastName:string;
  firstName:string;
}
Enter fullscreen mode Exit fullscreen mode

Note: The exact same Behavior as above, is possible using only functions in JavaScript. In fact, after compilation of the class, it winds up as a function. The 2nd article in this series will cover that.

Both class examples of Person have a lastName and firstName property. We use them like this:

let thom = new Person();
thom.firstName = 'Thomas';
thom.lastName = 'Edison';
Enter fullscreen mode Exit fullscreen mode

New Requirement
We need an employee class, employees will have an ID only. Employees are Persons which means we can use inheritance.

A JavaScript Class Inheriting a Base Class

// works only because employee
// strictly (is-a) a person
class Employee extends Person{
 empId;
}
...

let emp = new Employee();
emp.empId = 1;
emp.firstName = 'Joseph';
emp.lastName = 'Edison';
Enter fullscreen mode Exit fullscreen mode

Hey wait a minute where did emp get the first and last name properties? Answer: From extending Person.

This concept is known as sub-classing. Here's a huge secret to being successful with sub-classing or what we call 'classical inheritance'

Do not ever sub-class or try to sub-class something that does not have a strict 'is-a' relationship. For example; a car has tires but a car 'is-not' a tire, or is a tire a car! The Tire class should never be a sub-class of Car.

This is a cardinal rule which cannot be broken and is probably the root of many OOP implementation fails.

Sub-Classing

Proper sub-classing means that the sub class 'is-a' something of the parent class. An employee 'is-a' person. So it's ok for the Employee class to extend the Person class. Sub classing is always vertical in nature when seen on an object graph.

Composition
Unlike sub-classing, there is another way to inherent intrinsic javascript types as well as our own complex types. Composition is the 'has-a' relationship. A car has tires, or a car has an engine. Properties or Parameters accomplish Composition.

// with no constructor, 
// this is a model
class Car{
 // properties are compositional
 // they are has-a relationships
 tires;
 engine;
}
// with a constructor taking parms.
class Car{
 constructor(tires,engine){

   // this.engine and this.tires
   // are implicitly defined in ES6

   this.engine = engine;
   this.tires = tires;

   // in traditional OOP 
   // this is the same syntax
   // to implement data-hiding
 }
}

// compose the car like this
let ford = new Car("GoodYear MX100", "EcoBoost 8");
// or compose like this:
let ford = new Car();
ford.tires = "GoodYear MX100";
ford.engine = "EcoBoost 8";

// It's safe to say that class or function
// properties are compositional

Enter fullscreen mode Exit fullscreen mode

Cars have tires, Cars have engines. Cars are composed of properties. Any property of a class or function is composition. It has-those properties.

We now understand 'classical inheritance' as being vertical in nature, where sub-classes extend the properties and functions of the parent.

Composition shows the object graph of has-a relationships 'horizontally' We could call this 'horizontal inheritance'. Objects may contain (as in a property) or be allowed to accept a parameter to be contained or used.

Next time you hear "Favor composition over inheritance" it simply means to prefer 'has-a' implementation. Properties and Parameters with simple or complex types achieve this.

What it doesn't mean is that sub-classing is in any way something to avoid. When a sub-class truly is-a part of it's parent class it works just fine.

Single Responsibility

For both Composition and Inheritance the single-responsibility principle must be strictly followed. Every class or function we write should do only one thing. For example, a tire class or tire function should not implement a fillTire action. Tires don't fill tires, the fillTire function does. fillTire takes in a tire as a compositional part (property) to act on.

This is slightly different than C#, Java or C++ where a fillTire interface may be in the tire class. This is due to the fact that functions (methods) must exist within the class.

The reason we don't do this in JS is because functions are first class citizens. They are equal to a class in the javascript hierarchy. Functions do not need to be contained in a class.

In Javascript, the fillTire function would be contained in the Tire.js module.

OOP and Functional Styles Meet

This is where functional programming and OOP meet, the Single Responsibility Principle (SRP).

But, but, but
The class is just syntactic sugar for the run time function that's produced. True, but who cares how the run time compiler does it? Dismissing OOP concepts because "We are functional programmers only and OOP was never needed" is a bit naΓ―ve and old-school.

Don't get me wrong, the function is larger in life than the C#, Java or C++ method syntax, due to it's true first-class citizenship. If done right the function is the most powerful construct in JavaScript.

Using Class constructs is a workable solution when done properly. Sub Classing is fine but only with true 'is-a' relationships. Composition is for 'has-a' relationships and Functions are for single responsibility actions as a primary focus. Functions can contain multiple functions but only after we've created our SRP functions first.

OOP architecture in C++ is 11 years older than the invention of JavaScript. A lot has been learned in 25 years. It's all applicable to JavaScript but only if the concepts above are fully understood.

Top comments (5)

Collapse
 
jmfayard profile image
Jean-Michel πŸ•΅πŸ»β€β™‚οΈ Fayard • Edited

OOP is pattern, not a type of language, meaning you can write object-oriented programming in C, it will work, it will just be verbose and ugly.

Hello GObject !

It follows that you can also do OOP in JavaScript. Whether you should do OOP in JavaScript is an open-question. Functional Programming seems more natural to me in JavaScript, although one doesn't exclude the other.

Your explication of composition and composition over inheritance is very confused IMHO.

Neither composition nor inheritance has anything to do with passing parameters to the constructor. This is just the right thing to do in general because it's better to construct your object at once than to create an half-valid object and relying on mutability and on the programmer's discipline.

"Favor composition over inheritance" (.-.) it doesn't mean is that sub-classing is in any way something to avoid. When a sub-class truly is-a part of it's parent class it works just as well as composition.

This is vague. Saying that there are two approaches and neither is valid in 100% of the cases is not very helpful guidance. What "Favor Composition over Inheritance" does it to take stand, and affirm that you should prefer in general an HAS-A relationship. Valid IS-A relationships are the minority.

Wikipedia explains the benefits here en.wikipedia.org/wiki/Composition_...

To favor composition over inheritance is a design principle that gives the design higher flexibility. It is more natural to build business-domain classes out of various components than trying to find commonality between them and creating a family tree. For example, an accelerator pedal and a steering wheel share very few common traits, yet both are vital components in a car. What they can do and how they can be used to benefit the car is easily defined. Composition also provides a more stable business domain in the long term as it is less prone to the quirks of the family members. In other words, it is better to compose what an object can do (HAS-A) than extend what it is (IS-A).[1]
Initial design is simplified by identifying system object behaviors in separate interfaces instead of creating a hierarchical relationship to distribute behaviors among business-domain classes via inheritance. This approach more easily accommodates future requirements changes that would otherwise require a complete restructuring of business-domain classes in the inheritance model. Additionally, it avoids problems often associated with relatively minor changes to an inheritance-based model that includes several generations of classes.

For a good example of what goes wrong if you prefer Inheritance over Composition, have a look at my article on the Android SDK :P

Collapse
 
jwp profile image
John Peters • Edited

Thanks for input JMF, the article has been changed to highlight your points.

Collapse
 
dendihandian profile image
Dendi Handian

The last time I used C language (6 years ago), it doesnt have OOP functionality but C++ is.

Collapse
 
spic profile image
Sascha Picard • Edited

Nope, JS is not limited to OOP. I can do functional programming since functions are first class objects. Meaning they can be

  • stored in a variable, object, or array,
  • passed as an argument to a function and
  • returned from a function

Furthermore RxJs implements Observables, which allows reactive programming.

Collapse
 
jwp profile image
John Peters • Edited

Thanks Sasha, I've updated the article to reflect your input.