JavaScript is a bit confusing for developers experienced in class-based languages like Java or C++. One of the characteristics of a language is its support for particular programming paradigms. JavaScript programs tend to make heavy use of passing functions around so they are called functional languages despite having variables and many imperative constructs.
const paradigms = [
{name: 'Imperative', support: true, parent: null},
{name: 'Declarative', support: true, parent: null},
{name: 'Functional', support: true, parent: 'Declarative'},
{name: 'Object-Oriented', support: true, parent: 'Imperative'},
{name: 'Event-Driven', support: true, parent: null},
{name: 'Quantum', support: false, parent: null},
{name: 'Symbolic', support: false, parent: null}
]
/* Imperative Pattern */
for (let i = 0; i < paradigms.length; i++) {
if (paradigms[i].support === true) {
console.log(paradigms[i])
}
}
/* Declarative Pattern */
const jsParadigms = paradigms.filter((p) => p.support === true)
console.log(jsParadigms)
A programming paradigm is a style, or “way,” of programming, they are not meant to be mutually exclusive. A single program can feature multiple paradigms. Very few languages implement a paradigm 100% — when they do, they are pure. It is incredibly rare to have a pure OOP
language or a pure functional
language.
Let’s look at common programming paradigms you might encounter over the years:
This post ignores the complexity of parent-child relationship between above programming paradigms and focuses more on those supported by JavaScript.
Functional programming follows a simple principle: programs are built mostly with a handful of very small, very reusable, very predictable pure functions. Pure function will always return the same output regardless of the number of times the function is called, can be safely applied with no side-effects.
Functional programming brings many benefits to your programs:
Lisp, Erlang, Haskell, Closure, and Python are famous functional programming languages; Haskell is a purely functional programming language in the sense that it allows no other paradigms. Functional programming is a natural fit for JavaScript because it offers first-class functions, closures, and simple lambda syntax.
JavaScript has first-class functions — it makes easy to assign functions to variables, pass them into other functions, return functions from other functions, compose functions, and so on.
// Return function from other function
function doSomething() {
return function () {
console.log('Hello There!')
}
}
// Asign function to a variable
const hello = function () {
console.log('Hello There!')
}
JavaScript has closures — is the combination of a function and the lexical environment within which that function was declared; simply define a function inside another function, and expose the inner function, either by returning it, or passing it into another function. For every closure we have three scopes: local scope, outer functions scope, and global scope.
// global scope
var e = 10
function sum(a) {
return function sum2(b) {
return function sum3(c) {
// outer functions scope
return function sum4(d) {
// local scope
return a + b + c + d + e
}
}
}
}
JavaScript has lambda syntax — allows functions to be passed around as data, uses fat arrow notation =>
as lambda operator, and its body can be expression or statement block.
// has an expression as its body
const double = (value) => value * 2
// has a statement block as its body
const triple = (value) => {
return value * 3
}
function multiply(arr, func) {
return arr.map((value) => func(value))
}
const arr = [1, 2, 3, 4, 5]
const doubledArr = multiple(arr, double)
const tripledArr = multiple(arr, triple)
When working with functional programming paradigm we also encounter following patterns, it’s very useful and powerful, it’s important to understand what they are, how they work, and how to put them to good use.
The program is written as a collection of classes and object which are meant for communication. The smallest and basic entity is object and all kind of computation is performed on the objects only.
This paradigm emphasizes on data rather procedure, it can handle almost all kind of real life problems which are today in scenario with many benefits like data security, inheritance, code reusability, flexibility, and abstraction.
When it comes to inheritance, JavaScript supports it in a process of reusing existing objects via delegation that serve as prototypes, also known as prototypal inheritance.
Each object has a private property which holds a link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null
as its prototype. By definition, null has no prototype, and acts as the final link in this prototype chain
.
// a function has a default prototype property as follow
{
constructor: Ć’ doSomething(),
__proto__: {
constructor: Ć’ Object(),
hasOwnProperty: Ć’ hasOwnProperty(),
isPrototypeOf: Ć’ isPrototypeOf(),
propertyIsEnumerable: Ć’ propertyIsEnumerable(),
toLocaleString: Ć’ toLocaleString(),
toString: Ć’ toString(),
valueOf: Ć’ valueOf()
}
}
The language runtime is capable of dispatching the correct method or finding the right piece of data simply by following prototype chain until a match is found.
Objects are mutable in JavaScript, so we can augment the new instances, giving them new fields and methods. These can then act as prototypes for even newer objects. We don’t need classes to make lots of similar objects, objects inherit from objects.
Event-driven programming is a programming paradigm in which the flow of the program is determined by events such as user actions (mouse clicks, key presses), sensor outputs, or messages from other programs or threads.
In an event-driven application, there is generally a main loop that listens for events, and then triggers a callback function when one of those events is detected.
Event-driven programs can be written in any programming language, although the task is easier in languages that provide high-level abstractions, such as await and closures.
JavaScript language itself doesn’t support I/O but relied on host environments. To coordinate events, user interaction, scripts, rendering, networking, and so forth, host environments must use event loop.
Obviously, most programs need to produce output, so complex programs usually can’t be composed using only functional programming, but wherever it’s practical, it’s a good idea mix multiple paradigms to achieve expected result. Each has their own benefits and drawbacks, mix them nicely to build products which are performant, reusable and maintainable.