JavaScript programming Paradigms
JavaScript is the primary programming language used for web development on the front-side, as it has a long history that can be traced back to the beginnings of the World Wide Back in 1995 when it was officially released. Surviving some battles in the supremacy of the frontend language, with strong contenders like the Java applets that were very successful in the late '90s and early 2000s, which consisted in small applications embedded in the web page and written in Java. Another famous contender was the Adobe Flash platform, which also offered the possibility to write applications on an embedded platform inside the browser, but using a programming language more related (we might call them cousins) to JavaScript, from the same family of EcmaScript languages: ActionScript.
Article series
- The evolution of Frontend Development
- The rise of Single Page Applications
- What Front-end development really means?
- JavaScript Engine and the Event Loop
- JavaScript Programming Paradigms
There are other examples we could cite of not so successful attempts to take over JavaScript, as the defacto programming language for the Web, I would say that what they had in common was the idea that JavaScript was not good enough to provide a rich and robust platform for multimedia features, such as SDKs for building video players, video games, 3D rendering, to say some. At the time these platforms were successful, like Java Applets, is true that JavaScript was far behind in terms of good Web APIs that provided the features for rich multimedia features and that's because those platforms succeeded for a while. However, in the long run, they all lost the fight against JavaScript, but why?. In part was because some platforms like Java applets or Microsoft Silverlight tried to bring the same ideas that had worked for desktop applications development, into frontend development, building a runtime and framework over another existing browser runtime, which is a fallible idea, trying to put a patch that doesn't fit in and is going to break sooner than later.
Modern Frontend Development in JavaScript
There are several reasons for what a runtime over another runtime is not a good idea:
- The standard defining the Browser API interfaces continues evolving and is independent of any framework that's built upon, forcing breaking changes in those frameworks that are hard to keep up with.
- There is a context switching cost to move from the development work in JavaScript and the programming languages used in another framework, such as Java, slowing down development as a whole.
- Many of the features that a dynamic typed and interpreted programming language as JavaScript are lost in frontend development when we introduce a completely different language on top of it. This is the main point of discussion for the rest of this section.
Having learned the lesson from failed attempts to replace JavaScript (at least at a runtime level) modern frontend development is now focused on the use of JavaScript and the Web APIs to build all kinds of rich web applications features, like video playback, 2d, and 3d animation rendering, GEO localization, hardware interfacing, caching, among several others. Thus, large and complex projects can be divided into sections (or modules) specialized in some features. For instance, a project can have a separate module for visualizations (think of pie charts, bar, and scatter plots) that are shared between several pages, which in turn can be also a separate module, as it's the case of Angular and Vue projects structure.
JavaScript Modules
Modules are the primary artifacts used to separate entire sections of code either by business-related rules or by logic cohesion in the form of relationships between a group of entities, such as classes and functions.
Virtually every programming language has a mechanism to split sections of code into modules (like namespaces in C# or Java Modules to cite some), and in the world of JavaScript, we've seen some proposals over the years, like CommonJS, RequireJS, Webpack or Babel, that come from external libraries. However, recently most browsers have adopted the standard EcmaScript 6 (ES6) modules, or simply JavaScript modules.
Programming Paradigms
A central subject in the design and architecture of a software system is the programming language on which is built upon. A programming language usually supports one or more programming paradigms. We can define a paradigm as the way to write a program, which tells us what structures to use, when, and how.
JavaScript is in some ways a special programming language that was designed with a paradigm in mind that differed from other popular languages at the time, like Java (JavaScript took its name from Java, to make it sound familiar, but the language is very different). That is, JavaScript was not an Object-Oriented language (OOP), like others that emerged before in the book of this paradigm, like Java or C++. At least it was not an OOP language as we know them today, but to understand this better, we need to take a closer look at the three most relevant programming paradigms (from which any other derives): Structured, Functional, and Object-Oriented paradigms.
Structure Programming
This is the first paradigm that most of the software engineers learn first in the university, as it was also the first one to be adopted in the industry, popularized by a highly influential programming language like C and others that came later, such as Java or C#. The objective of this paradigm is, as its name suggests, to structure the code using restrains over random jumps (goto statements) that were very common in the past and then _considered harmul_ in a famous letter by Egster Wybe Dijkstra, who invented this paradigm.
JavaScript fully supports structured paradigm and takes a large heritage from other programming languages like C or Java in this regard, by the use of conditional statements like if/then/else/switch and loop statements do/while/for/foreach. A good way to summarize this paradigm in the words of Robert C. Martin, as follows,
-- Robert C. Martin
Functional Programming
The oldest of all the programming paradigms, although not the first to be adopted by the industry for building and monetizing applications. Functional programming (FP) has its roots in lambda calculus, a branch of math developed by Alonzo Church at the same time that Alan Turing created the Turing machine model, proving later that both lambda calculus and Turing machines are equivalent models. So FP is a paradigm based on lambda calculus and follows the idea that values are mapped to other values through the use of trees of expressions made from functions and the value of data should be immutable to the furthest extent, or in other words, it should be modified under strict discipline rules.
-- Robert C. Martin
FP takes a prominent in JavaScript as a programming language and also in Frontend architecture. We'll see in upcoming articles, most state management in large applications is better achieved by following the state immutability principles with a purely functional approach.
As a programming language, JavaScript has many elements of Functional Programming, like the use of map/filter/forEach/some/every to loop over an iterable collection, creating a new collection each time, not modifying the original, enforcing thus data immutability.
Let's see an example of how we'd loop over an array of names and changing its values to upper case with a traditional structured programming approach.
let breeds = ['akita', 'chihuaha', 'pomeranian', 'schnauzer', 'samoyed'];
for(let i = 0; i < breeds.length; i++){
breeds[i] = breeds[i].toUpperCase();
}
// result: ["AKITA", "CHIHUAHA", "POMERANIAN", "SCHNAUZER", "SAMOYED"]
By using for in this way, the original breeds array is modified to contain all uppercase values, a clear example of mutable data. But let's see how the alternative using a map function.
const breeds = ['akita', 'chihuaha', 'pomeranian', 'schnauzer', 'samoyed'];
const upperBreeds = breeds.map(b => b.toUpperCase());
// result: ["AKITA", "CHIHUAHA", "POMERANIAN", "SCHNAUZER", "SAMOYED"]
In this case, JavaScript lets us explicitly keep the original array untouched by using the keyword const, and with the map function, a new array is created with all names in capital letters, leaving the original array intact. An additional benefit we get is that the code becomes more concise and clear, making it easier to read.
Object Oriented Programming
Object-Oriented Programming (OOP) was the second programming to be adopted in the industry and is the only one from the three discussed here that was not originated as the result of research in academia. Actually, it was not intended as the paradigm with many of the features for which we know it today, like inheritance, dynamic memory allocation with "new" or polymorphism, instead it was just about sharing immutable messages between objects and hiding implementation details (encapsulation). Alan Kay, considered one of the fathers of OOP, originally had that vision for this paradigm, as he stated:
-- Alan Kay, father of OOP
So, through the years, OOP suffered a transformation from the original intent of message sharing between objects to something fundamentally different (as the result of not having a formal mathematical or theoretical foundation as FP or structured programming), that is, memory allocation of objects in the heap memory space, allowing long-lived objects shared as memory references, from which we can access its methods at further points in time and use their properties. In other words, modern OOP is about the indirect transfer of control.
-- Robert C. Martin
When we see OOP this way, we get a really useful mechanism to structure the architecture of a software application, making use of techniques such as dependency injection (DI), (a technique we'll study in the next article in this series), that provides a robust mean for working with modules, enforcing encapsulation and loose coupling. These benefits are possible thanks to a property of OOP known as polymorphism, the interchangeability of object types at runtime, one of the best tools for software architecture, and the one we'll use the most for Frontend in this book.
Although, not everything in OOP enjoys popularity among software engineers and architects. Over years there has been some criticism about the features of this paradigm that doesn't provide a clear benefit, like having large chains of inheritance or data mutability inside objects, by getters and setter methods, and having a long and divided vision about what OOP is really about.
When studied in academia, some argue that OOP is a tool for creating a model of real-world concepts, by directly applying relationships between objects. So the question arises, should you define a relation between two objects like "one to one", "one to may", "many to many"?. Should we define an inheritance relationship as "is a" or "has a"?. Truth is that a model for the design of a system is better done at the architecture level, not at the language level where easily increases complexity and arise the need to find a benign pattern that keeps that complexity under control. That's what led to the birth of what we know today as Design Patterns, practical solutions for typical scenarios in OOP, but that are not really needed in Functional Programming or Structured Programming. In words of Edsger Dijkstra (father of FP):
-- Edsger W. Dijkstra, pioneer of computer science
There are some adepts of Object-Oriented Programming and also some detractors. However, we can't deny the large and rich heritage of development patterns, practices, and tools that comes with it. In the end, is just another paradigm that has a lot to offer, we just need to take it with a pinch of salt. For the purposes of this book, we're going to use Functional and Structured programming wherever possible, for most of the coding tasks, and keep OOP for structure higher-level components following polymorphism, the SOLID principles.
In the next post, we'll move into the types of web applications and how to apply the architecture principles in combination with architecture styles.