DEV Community

Cover image for Modern JavaScript for everyone: Mastering Modern JavaScript The Right Way
Daniel Obare
Daniel Obare

Posted on

Modern JavaScript for everyone: Mastering Modern JavaScript The Right Way

1. Introduction

JavaScript is the language of the web. If you want to code for the web, you need to know JavaScript inside and out. From humble beginnings, JavaScript has grown to a powerful and complex language with features such as classes, promises, arrow functions, generators, string templates, and many others.

We will start with the very fundamentals of the language: variables and datatypes. Then in each lesson you'll build knowledge, from data structures like arrays and maps to loops, control structures, and functions. Along with the basics of the language, you'll also learn some key built-in APIs for manipulating data, AJAX, and working with the web browser DOM. Finally, we'll get a look at some of the most powerful and widely used web APIs that are supported by all modern browsers.

2. Language Fundamentals

2.1 Variables

A variable is a container for a value, like a number we might use in a sum, or a string that we might use as part of a sentence.

let myAge = 17;
let dolphinGoodbye = 'So long and thanks for all the fish';
let iAmAlive = true;
let test = 6 < 3;
let myNameArray = ['Chris', 'Bob', 'Jim'];
let dog = { name : 'Spot',
            breed : 'Dalmatian' };
let myNumberArray = [10, 15, 40];

Enter fullscreen mode Exit fullscreen mode

2.2 Data Types

JavaScript is a loosely typed and dynamic language. Variables in JavaScript are not directly associated with any particular value type, and any variable can be assigned (and re-assigned) values of all types:
let foo = 42; // foo is now a number
foo = 'bar'; // foo is now a string
foo = true; // foo is now a boolean

JavaScript types
The set of types in the JavaScript language consists of primitive values and objects.

Primitive values (immutable datum represented directly at the lowest level of the language)
Boolean type

Null type
Undefined type
Number type
BigInt type
String type
Symbol type
Objects (collections of properties)
Primitive values

All types except objects define immutable values (that is, values which can't be changed). For example (and unlike in C), Strings are immutable. We refer to values of these types as "primitive values".

Boolean type
Boolean represents a logical entity and can have two values: true and false.

Null type
The Null type has exactly one value: null.

Undefined type
A variable that has not been assigned a value has the value undefined.

Number type
The number type has only one integer with two representations: 0 is represented as both -0 and +0. (0 is an alias for +0.)
> 42 / +0
Infinity
> 42 / -0
-Infinity

2.3 Arithmetic, Assignment and comparison operators

2.3.1 Arithmetic operator

An arithmetic operator takes numerical values (either literals or variables) as their operands and returns a single numerical value. The standard arithmetic operators are addition (+), subtraction (-), multiplication (*), and division (/).

1 / 2; // 0.5
1 / 2 == 1.0 / 2.0; // this is true

2.3.2 Assignment operator

An assignment operator assigns a value to its left operand based on the value of its right operand. The simple assignment operator is equal (=), which assigns the value of its right operand to its left operand.

let obj = {};

obj.x = 3;
console.log(obj.x); // Prints 3.
console.log(obj); // Prints { x: 3 }.

const key = "y";
obj[key] = 5;
console.log(obj[key]); // Prints 5.
console.log(obj); // Prints { x: 3, y: 5 }.
Enter fullscreen mode Exit fullscreen mode

2.3.3 Comparison operator

A comparison operator compares its operands and returns a logical value based on whether the comparison is true. The operands can be numerical, string, logical, or object values. Strings are compared based on standard lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type, JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the === and !== operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands to compatible
types before checking equality. Other comparison examples include:

Equal (==), Not equal (!=), Strict equal (===), Strict not equal (!==), Greater than (>), Greater than or equal (>=), Less than (<), Less than or equal (<=).

2.3.4 Logical operator
Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value. However, the && and || operators actually return the value of one of the specified operands, so if these operators are used with non-Boolean values, they may return a non-Boolean value. Another example is Logical NOT (!)

var a1 =  true && true;     // t && t returns true
var a2 =  true && false;    // t && f returns false
var a3 = false && true;     // f && t returns false
var a4 = false && (3 == 4); // f && f returns false
var a5 = 'Cat' && 'Dog';    // t && t returns Dog
var a6 = false && 'Cat';    // f && t returns false
var a7 = 'Cat' && false;    // t && f returns false

var o1 =  true || true;     // t || t returns true
var o2 = false || true;     // f || t returns true
var o3 =  true || false;    // t || f returns true
var o4 = false || (3 == 4); // f || f returns false
var o5 = 'Cat' || 'Dog';    // t || t returns Cat
var o6 = false || 'Cat';    // f || t returns Cat
var o7 = 'Cat' || false;    // t || f returns Cat

var n1 = !true;  // !t returns false
var n2 = !false; // !f returns true
var n3 = !'Cat'; // !t returns false
Enter fullscreen mode Exit fullscreen mode

2.3.5 Functions

Functions are one of the fundamental building blocks in JavaScript. A function in JavaScript is similar to a procedure—a set of statements that performs a task or calculates a value, but for a procedure to qualify as a function, it should take some input and return an output where there is some obvious relationship between the input and the output. To use a function, you must define it somewhere in the scope from which you wish to call it.

// The following variables are defined in the global scope
var num1 = 20,
    num2 = 3,
    name = 'Chamakh';

// This function is defined in the global scope
function multiply() {
  return num1 * num2;
}

multiply(); // Returns 60

// A nested function example
function getScore() {
  var num1 = 2,
      num2 = 3;

  function add() {
    return name + ' scored ' + (num1 + num2);
  }

  return add();
}

getScore(); // Returns "Chamakh scored 5"

function addSquares(a, b) {
  function square(x) {
    return x * x;
  }
  return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41

Enter fullscreen mode Exit fullscreen mode

3. Data Structures & Algorithms

With the primitive data types defined, we can now work on some data structure implementations specific to JavaScript. Data structures are a way of storing and organizing the data primitives we just described so that they can be efficiently accessed and used in algorithms.

3.1: Arrays
Arrays are sequences of primitive data types, similar to a list. In JavaScript, there are two common object-oriented implementations of array-like objects: stacks and queues, as well as the specially defined array object. Stacks and queues differ from the exact definition of arrays in other programming languages by how objects are added or removed.

Queues are FIFO (first in, first out) while stacks are LIFO (last in, first out). You can think of a queue as a line of people going into a store, where the first one in the line gets into the store, and a stack as a stack of files, with the last one placed on the stack being the first one out.

Both queues and stacks offer the opportunity to display every datatype stored within the array and to slice and “peek” at particular elements. This is also true of the JavaScript array type, which is a specially defined object in JavaScript.

We can work with arrays to define a list of data types, and then index and filter for the first one (by definition, arrays are zero-indexed, meaning a slice of [0] for the index will return the first item and so on).

3.2: Linked Nodes
Linked nodes include many different types of array-like objects since they store datatypes in sequences. The critical difference is that instead of pointing to indexes as we saw with our array example of when a datatype was placed in an array, linked nodes hold pointers to other objects. So, in order to follow the linked nodes, you’ll have to transverse the different list objects using each one as a reference to go to the next one. You start at the head and then go all the way to the tail instead of calling a master index.

There are multiple types, from singly-linked lists, doubly-linked lists (which links the tail to the head, allowing us to travel back and forth through the different data types) to trees and graphs. Trees connect parents to multiple child nodes as opposed to linked lists, which connects one parent with one child. Graphs allow for the connection of multiple parent nodes to multiple child nodes. Here is an implementation of a linked list.

3.3: Hash Tables
A hash table is a dictionary-like data structure, where keys are paired with values. Hash tables are great for rapid retrieval and modification of data, though the array and list-like objects above are better for storage. Still, especially with the explosive growth of data, hash tables have become nearly ubiquitous. For example, popular NoSQL databases used in the web such as MongoDB and Redis are distributed hash tables and key/value stores. This is an example of a hash table implementation in JavaScript.

Implementing Algorithms Using JavaScript

3.4: Doubling Algorithm (Arithmetic Algorithm)
Let’s start off with a simple arithmetic function, that shows us how to do a sequence of steps in JavaScript. We’ll take something and multiply it by two, then log it to our console. This requires us to define a simple variable and function.

Note at the end that when we try passing a string datatype to this algorithm, it results in an NaN datatype (not a number).

3.5: QuickSort (Sorting Algorithm)

A common problem with programming algorithms is how to sort through arrays of values so that they come in some logical order, say, from the lowest to the highest integer in an array of numbers. QuickSort is a sorting algorithm that can help with this. By employing a pivot and going through subsets of an array, we can slowly sort every element that is smaller than the pivot to its left.

3.6: Jump Search (Search Algorithm)

Now that we’ve sorted an array, another common class of programming algorithms tries to solve the problem of searching if a value exists in an array. Using jump search, we aim to chunk out subsets of the array such that it’ll be more efficient than binary search at filtering through already-sorted arrays. We look for an interval of known greater and lesser elements where our search value might be.

4. Working with DOM

The Document Object Model (DOM) connects web pages to scripts or programming languages by representing the structure of a document—such as the HTML representing a web page—in memory. Usually it refers to JavaScript, even though modeling HTML, SVG, or XML documents as objects are not part of the core JavaScript language.

The DOM represents a document with a logical tree. Each branch of the tree ends in a node, and each node contains objects. DOM methods allow programmatic access to the tree. With them, you can change the document's structure, style, or content.

Nodes can also have event handlers attached to them. Once an event is triggered, the event handlers get executed.

5. Asynchronous JavaScript

5.1 Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation. Since most people are consumers of already-created promises, this guide will explain consumption of returned promises before explaining how to create them.

Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.

Imagine a function, createAudioFileAsync(), which asynchronously generates a sound file given a configuration record and two callback functions, one called if the audio file is successfully created, and the other called if an error occurs.

function successCallback(result) {
  console.log("Audio file ready at URL: " + result);
}

function failureCallback(error) {
  console.error("Error generating audio file: " + error);
}

createAudioFileAsync(audioSettings, successCallback, failureCallback);
Enter fullscreen mode Exit fullscreen mode

5.2 promises chaining after a catch

It's possible to chain after a failure, i.e. a catch, which is useful to accomplish new actions even after an action failed in the chain.

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');

    console.log('Do this');
})
.catch(() => {
    console.error('Do that');
})
.then(() => {
    console.log('Do this, no matter what happened before');
});
Enter fullscreen mode Exit fullscreen mode

5.3 Async

An async function is a function declared with the async keyword, and the await keyword is permitted within it. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

Async functions may also be defined as expressions.

function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

Enter fullscreen mode Exit fullscreen mode

5.4 Await

The await operator is used to wait for a Promise. It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}

f1();
Enter fullscreen mode Exit fullscreen mode

Follow for more insights

Top comments (0)