Javascript Cheat Sheet

Basic: Terminal, script, Variables, Types & Strings

Connect a JavaScript file with <script></script>

script -> two attributes: src="./index.js and defer

(tells the browser to delay the loading of the script until all HTML elements are loaded. Alternative: script tag at the end of the body element, so defer attribute is not necessary. Less modern.)

Console

console.log("Hello World!")-> logs into console

Use

let number = 33;
console.log("number is", number) ->with a expresion-help before ("number is") to orientation in the console
console.log("the number is" + number) -> concatenation
console.log({number})-> destruckturing to see the object in the console
console.log(`the number is ${number}`) -> string literal

Other:

console.clear() -> clears console
console.error("Error!") -> logs as error into console

Variables

const

const -> declares a constant, the value can't be changed. Default way to declare variables

let

let -> declares a variable, the value can be changed. Only used when reassigning a new value is necessary.

var

var -> outdated, not used anymore.

Assignation

IMPORTANT Assignation with =
let counter = 0;

Variable Naming

let myFirstVariable = "hello" -> use camel case. Be very specific, longer names are better than shorter: updatedFollowerCounter instead of counter

Data Types

Primitive Data Types(7)

  • string -> a sequence of characters: "abcd" (with " ")
  • number -> a number: 1234 (without " ")
  • boolean -> a binary statement, can be true or false (without " ")
  • null -> represents "nothing", is a value used in JavaScript to indicate that a variable has been deliberately set to "no value". It is an explicit value that you can assign to a variable to show that it is empty or has no value.
  • undefined -> represents the state of "not existing". means that a variable has been declared, but no value has been assigned to it yet. If you access an uninitialised variable, it is set to undefined by default.
  • BigInt -> uncommon, used for integers larger than 9007199254740991
  • Symbol -> uncommon, used for creating unique elements

Other (not PDT!!)

NaN -> occurs when you perform mathematical operations on values that do not represent valid numbers. For example, if you try to divide a number by a string or perform an invalid mathematical calculation, you will get NaN as the result.

String methods & propierties

  • length -> let firstName = "John"; firstName.length // 4

    length is a propierty

  • indexOf -> const text = "Hello World";
    let letterHIndex = text.indexOf("H"); console.log(letterHIndex) // 0 ;
    let letterAIndex = text.indexOf("a"); console.log(letterAIndex) //-1 ;
    //in the order starting from 0, spaces are inclusive
  • search -> const message = "Hello John!";
    let positionJ = message.search("John"); console.log(positionJ) // 6
    let positionM = message.search("Martha"); console.log(positionM) // -1
    //in the order starting from 0, spaces are inclusive
  • concat() concatenates strings. string.concat("moreText") //Variable are posible too
  • charAt() returns the character at a specific position in a string, starting at 0. string.charAt(0) // The first character of the string
  • includes(subString) returns true if the string contains the subString. string.includes("hello")// true or false
  • replace(oldString, newString) replaces a portion of a string and returns the result. string.replace("john", "jane")// string include the first time the word jane, not john
  • replaceAll(oldString, newString) replaces all occurrences, not just the first one. string.replace("john", "jane")// string replace all "john" for "jane"
  • slice() cuts the string up to the specified index and returns the remaining characters. string.slice(1)// the string without the first Word
    string.slice(1,2)// the string from the character in index 1 to character in index 2
  • substring() also cuts the string based on specified indices (the same as slice, but dont work with negativ values).
  • toLowerCase() converts the string to lowercase.
  • toUpperCase() converts the string to uppercase.
  • toString() converts numbers into strings.
  • trim() returns a string with all whitespace removed from the beginning and end.
  • startsWith(subString) returns true if the string starts with subString..
  • endsWith(subString) returns true if the string ends with subString.
  • More Methods:MDN
  • -1 is normally used for the representation of non-existence or non-detection

Operators, Expresion and Number

Arithmetic operators

let a = 1; let b = 2 ; let c = 3; let result = 0

+ ->console.log ("a + b =", (a += b)) ; // or use = a+b
- ->console.log("a - b =", (a -= b)); // or use = a-b
* ->console.log("a * b =", (a *= b)); // or use = a*b
/ ->console.log("a / b =", (a /= b)); // or use = a/b
% ->console.log("a % b =", (a %= b));// or use = a%b
** -> console.log("a ** b =", (a **= b)); //or use a**b;
Incrementation (1) a++
Decrementation (1) a- -

Comparision operators

A === B
->strict equal: is true if both values are equal (including their type).
A !== B
->strict not equal: is true if both values are not equal (including their type).
A > B
->strictly greater than: is true if A is greater than B.
A < B
->strictly less than: is true if A is less than B.
A >= B
->greater than or equal: is true if A is greater than or equal B.
A <= B
-> less than or equal: is true if A is less than or equal B.
IMPORTANT: === and !== are strict equality operators. This is what you need almost always. Strict equality checks if type and value are the same: "3" === 3 is false.

Logical Operators

You can combine logical operators with brackets to define which operator should be evaluated first.

!A
->not: flips a true value to false and vice versa.
A || B
->or: is true if either A or B is true.
A && B
->and: is true if both A and B is true.

Truthy and Falsy Values

Sometimes you want to have a condition depending on another type of value. JavaScript can transform any value into a boolean with type coercion. That means that some values act as if they were true and others as if they were false: Truthy values become true, falsy values become false.

truthy values:

  • non zero numbers: 1, 2, -3, etc.
  • non empty strings: "hello"
  • true

falsy values:

  • 0 / -0
  • null
  • false
  • undefined
  • empty string: ""

Numbers Methods

let string = "2" // String

let numberA = 0.12345 //Number

let numberB = 0.56789 //Number

  • Number(string)transform to number
  • .toFixedround up and down . OUTPUT IS A STRING!!

    console.log(numberA.toFixed(3)); //0.123

    console.log(numberB.toFixed()); //1

    console.log(numberB.toFixed(3)); //0.568

  • Math.round()round up and down . OUTPUT IS A NUMBER!!

    console.log(Math.round(numberA)); //0

    console.log(Math.round(numberB)); //1

  • Math.ceil()round ALLWAYS up. OUTPUT IS A NUMBER!!

    console.log(Math.ceil(numberA)); //1

    console.log(Math.ceil(numberB)); //1

  • Math.floor()round ALLWAYS down. OUTPUT IS A NUMBER!!

    console.log(Math.floor(numberA)); //0

    console.log(Math.floor(numberB)); //0

  • Math.random()always gives us a random number between 0 and 1. You can combinate the methods:

    console.log(Math.random() * 10);Gives us a number between 0 and 10 ( 10 not included)

    console.log(Math.ceil(Math.random() * 10));Gives us a random number between 1 and 10 (10 Included)

    console.log(Math.round(Math.random() * 10))radom number from 0 - 10

    console.log(Math.round(Math.random() * 10 + 1));radom number from 1 - 10

  • console.log(Math) you see all the Methods

Date Methods

const todayDate = new Date(); get the Current Time Date back. An Object

const dateWithParameters = new Date(1977, 3, 10, 16, 12, 57) with the Parameters, gives us a specific date. Remember:the value of the months is given by the index starting with 0, i.e. April is 3

When we calculate we get miliseconds: todayDate - dateWithParameters = ..... miliseconds

Transform Methods

todayDate.toString() to transform to string to be able to use string methods (toLowerCase(), splice()...)

todayDate.toUTCString()clock in UTC (as in europe day, month, year)

todayDate.toLocaleDateString()returns a date to a localised representation (look at your computer)

todayDate.toLocaleString()returns a date to a localised representation

get Methods

todayDate.getTime() Miliseconds since 1.1.1970 0:00

todayDate.getFullYear()gives us the number of the year

todayDate.getMonth()gives us the INDEX of the month (January:0)

todayDate.getDate()gives us the number of the day

todayDate.getDay()gives us the INDEX of the day (Sunday:0)

todayDate.getHours()gives us the number of the hour

todayDate.getSecunde()gives us the number of the seconds

todayDate.getMilliseconds()gives us the number of the milisecondsr

set Methods - change Date

todayDate.setTime(number)

todayDate.setFullYear(number)

todayDate.setMonth(index)

todayDate.setDate(number)

todayDate.setDay(index)

todayDate.setHours(number)

todayDate.setSecunde(number)

todayDate.setMilliseconds(number)

Get the name of the day of the week

const date = new Date();

let weekDayLong = date.toLocaleString("default", { weekday: "long" });// Tuesday

let weekDayOneLetter = dayObj.toLocaleString("default", { weekday: "short" });// Tu

let weekDayOneLetter = dayObj.toLocaleString("default", { weekday: "narrow" });// T

Control flow

Conditional statements

if / (else if) else

With an if statement we can control whether a part of our code is executed or not, based on a condition.

The else block is executed only if the condition is false.


if (hour < 12) {
console.log("Good Morning.");
} else if (hour < 18) {
console.log("Good afternoon.");
} else if (hour === 24) {
console.log("Good night.");
} else {
console.log("Good evening.");
}

If the condition is not a boolean, it is converted into one by type coercion. This can be used to check whether a value is not 0 or an empty string:


const name = "Alex";
if (name) {
console.log("Hi " + name + "!");
} // only executed if name is not an empty string

Ternary Operator ? :

With if / else statements whole blocks of code can be controlled. The ternary operator can be used if you want to decide between two expressions, e.g. which value should be stored in a variable:


condition ? expressionIfTrue : expressionIfFalse;

->If the condition is true, the first expression is evaluated, otherwise the second expression.


We ALWAYS need an alternative value to true, no matter which: undefined, null, ""

const greetingText = time < 12 ? "Good morning." : "Good afternoon.";
And other example:
let number = 4;
function isEven(number) {
if (number % 2 === 0) {
return true;
} else {
return false;
}
}

let resultA = isEven(number)
? `The number ${number} is even`
: `The number ${number} is odd`;

switch

The "break" statement is necessary in a switch-case statement to exit the switch block after executing the code for a matched case, preventing the execution of subsequent case statements and ensuring that only one case is executed.

We can not put conditions inside switch(), becouse dont work, we need to use a boolean. Only inside the case works the condition

let number = 100;

switch (true) {
case number > 50:
console.log("Number is bigger to 50");
break;
case number < 50:
console.log("Number is small as 50");
break;
default:
console.log("Number is 50");
break;
}

With strings:

let acount = "Admin" switch (account) {
case "Admin":
case "Moderator":
console.log("You have all the privileges.");
break;
case "User":
console.log("As a user, you can read and comment on posts. Limited rights.");
break;
default:
console.log("Please log in.");
}

Loops

A loop executes a respective block of code over and over again until an end criteria is met. In JavaScript, two basic types of loops exist:

while while loop: are used when a task needs to be executed until a specific criteria is met.

let string = "a";

while (string.length <= 8) {

console.log(string);

string = string + string;

}


for loop: are commonly used when a given task needs to be executed x times or for each element in an object / array

types of for loops

for

for loops are intended for repeating a task a certain number of times. They consist of three internal parts:

- an internal counter which is increased / decreased every iteration.

- a criteria which checks the value of the counter. As long as the criteria is true, the loop is executed.

- a rule how the counter is increased / decreased (in most cases it is increased by 1)

for (let counter = 0; counter < 4; counter++) {

console.log(counter);

}

Output(each number on a new line) // 0; 1; 2; 3;

for...in

The for...in is a shorthand notation to loop through all keys of an object:

const user = {

name: "Joe",

age: 28,

email: "joe@mail.com",

};


for (const key in user) {

console.log(user[key]);

}


Output(each string on a new line) // 'Joe'; 28; 'joe@mail.com'

for...of

Similar to for...in the for...of loop is a shorthand notation, but for looping through all items of an array.

const fruits = ["apple", "banana", "melon"];

for (const fruit of fruits) {

console.log(fruit);

}


Output (each string on a new line)// 'apple'; 'banana'; 'melon'

Functions

They contain a set of statements - in other words: They contain JavaScript code. Functions have to be defined. When a function is defined it can be called an arbitrary number of times.

Declarationa and expression

Function Declarations

Declaration which consists of:
-the function keyword
-the function name
-the function body (JavaScript statements / JavaScript code)

function greet() {
console.log("Hi Friends!");
console.log("Nice to be here.");
}

Function Parameters

Functions can accept parameters. Parameters can be used like predefined variables inside the function body. When declaring a function we are free to choose a name for the parameters, but descriptive, short names should be chosen.

function printLetter(name) {
console.log("Hi " + name + ", hope you are fine. Love, Johnny");
}


function printSum(first, second, third) {
const sum = first + second + third;
console.log("The sum of your numbers is: " + sum);
}

Function Calls

When functions are defined you can call them by writing their name, followed by parentheses ("round brackets").

greet();

printLetter("Max");
printLetter("Jordan");


printSum(1, 2, 3);
printSum(3, 4, 5);

Return Statements

A function can return a value back to the place where it was called. This is done via a return statement. A function can return only one expression value, but can have multiple return statements, in combination with if else statements for

function add3Numbers(first, second, third)
{ const sum = first + second + third;
return sum;
}
const firstSum = add3Numbers(1, 2, 3); // the return value is stored in "firstSum", namely 6


function checkInputLength(inputString) {
if (inputString.length > 3) {
return true;
} else {
return false;
}
}

Early Return Statements

As soon as a return statement is reached in a function call, the function execution is ended.This behavior can be used to our advantage as early return statements (A return statement can be left empty, the returned value is then undefined).

function setBackgroundColor(color) {
// first condition
if(typeOf color !== 'String') {
return;
}
// second condition
if(!color.startsWith('#')) {
return;
}
// only if all 2 conditions are passed the final line of code is executed.
body.style.backgroundColor = color;
}

Arrow Function Expression

const addNumbers = (first, second) => {
return first + second;
};

Implicit Return Statements

The advantage of arrow functions are possible shorter notations when certain criteria apply

  • 1.-Omit the round brackets around the parameters: This is possible, if there is only one input

    const addOne = (number) => {
    return number + 1;
    };

    can be rewrite as:

    const addOne = number => number + 1;
  • 2.-Implicit return statements: If the function consists only of a return statement, the curly brackets and the return keyword can be omitted

    const addNumbers = (first, second) => {
    return first + second; };

    can be rewrite as:

    const addNumbers = (first, second) => first + second;

Call Back Functions

A callback function is a function that is passed as an argument into another function.

The outer function can execute this for: when an event is triggered,when the fetched data arrived on your computer, for each element in an array

button.addEventListener("click", () => {

console.log("Inside the callback function.");

});


1.- outer function(Higher Order Functions): addEventListener()

2.- first argument: 'click'

3.- second argument: callback function:

() => {

console.log("Inside the callback function.");

};

This type of function is called anonymous function, since it is declared without giving it a name

Higher Order Functions: .then,.forEach,.map,.filter

A callback function can accept parameters:

button.addEventListener("click", (event) => {

console.log("This button was clicked:", event.target);

});

OPP - CONSTRUCTOR & THIS

Through object-oriented programming (OOP) we can create templates, for example for data. Constructors are super important to use, so we make sure that we always build our objects in the same form/type. We create a template, so to speak, which we then only have to fill with parameters.

Constructor: Function Version

function Person(nameParameter, ageParameter){

this.name = nameParameter;

this.age = ageParameter;

};

const personA = new Person("Jane", 23)

Constructor: Class Version

class Person {

constructor(name, age) {

this.name = name;

this.age = age;

}

greet(){

console.log(`Hello, I am ${this.name} and i am ${this.age} years old`);

}

}

const personB = new Person ("Jake", 33)

personB.greet() // Hello, I am Jake and i am 33 years old

Class Inheritance with extend & super

we want a new constructor that actually has the same parameters as the person plus a new one:

class Student extends Person {

constructor(name, age, schoolClass) {

super(name, age);

this.schoolClass = schoolClass;

}

}

const student = new Student("Tim", 10, 4);

Setter method to check properties before writing it

class Client {

constructor(name, age) {

this.name = name;

this.setAge(age);

}

setAge(age) {

if (typeof age === "number") {

this.age = age;

} else {

this.age = "WRONG VALUE";

}

}

descrition() {

console.log(`${this.name} is ${this.age} years old`);

}

setName(newName) {

this.name = newName;

}

}

In this way if data is entered into our database we can check if it is correct.

const client1 = new Client("Jade", "55");

const client2 = new Client("Jude", 22);

client1.descrition()// Jade is WRONG VALUE years old

client1.descrition()// Jude is 22 years old

With the set method in the constructor we can also change values that already exist.

client2.setName("Julia")

client2.descrition()// Julia is 22 years old

IMPORTANT: It is NOT recommended to use dot notation (client2.name = "Julia") to change values. Set-methods should always be used

Objects and arrays

Arrays

  • Arrays aren't primitives but are instead Array objects
  • Arrays are resizable and can contain a mix of different data types
  • JavaScript arrays are zero-indexed.
  • Last element is at the value of the array's array.length property minus 1.

Access and change elements of one Array

let array =["a", "b", "c"];

console.log(array[1])// "b";

array[2]="d";

console.log(array);// ["a", "b", "d"]

Important Array Methods

forEach

The array method forEach executes some logic for each element within an array.

const pets = ["bird", "cat", "dog", "fish"];

pets.forEach((pet) => {

const petElement = document.createElement("p");

petElement.textContent = pet;

document.body.append(petElement);

});

map

The array method map is used to apply a transformation to each element of an array. The transformed elements are stored in the newly created array returned by map. The elements in the original array are not being altered.The created and the original array have the same length.

const pets = ["bird", "cat", "dog", "fish"];

const uppercasePets = pets.map((pet) => {

return pet.toUpperCase();

});

console.log(uppercasePets);

// ['BIRD', 'CAT', 'DOG', 'FISH']

The callback function provided to map must use a return statement to return a transformed element. map returns a new array.

You should not use map to trigger a side-effect, like document.createElement

filter

The array method filter is used to create a new array with a subset of the elements of the original array. The callback function returns a boolean value to define, if an element is being included in the resulting array or not. The original array is not being altered

const pets = ["bird", "cat", "dog", "ferret", "fish"];

const petsWithF = pets.filter((pet) => {

return pet.startsWith("f");

});

console.log(petsWithF);

// ['fish']

The callback function provided to filter must use a return statement to return a boolean value.

Chaining array methods

Often times you need to combine multiple array methods to achieve a desired result. Array methods like map and filter, that return a new array, can be chained.

Instead of storing each array in a separated variable, the methods can be called directly after another. This reduces the amount of code and improves readable.

const pets = ["bird", "cat", "dog", "ferret", "fish"];

const uppercasePetsWithF = pets

.filter((pet) => {

return pet.startsWith("f");

})

.map((pet) => {

return pet.toUpperCase();

});

console.log(uppercasePetsWithF);

// ['FERRET', 'FISH']

document.querySelectorAll

The NodeList returned by document.querySelectorAll is an array-like object. You can use the forEach method to iterate over the DOM elements.

const pets = document.querySelectorAll('[data-js="pet"]');

pets.forEach((pet) => {

pet.addEventListener("click", () => {

// [...]

});

});

A NodeList is not an array! Other array methods like map or filtercan't be used. If you need to use array methods, you can convert the NodeList to an array using Array.from()

includes

Use array.includes() to check whether the array contains the specified value. If it does, true is returned, otherwise false.

const colors = ["red", "yellow", "green"];

colors.includes("yellow"); // true

colors.includes("blue"); // false

find and findIndex

Use find() to receive the first element of the array that satisfies the provided testing function. Otherwise, it returns undefined.

const colors = ["red", "yellow", "green"];

colors.find((color) => color.startsWith("g")); // 'green'

colors.find((color) => color.startsWith("b")); // undefined

Use findIndex() to receive the index of the first element of the array that satisfies the provided testing function. If there is no such element, -1 is returned.

const colors = ["red", "yellow", "green"];

colors.findIndex((color) => color.startsWith("y")); // 1

colors.findIndex((color) => color.startsWith("b")); // -1

sort and reverse

Use sort() to sort the elements of an array. You need to provide a callback function in order to tell how the array is sorted.

sort() converts the elements into strings, then compares their sequences of UTF-16 Code units values. This is why array.sort() without a callback is mostly useless.

const numbers = [4, 42, 23, 1];

numbers.sort((a, b) => a - b);

// [1, 4, 23, 42]

numbers.sort((a, b) => b - a);

// [42, 23, 4, 1]

sort() two things inside of the callback function with strings:

1.- lowercase both strings before comparing them (uppercase works as well)

2.- using if-statements, be explicit about the return values dependent on the result of the comparison (nameA < nameB and nameA > nameB)

(In UTF-16, the upper- and lowercase version of the same letter do not have the same value)

const strings = ["Ana", "Peter", "Gaia"];

strings.sort((a, b) => {

const nameA = a.toLowerCase();

const nameB = b.toLowerCase();

if (nameA < nameB) {

return -1;

}

if (nameA > nameB) {

return 1;

}

return 0;

});

console.log(strings);

// ['Ana', 'Gaia', 'Peter']

use reverseto reverse an array, simply use array.reverse(). This can be combined with sort() as well:

const numbers = [4, 42, 23, 1];

const reversedNumbers = numbers.reverse();

// [1, 23, 42, 4]

slice

It's important to note that some array methods, as sort(), do not create a new array, but mutate the original one.

This behaviour will often cause errors. To prevent it, just make a copy of the original array using slice().

const numbers = [4, 42, 23, 1];

console.log(numbers); // [4, 42, 23, 1]

const sortedNumbers = numbers.slice().sort((a, b) => a - b);

console.log(sortedNumbers); // [1, 4, 23, 42]

console.log(numbers); // [4, 42, 23,

some and every

Use some() to test whether at least one element in the array passes the provided test.

const colors = ["red", "yellow", "green"];

colors.some((color) => color.startsWith("g")); // true

colors.some((color) => color.startsWith("i")); // false

In order to check if all elements pass the test, use every().

const colors = ["red", "yellow", "green"];

colors.every((color) => color.length > 2); // true

colors.every((color) => color.length < 1); // false

reduce

is an array method to reduce a list of values into a single value.

It's main use case is to calculate the sum of an array of numbers.

starting from the beginning, it executes the callback function on each element of the array, the return value of each calculation is passed to the next calculation (i.e. it becomes the new starting value for the next iteration through the array)the final result is a single value.

const numbers = [4, 42, 23, 1];

numbers.reduce((a, b) => a + b);

console.log(numbers); // 70

How to use the Methods and propierties

const array = ["a", "b", "c", "d"]

console.log(array.length);

// 4 //Output is a number. first element is 1 // length is a property

console.log(array.indexOf("d"));

// 3 //Output is a number. first Index is 0

console.log(array.push("e", "f"));

//["a", "b", "c", "d", "e", "f"]// add behind

console.log(array.pop())

//["a", "b", "c", "d", "e"]// Remove the last one and you can save it in one variable

console.log(array.shift())

//["b", "c", "d", "e"]// Remove before and you can save it in one variable

console.log(array.unshift("x", "y"))

//["x", "y", "b", "c", "d", "e"]// Add before

const text = "Hello JS friends"
console.log(text.split())

//["Hello JS friends"]//Convert to Array

console.log(text.split(""))

//['H', 'e', 'l', 'l', 'o', ' ', 'J', 'S', ' ', 'f', 'r', 'i', 'e', 'n', 'd', 's']//Every single element, space too

console.log(text.split(" "))

// ['Hello', 'JS', 'friends']//Separate where space are

console.log(text.split(" ").join(" "))

//"Hello JS friends"//Add the elements with (join) with one space (" ") between

console.log(text.split(" ")

const newArray = ["m", "n", "o", "p", "q"]
console.log(newArray.slice(1, 3))

//['n', 'o']//

first index is included, last one NO
const otherArray = ["i", "j", "k", "l"]
console.log(otherArray.splice(1, 2, "newItem"));

//['j', 'k'] // array.splice(startIndex, deleteCount(how many), item1, ...) we obtain the rest of the array. After that our Array is changed

console.log(otherArray)

//['i', 'newItem', 'l']

const numbersArray= [1, 5, 6, 3, 12]
console.log(numbersArray.reverse())

//[12, 3, 6, 5, 1]

console.log(numbersArray.sort())

//[1, 12, 3, 5, 6]//ASCII sort//Change original Array, recomended to use .slice() before .sort to do a copy

console.log(numbersArray.sort((a, b) => a - b))

// [1, 3, 5, 6, 12] //Change original Array, recomended to use .slice() before .sort to do a copy

Ascendet
console.log(numbersArray.sort((a, b) => b - a))

//[12, 6, 5, 3, 1] //Change original Array, recomended to use .slice() before .sort to do a copy

Descendent
const fruits = ["apple", "banana", "kiwi"];
fruits.forEach((fruit) => console.log(fruit))

//apple; banana; kiwi;// All items in new lines

const oddNumbers = [3, 5, 7]
let evenNumbers = oddNumbers.map((element)=> element+1); console.log(evenNumbers)

//[4, 6, 8]//

Return a Value and a new Array
Other Important: .include(); .filter(); .every(); .find(); .reduce();

3 ways to remove duplicates

const chars = ['A', 'B', 'A', 'C', 'B'];

Use Set

const uniqueChars = [...new Set(chars)];

console.log(uniqueChars);// Output: ['A', 'B', 'C']

Using the indexOf() and filter() methods

The indexOf() method returns the index of the first occurrence of the element in the array. The duplicate element is the element whose index is different from its indexOf() value. To eliminate duplicates, the filter() method is used to include only the elements whose indexes match their indexOf values, since we know that the filer method returns a new array based on the operations performed on it:

const uniqueChars = chars.filter((element, index) => {

return chars.indexOf(element) === index;

});

console.log(uniqueChars);//Output: ['A', 'B', 'C']

And if by chance we need the duplicates, we can modify our function a little, just by changing our rule:

const dupChars = chars.filter((element, index) => {

return chars.indexOf(element) !== index;

});

console.log(dupChars);//Output: ['A', 'B']

Using the includes() and forEach() methods

The include() function returns true if an element is in an array or false if it is not. The following example iterates over the elements of an array and adds to a new array only the elements that are not already there

const uniqueChars = [];

chars.forEach((element) => {

if (!uniqueChars.includes(element)) {

uniqueChars.push(element);

}

});

console.log(uniqueChars);//Output: ['A', 'B', 'C']

Objects

Objects are a central powerful function/data type in JS.Objects are a structured data type, which couple their values not to an index, but to a unique key.You can declare an object using {} curly brackets (object literals):

Create objects

We can have all types of values types.

keys can be written as in this example or as strings ("name":"Jane"), being strings we can only access them with the bracket notation (see below).

const person = {

name: "Jane",

age: 42,

admin: true,

hobbies:["code", "cinema", "walking"]

socialMedia:{

linkedin: "..."

gitHub: "..."

},

city: "Barcelona",

greet: function() {

console.log(`Hola, I am ${this.name}`)

add: (a,b)=> a+b,

};

Accessing, Change Values, Delete and Functions use

We can use the dot notation & the bracket notation to access to our Object. With the above person object, we habe the same value:

console.log(person.name)

console.log(person["name"])

Wenn keys are strings ("name", "city", etc). We can only use bracket notation person["name"]

Rewrite Values:

person.age = 43

Add neu Value & Key:

person.job="Developer"

Delete:

delete person.job;

Functions:

person.greet()//Hola, I am Jane` console.log(person.add(1,2))// 3

Dynamical with this

We can give the greet function to other object and thiswill refer to the new object

To use this we need allway a traditional function declaration, dont work with arrow funtion

person.greet()//Hola, I am Jane

let person2 = {

name: "John",

age : "23",

};

person2.greet = person.greet;

person2.greet();//Hola, I am John

Use funtions in Objects

const doThings = {

add: (a, b) => a + b,

sub: (a, b) => a - b,

write: (a, b) => `Hello my name is ${a} and i am ${b} old`,

},

console.log(doThings.add(2, 4));

console.log(doThings.sub(10, 1));

console.log(doThings.write("Jude", person.age))

Work with keys, values and loops

With for...in loop we can extract the keys and the values together

for (let key in person) {

console.log(person[key]) //Only works with [..] notation, not dot notation

console.log(key + ": " + person[key]);

Interleaved arrays in objects and viceversa.

Especially arrays of objects are very often used when working with databases. [{},{},{}]

Objects in objects are also very frequent.{{},{},{}}

We cann access combining dot and bracket notation

console.log(person.socialMedia.gitHub)// ist the same as

console.log(person.socialMedia["gitHub"]

Or: let sozialValues = Object.values(person.socialMedia)

sozialValues[1]

Loops with Objects

If we habe a Arry of Objects we can use map or forEach

const people = [ {name:"Jane", city:"Barcelona"}, {name:"Joe", city:"Berlin"}, {name:"Jude", city:"Lima"} ]

people.forEach((person) => {

const card = `<div style="background-color:red"></div>`

<h2 style="margin:1rem;">${people.name}</h2>

<h2 style="margin:1rem;">${people.city}</h2>

</div> `;

document.body.insertAdjacentHTML("afterbegin", card);

});

Methods in Objects: Transform Object to Array to use Array methods (map, filter, sort, etc)

Extract keys too with: Object.keys(person)

You can use Object.keys().forEach():

Object.keys(person).forEach((key) => {

console.log(key + ": " + person[key]);

});

Extract values too with Object.values(person)

Extract entries with const entries = Object.entries(person)

We have now an Array of Arrays and we can access to values like:

console.log(entries[3][1])//cinema

Transform a Array of entries in a Object Object.fromEntries()

Other Methods

Object.hasOwnProperty()//check if the key exist, answer is a Boolean

Object.freeze()Prevents the properties of an object from being modified.

Object.isFrozen()answer is a Boolean

Object.seal()Prevents adding or deleting properties of an object, but allows modifying existing properties.

Object.isSeal()answer is a Boolean

Object.assign(objectA, objectB)Copies the properties of one object to another object.

Object.getOwnPropertyNames()Returns an array of all properties of an object, including non-enumerable properties.

IMPORTANT:Object.is(valueA, valueB)compares two values and determines whether they are strictly equal. Answer is a Boolean

Object.is() is especially useful for avoiding pitfalls that can arise with JavaScript equality comparison, such as comparing NaN to NaN or comparing 0 to -0.

Object.is(5, 5); // true

Object.is(5, "5"); // false

Object.is(NaN, NaN); // true

Object.is(0, -0); // false

Object.is(null, null); // true

Scope and closures

Scope

The scope defines where variables are visible and where they can be referenced: global scope vs function scope

Function scope

Function scope

-> Variables defined inside a function are not accessible from outside. But all variables outside of the function can be accessed from inside the function body

function myFunction() {
const localVariable = true;
console.log(localVariable);
}
myFunction() // true console.log(localVariable); // Error! Variable not available outside of function

Global scope

Global scope

-> A variable is in the global scope when it is declared outside of any function, in a JavaScript file. Global variables are visible and can be accessed from anywhere in that JavaScript file after declaration.

Closures

Event handling

Events

The event Object and event.target. The event object is created whenever an event is triggered.

More info about Event

Events listener

.addEventListener()

The method addEventListener is used to react to events. There different events you can listen to:

List

First you specify the kind of event, e.g. click, then you define what code should be executed when the event is triggered. You write that code between the {} brackets, e.g. a console.log.

const button = document.querySelector('[data-js="button"]'); button.addEventListener("click", () => { console.log("Yeah"); });

Asynchronous programing

Asynchronous Code

Asynchronous code is code that runs in the background. This is useful for tasks that can take a long time to complete, but don't need to block the main thread. JavaScript is a single-threaded language, meaning that only one thing can happen at a time. Examples of asynchronous code include: network requests, file system access, animations and timers.

Promises

A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation, and its resulting value. Most of the time it is returned by a function that performs an asynchronous operation.

The Promise object has the following properties and methods:

state the state of the Promise object, can be "pending", "resolved" or "rejected"

result the result of the asynchronous operation (you'll almost never need to access this directly)

then()a method that takes a callback function that will be called when the asynchronous operation is complete

catch()a method that takes a callback function that will be called when the asynchronous operation fails

finally()a method that takes a callback function that will be called when the asynchronous operation is complete, regardless of whether it was successful or not

functionThatReturnsAPromise().then((value) => {

console.log(value);

});

Promises are almost always created for you by other asynchronous APIs, only rarely do you create them yourself. If you create a Promise yourself (new Promise()), you either know exactly what you are doing, or you are probably doing something wrong.

Some examples of asynchronous browser APIs that return a Promise:

fetch()— makes an HTTP request and returns a Promise that resolves to a Response object

element.animate().finished — animates an element and returns a Promise that resolves when the animation is complete

navigator.getBattery() — gets the current battery level and returns a Promise that resolves with the battery level

Asnyc Functions and Async/await

Using the await keyword, you can write asynchronous code that looks synchronous. Any function can be prefixed with the async keyword:

async function myAsyncFunction() {} const myAsyncArrowFunction = async () => {}

Inside an async function, you can use the await keyword to wait for a Promise to be resolved:

async function myAsyncFunction() {/p>

const value = await functionThatReturnsAPromise();

console.log(value);

}

This is can be easier to read than the Promise syntax especially when you have multiple asynchronous operations that depend on each other.

async function myAsyncFunction() {

const value1 = await functionThatReturnsAPromise1();

const value2 = await functionThatReturnsAPromise2(value1);

const value3 = await functionThatReturnsAPromise3(value2);

console.log(value3);

}

Compared to (avoid doing this):

function myFunction() {

functionThatReturnsAPromise1()

.then((value1) => {

return functionThatReturnsAPromise2(value1);

})

.then((value2) => {

return functionThatReturnsAPromise3(value2);

})

.then((value3) => {

console.log(value3);

});

}

Async functions always return a Promise. If the function returns a value, the Promise will be resolved with that value. Even if you're not using the return keyword the function will return a Promise that resolves to undefined when it reaches the end of it's scope. If the function throws an error, the Promise will be rejected with that error.

Handling Errors

try/catch

When using Promises, you can use the catch() method to handle errors. Using async functions, you can use the try/catch syntax.

async function myAsyncFunction() {

try {

const value = await functionThatReturnsAPromise();

console.log(value);

} catch (error) {

console.error(error);

}

}

The try block will be executed, and if an error is thrown, the catch block will be executed. The catch block has access to the error that was thrown. If any error is thrown in the try block, the catch block will be executed immediately aborting the execution of any further code in the try block:

finally

You can also use a finally block after any try block to run code after the try block is resolved, regardless of whether it was successful or not:

async function myAsyncFunction() {

try {

const value = await functionThatReturnsAPromise();

console.log(value);

} catch (error) {

console.error(error);

} finally {

console.log("done");

}

}

IMPORTANT: The try/catch/(finally) syntax is not limited to async functions, you can use it with any JavaScript code that might throw an error.

Parallel Promises: Promise.all()

If you have multiple asynchronous operations that you want to run in parallel, you can use Promise.all(). It takes an array of Promises and returns a Promise that resolves to an array of the results of the Promises in the same order.

async function myAsyncFunction() {

try {

const values = await Promise.all([

functionThatReturnsAPromise1(),

functionThatReturnsAPromise2(),

functionThatReturnsAPromise3(),

]);

console.log(values); // [value1, value2, value3]

} catch (error) {

console.error(error);

}

}

This will run all three asynchronous operations in parallel, and wait for all of them to complete before continuing. If any of the asynchronous operations fail, Promise.all() will fail as well.

There are advanced use cases where you might want to control when to resolve or reject a Promise. Promise.allSettled() (does not care if Promises are rejected or resolved) and Promise.any() (resolves once the first Promise is resolved) are two methods that allow you to do that.

DOM manipulation

The Document Object Model is a representation of the HTML document. Each HTML Tag is modelled as a node in a tree structure, which shows how HTML elements are nested. A computer program such as your JavaScript file can access and manipulate the HTML website by changing the DOM via the document object.

Selecting elements

.querySelector()

Before we can add interactivity, we need to select the necessary HTML-Elements. A good practice is to use a data-* attribute, like that:

const mainElement = document.querySelector('[data-js="main"]');

Other css selectors work as well, but the data-* attribute selectors should be preferred.

const mainElement = document.querySelector("main");
->tag as identifier
>const mainElement = document.querySelector(".main");
->class as identifier
const mainElement = document.querySelector("#main");
-> id as identifier

Other posibilities:

querySelector('elementA + elementB');
querySelector('elementA ~ elementB');
querySelector('[attribut="value"]');
querySelector(':pseudo');
querySelector('::pseudo-element');
querySelector(':pseudo');

Other methods to select:

.querySelectorAll() allows you to find and select multiple elements in the DOM based on these selectors

querySelectorAll('element')
->Selects all elements of the specified type.

querySelectorAll('.classname')
-> Selects all elements with the specified CSS class.

querySelectorAll('#id')
-> Selects the element with the specified ID (Note: IDs should be unique)

querySelectorAll('[attribute="value"]')
-> Selects all elements with the specified attribute and value.

querySelectorAll(':pseudo-class')
-> Selects all elements that match the specified pseudo-class, like :hover or :nth-child(n).

querySelectorAll('element.classname')
->to select all elements of a specific type with a certain class.

getElementById
-> Selects an element by its unique ID attribute.

getElementsByClassName
-> Selects elements by their class attribute.

getElementsByTagName
-> Selects elements by their HTML tag name.

getElementsByName
-> Selects elements by their name attribute.

Modifiying and use content

You can access in the console to all the propierties of the element:

const mainElement = document.querySelector("#main");

console.log(mainElement.innerHTML)

console.log(mainElement.clientWidth)

console.log(mainElement.baseURI)

Output in HTML

document.write

document.write("Hallo People!") -> Add text at the END of our HTML

document.write("<h2 class="test" style="color: red">Warning</h2>")

POP UPS

window.prompt() ->text with input (and placeholder). e.g:
let windowInput = window.prompt("What´s is yout name?", "please, write here your name")
window.confirm()-> text with 2 buttons yes/no
let confirmStatus = window.confirm("Agree to the cookies?);
// console.log(confirmStatus);

innerHTML

const content = document.querySelector("h1")
content.innerHTML += "Hello World!"

//-> With += add a the new text to the old.


//-> With = rewrite all the content !!.

const container = document.querySelector(".container")
container.innerHTML += "<h3> This is a new headline </h3>";

//-> Add new HTML Elements

Inputs & Forms

Inputs

Every input field in HTML holds a value in form of a string. You can access the value by using .value on.You can also change the value of the input by assigning a new value to this input property:

const textInput = document.querySelector('[data-js="textInput"]');
textInput.value = "changed value!";

This change is immediately visible on the website.

For example, you can enforce all uppercase letters in a form by combining this functionality with an input event listener on the input element:

// transform on every change the input value to uppercase letters

textInput.addEventListener("input", () => { const oldValue = textInput.value;
const newValue = oldValue.toUpperCase();
textInput.value = newValue;
});

Forms

To prevent the default behavior we can use:

  • in HTML form we need ; return false
  • <form onsubmit="function(); return false"></form>
  • Or in JS use .preventDefault()
  • const form = document.querySelector('[data-js="form"]');

    form.addEventListener("submit", (event) => {
    event.preventDefault(); console.log(event.target);
    });

The event Object and event.target

The event Object and event.target. The event object is created whenever an event is triggered.

event.target is a reference to the element to which the event originated from - in this case - the form.

More info about Event

Accessing Interactive Fields: event.target.elements and the name Attribute

While event.target represents the entire form, event.target.elements is a collection of all form elements (form fields, field sets and buttons.You get access to a specific form field via its name (<input name="firstName"/>) attribute and dot notation:

form.addEventListener("submit", (event) => {
event.preventDefault();
const formElements = event.target.elements;
console.log(formElements.firstName);
console.log(formElements.firstName.value);
});

Using Input Values

You can access all input values of the form by using FormData(). This constructor uses event.target and can be transformed into a usable object afterwards:

form.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
console.log(data);
});

Exception: Reading Values from Checkboxes

You can access the checkbox's state via the .checked property instead.

console.log(formElements.colorBlue.checked);
// output: true or false
console.log(formElements.colorBlue.value);
// output (always): blue

The input Event

The input event is fired every time when the value of a form field has been changed. For example, a <textarea /> will fire this event with every keystroke.

const messageField = document.querySelector('[data-js="message"]');

messageField.addEventListener("input", (event) => {
console.log(event.target.value);
});

Don't confuse the input event with the change event, which is only fired after a field's content has been committed by the user by pressing enter or moving the focus to the next field.

Focus Input Fields .focus()

This can be used to improve the user experience after submitting a form

messageField.focus();

Instead of querying the input element using querySelector, it can also be obtained via the event.target.elements collection:

event.target.elements.message.focus();

Resetting Forms .reset()

form.addEventListener("submit", (event) => {
event.preventDefault();
// [...] handle form data
event.target.reset();
});

This often comes in handy in combination with .focus(). After the message was send, the input field is cleared and re-focussed, so users can write the next message.

Change style

.style

Use .style and the css propierty in without - in camelCase;

const mainElement = document.querySelector("#main");

mainElement.style.backgroundColor = "yellow";

mainElement.style.fontSize = "20px"

.classList

Add/remove & toggle classes:

const main = document.querySelector('[data-js="main"]');

const button = document.querySelector('[data-js="button"]');

button.addEventListener("click", () => {
main.classList.add("page--primary"); });

Toggle:

main.classList.toggle("page--primary");

Remove:

main.classList.remove("page--primary");

HTML collection & NodeList

Html collection

An HTML collection is an object-like array of HTML element nodes. It is used in JS to access a group of elements in the DOM = (Document Object Model).

- HTMLCollections are live! As soon as the HTML changes, so does the collection.

- index based - The first element is 0

- Often used with document.getElementByClassName(), document.getElementByTagName() or elment.children. (there we get a collection back)

How to get access to the individual HTML elements, we have a "form" in Html with the name="loginForm", and 2 input with the names "username" & "email":

let myForm = document.forms.loginForm;

console.log(myForm.username);

console.log(myForm.username.value);

console.log(myForm.email.value);

Html collection vs NodeList

A NodeList is a collection of nodes, therefore Nodes. Unlike HTML Collection, Nodelist can contain all kinds of DOM nodes.

Nodes can be static or live.

- index based - the first element is 0

NodeList supports more methods than HTML Collections. For example, you can use forEach methods to loop through the list.

NodeList are often received as return values from DOM methods like document.quarySelectorAll() or Node.childeNodes.

with the querySelectortAll() we usually get back a NodeList!

const liNodelist = document.querySelectorAll("li");

with the getElementByTagName we usually get back an HTML Collection.

const liHTMLCollection = document.getElementsByTagName("li");

Extract Child elements (example):

console.log(document.body.children[0])

console.log(document.body.children[0].children)

console.log(document.body.children[1].children[2])

Add a child element:

document.body.children[0].innerHTML += "<p> I am new here </p>"

Change Style:

document.body.children[0].children[0].style.color = "red";

Navigation with firstElementChild and lastElementChild

console.log(document.body.firstElementChild);//first element of my HTML console.log(document.body.lastElementChild);//last element of my HTML console.log(document.body.children[0].lastElementChild);// This is how you can move through Html with these keys

Creating Elements

We can create and add elements with:

  • .createElement and .appendChild or .append

    let div = document.createElement("div");

    div.textContent = "I am a new Div"

    div.style.height = "100px";

    document.body.appendChild(div);

    const article = document.createElement("article");

    const button = document.createElement("button");

    document.body.append(article); // placing the created article at the end of the body

    article.append(button); // placing the created button into the article

    We can create and add elements dynamically with forEach

    let socialMedia = ["meta", "tw", "insta"];

    socialMedia.forEach((elt) => {

    let listItem = document.createElement("li");

    listItem.textContent = elt;

    document.body.firstElementChild.appendChild(listItem);

    });

    We can add attribute with .setAttribute

    let image = document.createElement("img")

    image.setAttribute("src", "https://..........")

    image.setAttribute("alt", "alt text")

    image.setAttribute("class", "img-class")

    You can also give it the values, propiertiers and methods directly if the element exists. You can assign HTML attributes by using the element properties

    Common Element Properties and Methods are: classList,textContent,stylehiddenfocus() hasAttribute(), querySelector()

    let image = document.createElement("img")

    img.src = "https://..........";

    img.alt = "alt text";

    img.className = "img-class"

    button.textContent = "Click me!";

  • adding the element/tag with .innerHTML +=``

    document.body.innerHTML += '<img class="img-class" src="https:......" alt="test">';
  • adding the element/tag with .insertAdjacentHTML("location", newElement).

    the new element will be the first one after body:

    let html=`<p> I am new here </p>`; document.body.insertAdjacentHTML("afterbegin", html);

    Other locations possibilities:

    beforebeginn, afterbeginn ,beforeend, afterend

BOM & Window Object

the window object represents the opened window in the browser. JS window accesses the browser window with, among other things, width / height of the viewport, events and timers. Important Informations of window:

console.log(window)// Everything

console.log("innerHeight", window.innerHeight)// Value in px, Browser line and devtools are NOT included

console.log("innerWidth", window.innerWidth)//Value in px, Browser line and devtools are NOT included

console.log("outerHeight", window.outerHeight)//Value in px, Browser line and devtools are included

console.log("outerWidth", window.outerWidth)//Value in px, Browser line and devtools are included

console.log(window.location)//the URL - address of the current page

console.log(window.location.host)//this is how we get the domain back

console.log(window.location.pathname)//this is how we get the path back

Screen Object

The Screen Object is located in the Window Object. It gives us information about the screen/monitor on which the browser window is open.

console.log(window.screen);//

console.log("screen height", window.screen.height)//get the height back in px-> total height with taskbar

console.log("screenWidth", window.screen.width)//get the width back in px-> total width with taskbar

console.log("availHeight", window.screen.availHeight)//we get back the height of the screen WITHOUT the taskbar, i.e. only the usable area

console.log("availWidth", window.screen.availWidth)//we get back the width of the screen -> only the usable area

Window Methods

window.alert("Message")Popup containing a text

let promtAnswer=window.promt()Opens a popup where the user can enter something, to access the text input we need to store it in a variable

window.open("url", "_self"/"_blank")With window.open(url) we can redirect to another page. Attention: we can no longer click on the back arrow. By default, the page is always opened in a new tab. History will be deleted

window.onload = console.log("The page was loaded")when the page has been completely loaded, then something should happen

window.scrollTo(X,Y)koennen wir zu einem bestimmten Punkt auf der Seite scrollen (See if the html or elements are long enough to the point you choose -check your css).

window.location.reload()with this we can reload the current page

window.loacation.replace("url")we can redirect to another URL. History will be no deleted like in window.open()

window.historyFor back buttons. For example:

backButton.addEventListener("click", () => {

history.back();

});

TIME Methods in the Window Object

Window Object allows the execution of code at specific time intervals.the two main methods we use in JS are:

setTimeout(function, milliseconds)executes a function after waiting a specified number of milliseconds

let interval = setInterval(function, milliseconds)same as setTimeout(), but repeats the execution of the function continuously.

clearInterval(interval)deletes an interval again. We have to store the setInterval in a variable so that we can delete it again later.

ES6+ features

JavaScript isn't done – it is still being developed. The tc39 group is meeting up on a bi-monthly schedule to discuss proposals regarding ECMAScript specifications, which JavaScript is based on. Here you can find a list of active proposals that are still in the process of being reviewed. You can take a look at some of the finished proposals over here.

Template literals

let firstName = "Jane";
let lastName = "Doe";
let city = "Los Angeles";
let concatenation = firstName + lastName + " is from " + city;
let templateLiteral = `${firstName} ${lastName} is from ${city}`;

Any expression can be placed into these placeholders:

const greeting = `Hello ${
name !== null ? name : "mysterious person"
}, good to see you!`;

Destructuring

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. Destructuring doesn't mutate the original array or object.

Destructuring Arrays

const greekLetters = ["alpha", "beta", "gamma", "delta"];

If we want to declare the values of greekLetters as distinct variables, accessing them might turn out to be quite tedious. Without Destructuring

const firstLetter = greekLetters[0];

const secondLetter = greekLetters[1];

const thirdLetter = greekLetters[2];

With Destructuring

const [firstLetter, secondLetter, thirdLetter, fourthLetter] = greekLetters;

If you don't need or want the second and the fourth value in our initial array greekLetters there's an option to skip values during the process of destructuring.

const [firstLetter, , thirdLetter] = greekLetters;

// firstLetter → 'alpha'

// thirdLetter → 'gamma'

thirdLetter now contains the third value of greekLetters. The extra comma skips the value at the respective position in greekLetters. The fourth value is ignored because the destructuring assignment ended.

Skipping values with extra commas can quickly become unreadable. Imagine wanting to skip 5 values: const [a, , , , , , g] = x;. General advice is to be very mindful with this feature.

Destructuring Objects

const coachObject = {

name: "Sam",

mood: "great",

skills: "amazing",

};

You can use destructuring assignment like this:

const { name, mood, skills, score } = coachObject;

In contrast to array destructuring the variable names are not arbitrary and do not depend on the order. Here the names of the variables have to match the keys of the object properties. You can rename the variable while destructuring

const { name: firstName } = coachObject;

You can also set a default value of a property in case it does not exist:

const { isAdmin = true } = coachObject;

// isAdmin → true

Spread operator

The rest and the spread syntax look deceptively similar. Both are identified by three dots: .... They do two different things though, depending in which context ... is used.

Rest Syntax (...)

The rest syntax allows you to say: "Put the rest into this variable" when using destructuring assignment or declaring function parameters. You can use it with arrays:

const greekLetters = ["alpha", "beta", "gamma", "delta"];

const [firstLetter, ...allTheOtherLetters] = greekLetters;

// firstLetter → "alpha"

// allTheOtherLetters → ["beta", "gamma", "delta"]

Or with objects:

const coachObject = {

name: "Sam",

mood: "great",

skills: "amazing",

};


const { name, score, ...theRestOfTheCoachObject } = coachObject;

And even with function parameters:

function logLetters(firstLetter, ...moreLetters) {

console.log("the first letter is", firstLetter);

console.log("even more letters", moreLetters);

}


logLetters("alpha", "beta", "gamma", "delta");

// the first letter is alpha

// even more letters (3) ['beta', 'gamma', 'delta']

Spread Syntax (...)

The spread syntax allows you to say: "spread everything inside this variable into here" when declaring array or object literals or calling functions.

It works like this with array literal declarations:

const greekLetters = ["alpha", "beta", "gamma", "delta"];

const moreGreekLetters = [...greekLetters, "epsilon", "zeta"];

// moreGreekLetters → ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']

You can spread two (or more) arrays into another array ...

const redColors = ["crimson", "pink", "purple"];

const blueColors = ["navy", "teal", "sky"];

const mixedColors = [...redColors, ...blueColors];

// mixedColors → ['crimson', 'pink', 'purple', 'navy', 'teal', 'sky']

... and it matters where respectively you spread one array into another:

const cats = ["cat", "cat", "cat"];

const dogs = ["dog", "dog", "dog"];

const catsAndDogs = [...cats, ...dogs];// catsAndDogs → ['cat', 'cat', 'cat', 'dog', 'dog', 'dog']

const dogsAndCats = [...dogs, ...cats];// dogsAndCats → ['dog', 'dog', 'dog', 'cat', 'cat', 'cat']

const catsBetweenBirds = ["bird", ...cats, "bird"];// catsBetweenBirds → ['bird', 'cat', 'cat', 'cat', 'bird']

Spread syntax with object declarations

const circle = { radius: 5, shape: "circle" };

const greenCircle = { ...circle, color: "green" };// greenCircle → { radius: 5, shape: 'circle', color: 'green' }

Notice that the order of spread operations matters because you can override properties:

const circle = { radius: 5, shape: "circle" };

const largeCircle = { ...circle, radius: 20 };// largeCircle → { radius: 20, shape: 'circle' }

const notALargeCircle = { radius: 20, ...circle };// notALargeCircle → { radius: 5, shape: 'circle' }

Copy

The spread syntax is very helpful for creating a shallow copy of arrays and objects:

const book = { title: "Ulysses", author: "James Joyce" };// book → { title: 'Ulysses', author: 'James Joyce' }

const shallowCopyOfBook = { ...book };// shallowCopyOfBook → { title: 'Ulysses', author: 'James Joyce', year: '1920' }

shallowCopyOfBook.year = "1920";


const greekLetters = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"];

const sortedGreekLetters = [...greekLetters].sort((a, b) => a.localeCompare(b));

// this is an alternative to greekLetters.slice() which also creates a shallow copy

// greekLetters → ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']

// sortedGreekLetters → ['alpha', 'beta', 'delta', 'epsilon', 'gamma', 'zeta']

spread syntax when calling functions

const numbers = [4534, 3411, 2455, 4952];

const smallestNumber = Math.min(...numbers);

Optional Chaining

The optional chaining operator ?. is like the chaining operator ., except that instead of throwing an error if a reference is nullish (null or undefined), the expression 'short-circuits' with a value of undefined . This is useful when you're not sure if a property, method or index exists or if something is callable.

function feedCats(cats) {

return cats.forEach((cat) => console.log(cat, "says meow"));

}

feedCats(["Garfield", "Tom", "Grumpy Cat"]);// logs:Garfield says meow, Tom says meow, Grumpy Cat says meow

feedCats();//// Uncaught TypeError: Cannot read properties of undefined (reading 'forEach')

Whith Chaining

function feedCats(cats) {

return cats?.forEach((cat) => console.log(cat, "says meow"));

}

feedCats();// logs nothing, but also does not throw

The ?. short-circuits the expression and evaluates the return statement to undefined. This way the error is avoided without the need to explicitly use if statements or ternaries to check the values. Optional chaining is useful when targeting a property in an object with a deeply nested structure.

Nullish Coalescing

The nullish coalescing operator ?? is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.

const chocolate = true;

function chocolateCheck() {

return chocolate ?? "No chocolate :(";

}

const result = chocolateCheck();

In this example, our function chocolateCheck will return the value of chocolate (in this instance: true ), because the value of chocolate is neither null or undefined.

const chocolate = undefined;

function chocolateCheck() {

return chocolate ?? "No chocolate :(";

}

const result = chocolateCheck();

In this example, our function would return No chocolate :(, seeing as how the value of chocolate is undefined. The same would happen if we declared the variable chocolate with the value null.

Written as an if/else statement, the code would look like this:

const chocolate = true;

function chocolateCheck() {

if (chocolate === null || chocolate === undefined) {

return "No chocolate :(";

}

return true;

}

const result = chocolateCheck();

Modules and Import/Export

Modules

JavaScript modules (sometimes also called "ECMAScript Modules" or "ESM") are a way to organize code into separate files. To use modules you have to let the browser know that you are using modules. This is done by adding the type="module" attribute to the <script>tag.

<script type="module" src="./my-module.js"></script>

Modules allow you to use import and export statements but also change a few other things about how the browser treats your code that differ from standard scripts: They have their own scope and are not accessible from the global scope (unless exported). They also do not require the defer attribute as they are deferred by default. The scripts are executed in the more modern strict mode automatically.

Exporting using export Statements

Using the export statement you can export a variable or function to make it available to other modules. You can use named exports to export multiple variables or functions or one default export per module to export the main functionality of the module.

export const name = "Alex";

export const age = 26;

export function sayHello() {

console.log("Hello");

}

It is also possible to export functions or variables after they have been declared.

const name = "Alex";

const age = 26;

export { name, age};

default Exports

Default exports are created by using the keyword export default. You can only have one default export per module. Before a function declaration the syntax is similar to named exports.

export default function sayHello() {

console.log("Hello");

}

For directly exporting variables as default you only declare the value of the thing you're exporting.

export default "Alex";

You can mix named and default exports.

export const name = "Alex";

export default function sayHello() {

console.log("Hello");

}

Importing using import Statements

Code from other modules can be imported using the import statement. Import statements should always be placed at the top of the file. Everything that can be exported from a module can also be imported from another module.

Importing Named Exports

If another module exports a named export you can import it as such.

import { name, age } from "./my-module.js";

Importing default Exports

If another module exports a default export you have to give it a name when importing it.

import myModule from "./my-module.js";

Notice that the name you give it does not nessesarily have to match the name of the module or the original name of the thing that was exported. For example myModule could have the value of the sayHello function which is not clear from the name. Since you could import the same module in multiple files you can also give it a different name every time.

Mixing Named and default Imports

import myModule, { name, age } from "./my-module.js";

Renaming Named Imports

You can rename named imports explicitly by using the as syntax.

import { name as firstName, age as yearsSinceBorn } from "./my-module.js";

The variables firstName and yearsSinceBorn are now available in the current module. This can be useful if the name of an import conflicts with a local variable name.

Structuring JavaScript Code

Utility Functions and Constants

Utility functions are functions that are used in multiple places in your code. They are usually smaller functions that are used to perform a specific task. They should be be pure and not have any side effects.

Shared constants are constants that are used in multiple places in your code.

Functions and constants can be grouped into files that are named after the functionality they provide. For example math.js could contain functions like add, subtract, multiply and divide.The file should have a named export for each function.

It is recomended to create a utils folder in your project and put all utility functions in there.

Vanilla JavaScript Components

Recomendations:

Create a folder for each component

Make your component file and function names uppercase (PascalCase)

Each component has a named export for the component function (e.g. export function ButtonGroup())

Components can take arguments that are called props or properties a convention (e.g. export function ButtonGroup(props))

Components should not depend on the outside world and create their own DOM elements

Components should return a single DOM element

Web Apis and Fetch

Apis

The term API is short for Application Programming Interface.

An API provides a way one software (the application) can interact with another software.

A browser provides a lot of Web APIs. Each Web API defines a way on how a JavaScript application can use a feature given by the browser, like:

Web Animations API

Battery API

Fetch API

APIs running on a server environment are a different type of API. They are provided by a server, opposing to the APIs provided by the browser (which is also called the client). A common use-case for such APIs is to read / load data. Other operations like writing or deleting data is also possible. There are common approaches regarding the architecture of a serve-side API. One such approach are REST-APIs

Fetch API (with async & await)

fetch is a web API to asynchronously fetch (load) resources from the network, like text documents.

async function fetchData() {

const response = await fetch("/url/to/something");

const data = await response.json();

return data;

}

  • 1.-We mark the function with the async keyword, because we want to use await inside the function.
  • 2.-We declare a variable named response. It stores the Response object that is returned by fetch.
  • 3.-Once this promise resolves (the network request is finished), we call the .json method on the response variable. This function returns another Promise.
  • 4.-This second promise resolves with the actual data (payload) converted from JSON (a formatted string) to a JavaScript value or object. This result is stored in the variable named data.
  • 5.-The function returns the value stored in the data variable.

The Response object features other useful methods (all returning promises), among which are the following:

  • response.json() returns a Promise that resolves to the downloaded data JSON-parsed as a JavaScript value or object
  • response.text() returns a Promise that resolves to the downloaded data as raw text
  • response.formData() returns a Promise that resolves to the downloaded data parsed as FormData
  • response.blob() returns a Promise that resolves to the downloaded data as a raw blob (that's a machine readable format using zeros and ones)

JSON

structured data based on JavaScript object syntax. It is commonly used for transmitting data between a client (browser) and a server in web applications.

JSON is very close to being a subset of JavaScript syntax. Most valid JSON is also valid JavaScript code. This means that you can copy JSON into any .js file, put a const myData = in front, and watch Prettier make it look like real

A valid JSON even though it closely resembles JavaScript object literal syntax, it can be used outside of JavaScript. Other programming languages often feature the ability to read (parse) JSON.

JSON exists as a string that needs to be converted to a native JavaScript object when you want to access the data. JavaScript provides a global JSON object that features conversion methods in either direction.

JSON Conversion Methods

JSON.parse()-This method parses a JSON string and constructs the JavaScript value or object described by the string.

JSON.stringify()-This methods converts a JavaScript value or object to a JSON string.

HTTP Response Status Codes

HTTP response status codes usually indicate whether a specific HTTP request has been successfully completed. For the most part, any sort of response is considered a successful completion of the request.

With the exception of custom server software, these responses are standardized. You can find a list of HTTP response status codes here .

One of the most well-known HTTP response status codes is 404 not found

Error Handling

fetch() doesn't throw an error when the server returns a bad HTTP status, e.g. client or server errors.

A request will only register as rejected if a response cannot be retrieved. This may be due to network problems, such as no internet connection, the host not being found, or the server not responding.

We can separate good from bad HTTP response statuses by relying on the boolean response.ok property. It is set to true only if the HTTP response code is between 200-299.

If something goes wrong that causes that we do not receive a response the fetch API throws an error. Once an error is thrown execution stops and JavaScript looks for the closest catch block.

Any production fetch call should be inside a try...catch block:

async function fetchSomething() {

try {

const response = await fetch("/bad/url/oops");

if(response.ok) {

const data = await response.json();// Success (Good Response)

return data;

} else {

console.error("Bad Response"); // Failure (Bad Response)

}

} catch (error) {

console.error("An Error occurred"); // Failure (Network error, etc)

}

}

OLD version and problematic for debugging & not recomended

fetch(url)

.then((response) => response.json())

.then((data) => {

console.log(data);

})

.catch((error) => {

console.error("Error Message", error);

});

REST(REpresentational State Transfer) API

When you create an API, you need to come up with ideas on how to structure your API, shape the features you provide and which rules to apply. This is referred to as the architecture of your API.

REST is a set of architectural constraints, not a protocol or a standard. You as an API developer can implement REST in a variety of ways.

The overall idea is like this:

  • A client requests a resource from a server (like a document containing information on a specific topic)
  • The server formulates a response that represents the resource in a format the client understands (like JSON - other formats like HTML, XML oder plain text are also possible)
  • This transfer of data changes the state of the web application.

Each resource has a unique address. The whole communication is done via HTTP and uses different HTTP methods (like GET/POST/PUT/DELETE) to describe desired actions.

More information here

Testing and debugging

What are unit tests

When developing apps, errors inevitably emerge in your code. That's why apps must be tested regularly to ensure that they work as expected. Since manual testing by clicking in the UI is very time consuming and cumbersome, tests are automated.

Test automation can be performed in several ways. A good classification is provided by the Testing Trophy.

Static Testing refers to linting and can be understood as a spell checker for your code. Unit tests check whether a single function works as intended. The goal is to test each individual unit independently and isolated from other data and external influences. The test can be run automatically after each modification to ensure latest changes won't break the app

Test Driven Development (TDD)

With Test driven development (TTD) the test is written first. Afterwards you write the function, which should create a desired result.

Following this approach you get feedback as early as possible whether your work is going into the right direction. In the beginning all tests will fail. Gradually more and more test will be successful. The implementation of your function is finished as soon as all test runs are successful.

How to test with jest

Tests are placed in a file next to the code you like to test, but with .test.js as filename ending.

calculator.js <--- Code used in your app

calculator.test.js <--- Tests for this code

When writing tests, you build various different scenarios that check a certain desired result of a function. Each of such test cases is wrapped into a function called test() . The first argument of the test() function is a description of the test case in plain english.

Unit tests usually have a similar structure. First, the function to be tested is called and any required arguments are passed. Afterwards the result of this function call is passed to the expect() function. Thus different matcher functions like toBe() or toEqual() can be used.

This way the actually generated result of the function is checked against an expected result defined in the test case.

import { add } from "calculator";


test("adds the numbers 1, 2 and 3 correctly", () => {

const result = add(1, 2, 3);

expect(result).toBe(6);

});


test("adds the numbers 13, 28 and 42 correctly", () => {

const result = add(13, 28, 42);

expect(result).toBe(83);

});

Run Tests locally

Run all tests: npm run test

When executing this commands you will see the result of the test run: a list of all included tests and whether they were successful or failed.

Best practices

More Important practices

Use Descriptive Variable and Function Names:

Utilize meaningful and descriptive names for variables and functions to enhance code readability and maintainability.

Follow a Consistent Coding Style:

Adhere to a consistent coding style, such as the popular JavaScript style guides like Airbnb, Google, or StandardJS, to make your code more understandable and easier to collaborate on.

Properly Comment Your Code

Add clear comments to your code to explain complex logic, important decisions, and any potential gotchas for other developers who may work on the code in the future.

Avoid Global Variables:

Minimize the use of global variables to prevent variable collisions and make your code more modular. Use modules or closures to encapsulate your code.

Use Strict Mode:

Enable strict mode by adding "use strict"; at the beginning of your JavaScript files to catch common coding mistakes and improve code quality.

Handle Errors Gracefully:

Implement proper error handling using try-catch blocks or other error-handling mechanisms to prevent unexpected crashes and provide better user experiences.

Optimize Performance:

Focus on code performance by minimizing unnecessary DOM manipulations, reducing loops and iterations, and employing techniques like debouncing and throttling for event handling.