DEV Community

Cover image for How to organize your JS code: Part 1
Muhammad Ahmad
Muhammad Ahmad

Posted on

How to organize your JS code: Part 1

Classes

The terms “object-oriented,” “class-oriented,” and “classes” are all very loaded full of detail; they’re not universal in definition.
We will use a common and somewhat traditional definition here, most likely familiar to those with backgrounds in “object oriented” languages like C++ and Java.

A class is a definition of a “type” of custom data structure that includes both data and behaviors that operate on that data. Classes define how the data structure works, but classes are not themselves concrete values. To get a concrete value that you can use in the program, a class must be instantiated (with the new keyword) one or more times.
Consider this code:

class Page {
 constructor(text) {
 this.text = text;
 }
 print() {
     console.log(this.text);
   }
}

class Notebook {
 constructor() {
    this.pages = [];
 }
 addPage(text) {
    var page = new Page(text);
    this.pages.push(page);
    }
    print() {
        for (let page of this.pages) {
             page.print();
        }
    }
}

var mathNotes = new Notebook();
mathNotes.addPage("Arithmetic: + - * / ...");
mathNotes.addPage("Trigonometry: sin cos tan ...");
mathNotes.print();
Enter fullscreen mode Exit fullscreen mode

In the Page class, the data is a string of a text stored in a this.text property, the behavior is print(), a method that outputs the text to the console.
For the Notebook class, the data is an array of Page instances. The behavior is addPage(..), a method that instantiates new Page pages and adds them to the list, as well as print() (which prints out all the pages in the notebook).

The statement mathNotes = new Notebook() creates an instance of the Notebook class, and page = new Page(text) is where instances of the Page class are created.

Behavior (methods) can only be called on instances (not the classes themselves), such as mathNotes.addPage(..) and page.print().
The class mechanism allows packaging data (text and pages) to be
organized together with their behaviors (e.g., addPage(..) and print()).
The same program could have been built without any class definitions, but it would likely have been much less organized, harder to read and reason about, and more susceptible to bugs and subpar maintenance.

Another aspect inherent to traditional “class-oriented” design, though a bit less commonly used in JS, is “inheritance” (and “polymorphism”). Consider:

class Publication {
 constructor(title,author,pubDate) {
   this.title = title;
   this.author = author;
   this.pubDate = pubDate;
 }
 print() {
    console.log(`
      Title: ${ this.title }
      By: ${ this.author }
      ${ this.pubDate }
    `);
  }
}
Enter fullscreen mode Exit fullscreen mode

This Publication class defines a set of common behavior that any
publication might need.
Now let’s consider more specific types of publication, like Book and BlogPost:

class Book extends Publication {
 constructor(bookDetails) {
    super(
       bookDetails.title,
       bookDetails.author,
       bookDetails.publishedOn
     );
     this.publisher = bookDetails.publisher;
     this.ISBN = bookDetails.ISBN;
 }
 print() {
    super.print();
    console.log(`
       Publisher: ${ this.publisher }
       ISBN: ${ this.ISBN }
    `);
   } 
}
class BlogPost extends Publication {
    constructor(title,author,pubDate,URL) {
        super(title,author,pubDate);
        this.URL = URL;
 }
    print() {
       super.print();
       console.log(this.URL);
   }
}
Enter fullscreen mode Exit fullscreen mode

Both Book and BlogPost use the extends clause to extend the general definition of Publication to include additional behavior. The super(..) call in each constructor delegates to the parent Publication class’s constructor for its initialization work, and then they do more specific things according to their respective publication type (aka, “sub-class” or “child class”).
Now consider using these child classes:

var Article = new Book({
    title: "How to organize JS",
    author: "Muhammad Ahmad",
    publishedOn: "August 2021",
    publisher: "dev.to",
});
Article.print();
// Title: How to organize JS
// By: Muhammad Ahmad
// August 2021
// Publisher: dev.to
Enter fullscreen mode Exit fullscreen mode

Notice that the child class instance have a print() method, which was an override of the inherited print() method from the parent Publication class.
It did override the child class print() methods call super.print() to invoke the inherited version of the print() method.
The fact that the inherited and overridden methods can have the same name and co-exist is called polymorphism.

Inheritance is a powerful tool for organizing data/behavior in separate logical units (classes), but allowing the child class to cooperate with the parent by accessing/using its behavior and data.

Discussion (2)

Collapse
suvi profile image
Suvendu Karmakar

Good post on explaining the significance of Inheritance. Speaking from some experience, I have seen abstraction to be a greater tool than Inheritance to organize code and separating logical units. Food for thought. :)

Collapse
0xf10yd profile image
Muhammad Ahmad Author

I shall talk about it on part 2, thanks for the comment (✯ᴗ✯)