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;
}
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';
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';
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
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)
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_...
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
Android's billion-dollar mistake(s)
Jean-Michel Fayard π«π·π©πͺπ¬π§πͺπΈπ¨π΄ γ» Sep 25 '19 γ» 10 min read
Thanks for input JMF, the article has been changed to highlight your points.
The last time I used C language (6 years ago), it doesnt have OOP functionality but C++ is.
Nope, JS is not limited to OOP. I can do functional programming since functions are first class objects. Meaning they can be
Furthermore RxJs implements Observables, which allows reactive programming.
Thanks Sasha, I've updated the article to reflect your input.