Organizing JavaScript

Links

https://en.bem.info/methodology/
https://www.webcomponents.org/specs

Prolog

This post is about JavaScript usage on a larger scale. Writing small snippets is one thing and you can get away with almost everything. Writing larger applications becomes a question of scaling. You need architectural patterns to linearily achieve progress when working on large applications.

Scope and Modularity

JavaScript has Scopes. The global scope is the parent scope that always exists. Without any further preparation, variables will be assigned to the global scope. That means all variables from code in script-tags or imported .js files will by default live in the global scope unless local scope is introduced to house variables.

The issue with global scope is that variable names can clash and code ultimately stops working. It prevents good organization and code reuse. Modularity and therefore local scope is needed.

Hoisting with the var Keyword

Hoisting means that variable declarations that make use of the var keyword are moved to the top of the current script (tag or file) or if they are defined within a function, they are used to the top of the current function. Only variable declarations are hoisted, initializations are not moved to the top. That means a hoisted variable can be undefined because the initialization is not moved to the top.

The effect is the same as seen in older versions of C. Variables can only be declared at the top of a function. Hoisting is the automatic process of moving variable declarations to the top of the function or script tag or script file.

Hoisting with the let Keyword

Variables defined with the let keyword are not hoisted all the way to the top of the function or script (tag or file) as is the case with the var keyword but they are hoisted within the nearest scope exclusively. Again, their initialization is not hoisted, so they can be undefined.

The nearest scope is defined by everything that is enclosed in curly braces. That means if-statements, function bodies, loop bodies and even an artificial block defined by an opening and a closing curly brace define local scope.

How to use var, let and const

Code is easy to read if you use const everywhere. If a variable value has to change, use let instead of const. Never use var.

Functions define local Scope

Each function introduces new local scope defined by the function body.

In the very good book Mastering modular JavaScript by Nicolás Bevacqua, the author lists three ways functions (to be precise, the pattern is called IIFE (Immediately-Invoked Function Expression)) have been used a few years ago to define local scope similar to modules:

(function() {
console.log('IIFE using parenthesis')
})()

~function() {
console.log('IIFE using a bitwise operator')
}()

void function() {
console.log('IIFE using the void operator')
}()

Blocks define local Scope

Not only functions define local Scope but blocks do. A function is a special case of a block.

In ES6 this code is possible

{  // open block    
  let tmp = ···;
···
} // close block

The let keyword creates a variable in the local scope (as opposed to the var keyword which  creates a variable that is hoisted to the top of the function which potentially changes the scope it is defined in.

Modules define local Scope

ECMAScript 2015 (ES6) introduced modules (ECMAScript Modules (ESM)) as part of the JavaScript language. In node (which uses CommonJS by default, the ESM system is available only when specifying –experimental-modules and using the .mjs extension for modules)

Before ES6, custom libraries (CommonJS, RequireJS) provided module functionality for JavaScript programmers. Those custom libraries are still used extensively today. Probably because it is a massive undertaking to refactor all existing code to ES6.

So now there is a mix of CommonJS, RequireJS and ES6 modules used in the wild. The syntax for ES6 modules (export, import) differs from the CommonJS and RequireJS syntax (use of the exports-object, require).

While CommonJS is the dominant module system in node, RequireJS is more geared towards browsers. RequireJS implements the Asynchronous Module Definition (AMD) standard. Typescript adopted the ES6 module syntax from the start.

Later Browserify allowed to bundle node bundles into a browser-ready format and allowed the use the node package manager and all it’s modules in the development of web applications that run in a browser. Today webpack is the most widely used bundler and mostly took over Browserify.

Across all possible module systems, the common parts are that every file is a module and a module cannot be spread across multiple files. Every module has it’s own scope and context.

Object Orientated Programming (OOP)

To me, object orientation is about combining state and functions that allow you to access and manipulate that state in one place that you can easily find even in large applications.

Even after weeks of not working on a project, it is clear that a persons name and address are stored in the Person class. It just makes sense and is intuitive.

In Java and C++ for example, first a Class has to be defined. The class definition controls which member variables and which function a class has. At runtime variables (aka. objects or instances) are created from the class. No member variables or functions can be added or removed.

JavaScript is different in many ways. An object can be created without a class definition. At runtime, member variables and functions can be added and removed from objects.

OOP using Object Initializers

let personInstance = {
name: 'person1',
age: 30
}

// location A

personInstance.isBlocked = true;

// location B

delete personInstance.isBlocked;

// location C

In the example above, an object is defined (using JavaScript Object Notation (JSON)) without a class and without a constructor function (see below)! Instead a so called object initializer is used, see https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Working_with_Objects

The object initializer is a block of JSON that defines the object, it’s member variables and it’s functions. For alternatives to object inititalizers look at constructor functions and classes!

In the rest of the script, a member variable ‘isBlocked’ is added and initialized and then removed again. console.logging personInstance.isBlocked at location A yields ‘undefined’ as the isBlocked member is not yet part of the object. At location B, logging will yield true as expected. At location C, logging will again yield ‘undefined’ as the member was removed.

let app = {

settings: {
container: document.querySelector('.calendar'),
calendar: document.querySelector('.front'),
days: document.querySelectorAll('.weeks span'),
form: document.querySelector('.back'),
input: document.querySelector('.back input'),
buttons: document.querySelector('.back button')
},

init: function() {
console.log('container: ', this.settings.container);
console.log('calendar: ', this.settings.calendar);
console.log('days: ', this.settings.days);
console.log('form: ', this.settings.form);
console.log('input: ', this.settings.input);
console.log('buttons: ', this.settings.buttons);
},
}

app.init();

The code above combines data (settings) and functions (init()) into an object (app). Then it calls a method on the app instance. The call will output the state stored in that instance.

There is a shorthand notation to add a function to an object. Instead of using the key value pair notatino ( functionName: function { … } ) you can use functionName() { … }. In the example above init() { … } instead of init: function { … }

The keyword: this

In JavaScript, this used inside a function, refers to the object that called the function. If the function is a member function of an object, that behaviour is not changed. In JavaScript, the this keyword has no relation to the objects instead it has a relation to the caller.

In Java and C++, this used inside a member function refers to the object instance. Here, the this keyword has no relation to the caller!

In DOM event handlers such as click handlers, this refers to the object that emitted the event.

When a function is defined in global scope and the script is executed inside a browser, this refers to the window object. When strict mode is enabled in addition, the this keyword in global scope is undefined and does not point to the window object!

With arrow functions, the behaviour of the this keyword is different than for normal functions. So arrow functions are not syntactic sugar but they have their own characteristics. Arrow functions do not have an own this pointer! Because they have no own this pointer, when calling an arrow function, they do not bind the this pointer to the context of the call! That in turn means that arrow functions do not shadow a this pointer that might exist before calling the arrow function. Because you can still use the this keyword inside an arrow function, the question remains, what the this pointer refers to inside an arrow function! This good news for OOP is that this inside an arrow function defined inside an object has a relation to the object that the arrow function is defined inside! this in an arrow function allows you to access the member variables and functions of the object instance!

When the this keyword is used inside a constructor function, this points to the newly created object instance.

Constructor function is

https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions

CONSTRUCTOR FUNCTIONS

Instead of defining objects using the JSON notation, there is another way called constructor functions.

function Person(first, last, age, eye) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eye;
this.name = function() {
    return this.firstName + " " + this.lastName;
  };
}

const myFather = new Person("John", "Doe", 50, "blue");
const myMother = new Person("Sally", "Rally", 48, "green");

console.log(myFather.name());

The example is taken from here: https://www.w3schools.com/js/js_object_constructors.asp

A function called Person is defined and later used in conjunction with the new keyword to arrive at instance variables myFather, myMother.

The Person() function is referred to as the constructor function. Inside the constructor function, the this keyword actually points to the instance that is just being created. 

OOP with Prototypes

Funtions can be added to the prototype and can then be called later.

OOP With Classes

The class keyword in javascript is syntactic sugar for JavaScript’s prototype system. That means the compiler or interpreter transforms the keywords into other JS features so the programmer is freed from the task.

Classes where introduced in ES6 (ECMAScript 2015)

Epilog

Especially with languages, I am no proficient in, I personally find myself in a situation where my progress keeps getting slower and slower over a day of developing software, just because I keep battling the language and how to organize the code when the application gets bigger. It gets slower until I get to a complete stop and I have to give up for the day. The next day there is even less progress. It becomes similar to wading through a swamp and you are finally so tired that you give up and the swamp swallows you. 

With languages I am proficient in, I find that I am not blocked by the language itself. I am blocked by medium to hard problems I have to solve but the programming language is at tool that makes it easier to solve the problems rather than slowing me down.

When you find yourself in a situation where the language is slowing you down, you have to realize that your programming skills in that language are lacking and you have to go back to school.

This article showed ways to use JavaScript which are applicable to larger problems

Leave a Reply