NodeJS in a nutshell

Contents

Introduction

Welcome! This article is a summary of the NodeJS language. It is intended for people who are new to Node, or who want to refresh their knowledge of the language, and is a quick overview of the most important concepts.

Before we start

I would like to mention that NodeJS (and JS in general) are high-level programming languages, so the terms "heap" and "stack" will not be seen in this article. The engine takes care of all the memory management, so you don't have to worry about it. Additionally, if you want to use low-level functions, you will have to write C++ addons for NodeJS.

So, where do we start?

Well, the basics. You may have heard of the term "variables". Usually, beginners are taught to think of variables as "a box that has a name and data". Here is an example of variable definition:
let x = 5;
So, what exactly is happening here? Well, we are creating a variable called "x" and assigning it the value of 5. The "let" keyword is used to define a variable. You can also use "const" to define a constant, which is a variable that cannot be changed. The "var" keyword is also used to define a variable. It can also be used to declare variables, but the key difference between them lies in their scopes - "var" is function scoped while let is "block" scoped - this means that var variables are accessible anywhere within the function, while let variables are only accessible within the block they are defined in.
Some more examples of variable definitions:

String (text) variable

let x = "Hello, world!";

Boolean (true/false) variable

let x = true;

Array (list) variable

let x = [1, 2, 3, 4, 5];

Object (dictionary/map) variable

let x = { a: 1, b: 2, c: 3 };
Arrays and objects are special data types, and if you are a beginner you are probably not familiar with them. Basically, an array is a list of values, and each value can be retrieved by it's position, called the index.
Note: indexes always start with 0, so to get the first element of an array you would do:
let x = [1, 2, 3, 4, 5];
console.log(x[0]); // 1
Objects are similar to arrays, but instead of using indexes to retrieve values, you use keys. Keys are basically names of the values, and they can be any string. Here is an example of an object:
let x = { a: 1, b: 2, c: 3 };
console.log(x.a); // 1
Note how there is a semi-colon (;) at the end of each line. It is not required, but it is good practice to use one at the end of each line.

Functions

Functions are a way to group code together and execute it later. They are defined using the "function" keyword. Here is an example of a function:
function sayHello() {
    console.log("Hello, world!");
}
To execute a function, you simply call it by it's name:
sayHello(); // Hello, world!
Functions can also take arguments, which are values that are passed to the function when it is called. Here is an example of a function that takes an argument:
function sayHello(name) {
    console.log("Hello, " + name + "!");
}
To call a function with arguments, you simply pass them to the function when you call it:
sayHello("John"); // Hello, John!
You can also define a function that returns a value. Here is an example of a function that returns the sum of two numbers:
function sum(a, b) {
    return a + b;
}
When you call a function that returns a value, you can assign the output to a variable:
let x = sum(1, 2);
console.log(x); // 3

"if" Statements

"if" statements are a way of checking whether a condition is true and running code based on that. Here is an example of an "if" statement:
let x = 5;
if (x == 5) {
    console.log("x is equal to 5!");
}
You can also use "else if" and "else" to run code if the condition is false:
let x = 5;
if (x == 6) {
    console.log("x is equal to 6!");
} else if (x == 5) {
    console.log("x is equal to 5!");
} else {
    console.log("x is not equal to 5 or 6!");
}
Feel free to chain as many "else if" statements as you want. You can also use "else if" without an "else" at the end.

Conditional operators

Conditional operators are used to check whether a condition is true or false. Here is a list of all the conditional operators:

Loops

Loops are a way of repeating code. There are two types of loops: "for" loops and "while" loops.

"for" loops

"for" loops are a way of repeating code a certain number of times. Here is an example of a "for" loop:
for (let i = 0; i < 5; i++) {
    console.log(i);
}
This code may look a bit confusing, so let's break it down. The first part of the "for" loop is the initialization, which is executed before the loop starts. In this case, we are declaring a variable called "i" and setting it to 0. The second part is the condition, which is checked before each iteration of the loop. In this case, we are checking whether "i" is less than 5. The third part is the increment, which is executed after each iteration of the loop. In this case, we are incrementing "i" by 1. The code inside the loop is executed as long as the condition is true.

"while" loops

"while" loops are a way of repeating code until a condition is false. Here is an example of a "while" loop:
let i = 0;
while (i < 5) {
    console.log(i);
    i++;
}
This is very similar to a "for" loop, except that the initialization and increment are done outside of the loop. This is useful if you want to use the variable outside of the loop.
Note: when writing while loops, you have to make sure not to get stuck inside it. Otherwise, you will get a "Maximum stack call size exceeded" exception.

Modules

Modules are a way of organizing your code. You can import and export variables, functions, and classes from modules. Here is an example of a module:
module.exports = {
    sayHello: function(name) {
        console.log("Hello, " + name + "!");
    }
}
You can import this module like this:
let myModule = require("./myModule.js");
myModule.sayHello("John"); // Hello, John!
You can also download modules from npm, a registry of open-source modules. Here is an example of a module from npm:
> npm install discord.js
You can import and use this module like this:
let Discord = require("discord.js");
let client = new Discord.Client();
client.login("token");

Classes

Classes are another way of organizing your code. They are similar to modules, but they are more object-oriented. Here is an example of a class:
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old!");
    }
}
You can create an instance of this class like this:
let john = new Person("John", 15);
Let's break down what's happening here. First, we are declaring a class called "Person" using the "class" keyword. Then, we are creating a constructor function using the "constructor" keyword. The constructor function is executed when we create a new instance of the class. The constructor function takes two parameters: "name" and "age". We are then setting the "name" and "age" properties of the class to the values of the "name" and "age" parameters. Finally, we are creating a method called "sayHello" using the "sayHello" keyword. We can call this method using the "this" keyword. The "this" keyword refers to the current instance of the class, when you are writing code inside the class.
Note: functions also have a "this" keyword, but it refers to the current object (parent) of the function. This doesn't apply to arrow functions. But what are arrow functions?

Arrow Functions

Arrow functions are a way of writing functions in a shorter way. Here is an example of an arrow function:
let sayHello = (name) => {
    console.log("Hello, " + name + "!");
}
This is very similar to a normal function, except that we are using the "=>" arrow instead of the "function" keyword. You can also write arrow functions in a shorter way:
let sayHello = name => console.log("Hello, " + name + "!");
This is very similar to the first example, except that we are not using curly braces. This is because we are only writing one line of code inside the function. You can also write arrow functions in an even shorter way:
let sayHello = name => "Hello, " + name + "!";
This is very similar to the second example, except that we are not using curly braces. We also don't need to write "return" because arrow functions automatically return the value of the last expression.

Promises

Before I actually explain this topic, I need to explain what synchronous and asynchronous code is. Synchronous code is code that is executed in order. Asynchronous code is code that is executed when a certain event happens. For example, if you are writing a Discord bot, you will need to use asynchronous code because you need to wait for the bot to connect to Discord before you can send messages.
So, what are promises? Well, a Promise is a JavaScript utility class that lets your write asynchronous code. Here is an example of a promise:
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Hello, world!");
    }, 1000);
});
Let's break this down. First, we are creating a new instance of the Promise class. Then, we are passing a function to the Promise class. This function takes two parameters: "resolve" and "reject". The "resolve" parameter is a function that is executed when the promise is resolved. The "reject" parameter is a function that is executed when the promise had an error. Finally, we are using the "setTimeout" function (built-in utility function) to wait for 1 second before resolving the promise.
Now, let's see how we can use this promise:
promise.then((value) => {
    console.log(value);
});
The "then" function is executed when the promise is resolved. The "value" parameter is the value that was passed to the "resolve" function. In this case, the value is "Hello, world!". You can also use the "catch" function to catch errors:
promise.catch((error) => {
    console.log(error);
});
The "catch" function is executed when the promise had an error. The "error" parameter is the value that was passed to the "reject" function. In this case, the value is "Hello, world!". You can also use the "finally" function to execute code after the promise is resolved or rejected:
promise.finally(() => {
    console.log("Promise finished!");
});
There is another helpful function called "all". It is a static function of the Promise class, meaning that you don't need to create an instance of the Promise class to use it. Here is an example of the "all" function:
Promise.all([promise1, promise2]).then((values) => {
    // values is an array of the values of promise1 and promise2
});

"await" Keyword

The "await" keyword is a way of writing asynchronous code in a synchronous way. It can only be used inside asynchronous functions. Here is an example of await:
async function sayHello() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Hello, world!");
        }, 1000);
    });
}

async function run() {
    let value = await sayHello();
    console.log(value);
}

run();
Let's break this down. First, we are creating an asynchronous function called "sayHello". Then, we are creating a new instance of the Promise class. Finally, we are returning the value of the promise. Next, we are creating another asynchronous function called "run". Then, we are creating a variable called "value" and setting it to the value of the promise, after waiting for it to resolve. Finally, we are logging the value to the console. From the main scope, we are calling the "run" function. This will log "Hello, world!" to the console after 1 second.

Summary & Credits

Thanks for reading this tutorial! I hope you learned something new.
Fonts: Fira Code | Orbitron
Syntax highlighting: highlight.js
Code block theme: Dracula at Night