JavaScript – JS & the DOM

Creating and inserting elements

  • createElement()
  • insertAdjacentHTML()
const newElement = document.createElement('div');
newElement.classList.add('newClass');
message.innerHTML =  'My HTML content';

header.append(message); // or prepend, or before, or after.

message.remove(); // remove element

Other methods etc:

// Styles

message.style.backgroundColor = '#232323'; // use camelCase - sets inline style
getComputedStyle(message).height;

Number.parseFloat(getComputedStyle(message).height, 10) // Gets numeric value of height

document.documentElement.style.setProperty('--color-primary', 'orangered'); // Sets root style variables

// Attributes
myButton.alt; myButton.src; myButton.className;
myButton.getAttribute('customAttribute');
myButton.dataset.versionNumber // gets data-version-number

Smooth Scroll

const btnScrollTo = document.querySelector('.btn-scroll-to');
const section1 = document.querySelector('#section--1');

btnScrollTo.addEventListener('click', function(e) {
  const s1coords = section1.getBoundingClientRect();
}

window.scrollTo({
 left: s1coords.left,
 top: s1coords.top + window.pageYOffset);
 behaviour: 'smooth'
});

// MODERN BROWSERS
section1.scrollIntoView({behaviour: 'smooth'});

// Viewport
document.documentElement.clientHeight document.documentElement.clientWidth

More Events & EventHandlers

const mouseEnterFunction = function(e) {// do something}

message.addEventListener('mouseenter', mouseEnterFunction); // allows you to add multiple event listeners.

// Alternatively - old version
message.onmouseenter = function (e) {};

// Remove
setTimeout(() => message.removeEventListener('mouseenter', mouseEnterFunction), 2000);

Bubbling & Capturing

Event is initially captured at root. Capturing phase goes through all the child elements to the target phase and the passes back (bubbling phase).

JavaScript: Timers

There are two types of timers: setTimeout and setInterval.

setTimeout()

setTimeout( () => console.log('show this after 3 seconds'), 3000);

The code will continue to be read after the setTimeout, it will run the timer in the background while it continues the code.

Timer can be cleared before executed using clearTimeout()

setInterval()

Creates a timed loop.

setInterval(function () {
  const now = new Date();
  console.log(now);
}, 1000);

Example Timer countdown

const startLogoutTimer = function() {
  const tick = function() {
    const min = String(Math.tunc(time / 60).padStart(2,0);
    const sec = String(time % 60).padStart(2,0); // remainder literal

    // In each call, print the remaining time to UI
    labelTimer.textContent = `${min}:${sec}`;

    // when - seconds, stop timer and log out user
    if(time === 0) {
      clearInterval(timer)
    }

    // Decrease by 1s
    time--;

  }

  // Set time to 5 minutes
  let time = 100;
  
  // Call the timer every second
  tick()
  const startTimer = setInterval(tick , 1000}
}

JavaScript – Dates

New Date()

Get’s current date, or pass a string or seconds passed since UNIX time 1/1/70 01:00

Date components have methods

const future = new Date(2037, 10, 19, 15, 23)
future.getFullYear();
future.getMonth();
future.getDate();
future.getDay();
future.getHours();
future.getMinutes()
future.getSeconds()
future.getISOString()
future.getTime(); // get timestamp

future.setFullYear(2040) // Modifies object

Operations with Dates

Convert date to a Number (+ or Number()) you can then perform maths on dates.

const calcDaysPassed = (date1, date2) => Math.abs(date2 - date1) / (1000 * 60 * 60 * 24);

Internationalizing Dates (Intl)

const now = new Date();
const options = {
 hour: 'numeric',
 minute: 'numeric',
 day: 'numeric',
 month: 'long',
 year: 'numeric',
 weekday: 'long'
}

new Intl.DateTimeFormat('en-US', options).format(now); // use ISO language code

const locale = navigator.language // Get ISO code from user's browser

Internationalizing Numbers (Intl)

new Intl.NumberFormat('en-US').format(num);

const options = {
  style: 'currency', // unit percent or currancy
  unit: 'celsius',
  currency: 'EUR'  // Not defined by locale so needs to be implied
  useGrouping: false // removes seperators
}

Javascript – Numbers

Numbers are binary base in javascript

+ works the same as Number():

Number('23')
+('23)

Number.parseInt(string, redex)

Turns string into integer if it starts with a number (removing any other characters in the string. Redex not neccessary

Number.parseInt('20px', 10) // returns 20
Number.parseInt('e20', 10) // returns NaN

Number.parseFloat(string, redex)

parseFloat is used if the number has a decimal place.

Number.isNaN(string)

Boolean to check if it is not a number

Number.isFinite(), Number.isInteger()

Better to use than isNaN. To check if it is a number as it can check if numbers are divided by 0.

Math

Math.sqrt*(); Square root
Math.max(array) // Returns maximum number in array (does string to number converstion)
Math.min(array) // Returns minimum in array

Math.PI + Number.parseFloat(radius) ++2)

//Random
Math.trunc.random() * 6) + 1); // gets a number between 1 and 6

// Random Function
const randomInt = (min, max) => Math.floor(random() * (min-max + 1) + min)
console.log(randomInt(10,20));

// Rounding Integers
Math.trunc(23.3) // removes the decimal values
Math.round() // round to nearest
Math.ceil() // round up
Math.floor() // round down

// Rounding decimals
(value).toFixed(nummber of decimal places) // !!! Returns String
+(2.345).toFixed(2) // returns 2.34

Remainder operator

returns the remainder of a division

5 % 2 // returns 1

Numeric Seperator

Use _, js will ignore it and convert the number fully.

BigInt

JS has 53 bytes for integers, so there is a limit to the highest number that can be used

Number.MAX_SAFE_INTEGER

Use n at the end of a very lager number or use BigInt()

Cannot mix BigInts with regular numbers (except on < or >) (convert number to bigInt). Math operations will not work either.

JavaScript – Array Methods

.slice

Works the same as for strings. Specify start from and start to in parameters. CAN ALSO BE USED to return a shallow copy of array.

let arr = ['a', 'b', 'c', 'd', 'e'];

//SLICE
arr.slice(2); // Returns c, d, e
arr.slice(2,4); // Returns c, d
arr.slice(-2); // Slices from rear (returns last two) d e
arr.slice(-1); // Returns last (e)
arr.slice(1, -2); // returns b, c
arr.slice; creates shallow copy of orignal array (same as [...arr]

.splice()

Same as above but MUTATES the original array (returns mutated array)

.toSpliced()

Non-destructive method of the above

.reverse()

Reverses the original array (mutates).

.toReversed()

Non-destructive method of reverse.

.concat(array)

Concatonated two arrays (as […arr, arr]). Doesn’t mutate original arrays.

.join(‘-‘)

Turns the array into a string joined by optional parameter

let arr = ['a', 'b', 'c', 'd', 'e'];
arr.join('+'); // Returns string of a+b+c+d+e

.at() (ES6)

Works just the same as [] however can use negative index (also works on strings):

// Get last array element.
arr.at(-1); works the same as arr[arr.length - 1]

forEach

Used for looping through arrays. Requires a callback function.

Passes the element, index and entire array

arr.forEach(function (element, index, array) {
 // Do something
});

YOU CANNOT BREAK OUT OF THE forEach LOOP. Use For of instead.

Works also with Maps and Sets. (will not return key of sets because there isn’t one).

.map()

Map loops through array and applies a function and builds a brand new array (no side effects).

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const eurToUsd = 1.1;

const movementsUSD = movements.map( function(mov) {
  return mov * eurToUsd;
}) // Functional programming

//Arrow function version.
const movementsUSD = movements.map(mov => mov * eurToUsd); 

// EXAMPLE 2
const movementsDescriptions = movements.map((mov,i ) => {
  `Movement ${i + 1}: You ${mov > 0 ? 'deposited' : 'withdrew'} ${Math.abs(mov)}`
});

// EXAMPLE 3
const user = 'Steven Thomas Williams';
const username = user
  .toLowerCase()
  .split(' ')
  .map(word => word[0])
  .join('');
console.log(username); // returns 'stw'

.filter(condition)

Will go through and return a new array containing all elements in original array the meet the specified test condition.

const deposits = movements.filter(function(mov){
  return mov > 0;
}); creates a new array with only deposits

const withdrawals = movements.filter(mov => mov < 0); // returns new array with only negative values.

.reduce(condition)

Boils all array elemements down to one single value (like adding all elements together.

// accumalator -> SNOWBALL
const balance = movements.reduce(function (acc, cur, i, arr) {
  return acc + cur;
}, 0); // 0 defines the start value of the accumalator

console.log(balance);

// simplified arrow function:
const balance = movements.reduce((acc,cur) => acc + cur, 0);


// Maximum values
const maxValue = movements.reduce((acc, mov) => acc > mov ? acc : mov, movements[0]); // Returns the highest amount in the array

.find(condition) ES6

Needs a callback function that returns a boolean. It will loop until the first true value is returned and will return the first element only.

const account = accounts.find(acc => acc.owner === 'Jessica Davis'); // returns the first object that returns a true value. 

.findIndex(condition) ES6

Needs a callback function that returns a boolean. It will loop until the first true value is returned and will return the first element index only.

.findLast(condition) & .findLastIndex(condition) ES2023

Same as above but returns last value / index.

.some(boolean condition)

Similar to includes but includes a condition instead rather than a specific value

.every(boolean condition)

Method returns true if every element in array meets the condition.

array.flat(depth)

No callback required, flattens the array.

array.flatMap(callback)

No callback required, Creates a map and then flattens the array.

Sorting Arrays

Sorting Arrays
arr.sort() // sorts in alphabeltical (converts to strings) - MUTATES original

// To sort in numerical strings ascending order (loops through array)
// return < 0, A, B (keep order)
// return > 0, B, A (switch order)
arr.sort((a, b) => {
 if (a > b) return 1;
 if (b > a) return -1;
}); //

// For numbers. simplified
arr.sort(( a,b ) => a - b);

// Non destructive method
const newArray = arr.toSorted(( a,b ) => a - b);

Array Grouping (Object.groupBy)

Creates an Object grouping elements as per callback condition.

const groupedArr = Object.groupBy(arr, callback => callback > 0 ? 'positives' : 'negatives') // 

Generating arrays programatically

const arr = new Array(7) // creates 7 empty slots
arr.fill(1) // fills the array with 1s
arr.fill(1, 3) // starts from index 3 [ , , 1, 1, 1, 1]
arr.fill(1, 3, 5) // fills from index 3 until index 5


const arr = Array.from({length: 7), () => 1); // creates array full of 1s


const inc = Array.from({length: 7}, (cur, i) => i + 1); // creates incrementing array

// Create an array of 100 random dice rolls
const diceRolls = Array.from(
  { length: 100 },
  () => Math.floor(Math.random() * 6) + 1
);
console.log(diceRolls);

.with(i, val)

Creates a new array copy but replaces the element at index i with new value

When to use each method?

To mutate original

Add to original

  • push (end)
  • unshift (start)

Remove from original

  • pop (end)
  • shift (start)
  • splice (any)

Others:

  • reverse
  • sort
  • fill

A new array based on original

Same length as original

  • map (loop)
  • filter (using condition)
  • slice (taking portion)
  • with (with one item replaced)
  • flat (flattened)
  • flatMap (flattened into a map)
  • toReversed
  • toSorted
  • toSpliced (with deleted items)
  • concat (joining two arrays)

An array index

  • indexOf (based on value)
  • findIndex / findLastIndex (based on test condition)

An array index

  • find
  • findLast

I want to know ifd array includes…

  • includes (based on value)
  • some (based on test condtion (boolean)
  • every (based on test condtion (boolean)

I want a new string

  • join (based on seperator)

To transform to value

  • reduce (based on accumulator – boils down array to a single value)

to just loop over the array

  • forEach (does not create a new array, just loops over it)

Other useful tools

  • Object.groupBy – Grouping an array by categories
  • Array.from – Creating a new array from scratch
  • new Array(n) – create a new array from scractch with n empty positions (use with .fill method)
  • […arr1, …arr2] joining arrays
  • [… new Set(arr)] Creating a new array containing unique values from arr
  • [… new Set(arr1).intersection(new Set(arr2))] – Create a new array containing unique elements that are present in both arr1 and arr2.

Javascript – Functions

Passing by value vs Passing by reference.

Javascript only has passing by value.

First Class and Higher Order Functions

First-Class Functions

  • JS treats functions as first-class citizens
  • This means that functions are simply values
  • Functions are just another “type” of object

Therefore they can be stored in objects and passed as parameters in other functions.

There are methods on functions (such as .bind())

Higher-Order Function

  • A function that receives another function as an argument, that returns a new function or both
  • This is only possible because of first-class functions
btnClose addEventListener('click', greet) // higher order function (addEventListener); first-class function (greet)

const myFunction = function(str, fn) // Callback function

Callback Functions

Allow us to create abstraction.

Vital part of the JavaScript

Call, Apply & Bind Methods

.call() is a method that can be applied to the function which sets the ‘this’ keyword (passed in the first argument).

.apply(thisArg, argsArray) is the same as above however it requires an array as the second argument.

…however with the spreadoperator there is no need to use the .apply method, just use .call(thisArg, ...argsArray) as it does the same thing.

The bind() method works like the .call() method but returns a new function. The new function is bound to the new object. You can also pass arguments after the thisArg to set the default parameters (partial application).

The bind method is useful when used with Event Listeners

lufthansa.planes = 300;
lufthansa.buyPlane = function() {
 console.log(this);
 this.planes++;
 console.log(this.planes);
};

// NaN
document.querySelector('button.buy').addEventListener('click', lufthansa.buyPlane) // this would be the buy button and therefore not work

// 301
document.querySelector('button.buy').addEventListener('click', lufthansa.buyPlane.bind(lufthansa);

Partial application

const addTax = (rate, value) => value + value * rate;

const ukTax = addTax.bind(null, 0.2); // this sets the value to a default

Coding challenge

'use strict';

///////////////////////////////////////
// Coding Challenge #1

/* 
Let's build a simple poll app!

A poll has a question, an array of options from which people can choose, and an array with the number of replies for each option. This data is stored in the starter object below.

Here are your tasks:

1. Create a method called 'registerNewAnswer' on the 'poll' object. The method does 2 things:
  1.1. Display a prompt window for the user to input the number of the selected option. The prompt should look like this:
        What is your favourite programming language?
        0: JavaScript
        1: Python
        2: Rust
        3: C++
        (Write option number)
  
  1.2. Based on the input number, update the answers array. For example, if the option is 3, increase the value AT POSITION 3 of the array by 1. Make sure to check if the input is a number and if the number makes sense (e.g answer 52 wouldn't make sense, right?)
2. Call this method whenever the user clicks the "Answer poll" button.
3. Create a method 'displayResults' which displays the poll results. The method takes a string as an input (called 'type'), which can be either 'string' or 'array'. If type is 'array', simply display the results array as it is, using console.log(). This should be the default option. If type is 'string', display a string like "Poll results are 13, 2, 4, 1". 
4. Run the 'displayResults' method at the end of each 'registerNewAnswer' method call.

HINT: Use many of the tools you learned about in this and the last section 😉

BONUS: Use the 'displayResults' method to display the 2 arrays in the test data. Use both the 'array' and the 'string' option. Do NOT put the arrays in the poll object! So what shoud the this keyword look like in this situation?

BONUS TEST DATA 1: [5, 2, 3]
BONUS TEST DATA 2: [1, 5, 3, 9, 6, 1]

GOOD LUCK 😀
*/

const poll = {
  question: 'What is your favourite programming language?',
  options: ['0: JavaScript', '1: Python', '2: Rust', '3: C++'],
  // This generates [0, 0, 0, 0]. More in the next section 😃
  answers: new Array(4).fill(0),
  registerNewAnswer() {
    let response = Number(
      prompt(
        this.question +
          '\n' +
          this.options.join('\n') +
          '\n(Write option number)'
      )
    );
    console.log(response);
    if (response >= this.options.length || response === NaN) {
      alert('Write option number');
      this.registerNewAnswer();
    } else {
      // update answers array
      this.answers[response]++;
      this.displayResults(this.answers);
    }
  },
  displayResults(type = 'array') {
    if (type === 'array') {
      console.log(this.answers);
    } else {
      console.log(`Poll results are ${this.answers.join(',')}`);
    }
  },
};

document
  .querySelector('.poll')
  .addEventListener('click', poll.registerNewAnswer.bind(poll));

const pollResponses = [5, 2, 3];

poll.displayResults.call({ answers: pollResponses }, 'string');

Immediately Invoked Function Expression (IIFE)

(function() {console.log('one time function')})();

(() = console.log('one time function'))();

All data inside IIFE is private and encapsulated.

Closures

A closure makes the function remember all the variables that were created at the function’s birthplace. If a variable is reachable via closure then it is not garbage collected from the Heap.

A closure gives a function access to all the variables of its parent function even after that parent function has returned. The function keeps a reference to its outer scope, which preserves the scope chain throughout time.

const secureBooking = function () {
  let passengerCount = 0;
  return function () {
    passengerCount++;
    console.log(`${passengerCount}passengers`);
  };
};

const booker = secureBooking();
booker();
booker();  // passengerCount still increments because closure.

console.dir(booker); // will show closure in the scopes

Javascript – Working with strings

String methods

string.indexOf('') // gives position of needle in haystack
string.lastIndexOf('') // if there are multiple

sting.slice(i, ii) // remove the first i and last ii characters of string

string.slice(-i) // Save last i of string

string.slice(0, string.indexOf(' ')) // gets first word.

string.toLowerCase();
string.toUpperCase();
string.trim(); removes LEADING spaces and \n

const normalizedEmail = eMail.toLowerCase().trim();

// replacing
.replace(item, replacement) // replace first appearance
.replaceAll() // replaces all // ***New method***

.replace(/string/g, replacement) // replacing with global exoressions

// Booleans
string.includes()
string.startsWith()
string.endsWith()


//Split and join
console.log('a+lovely+string'.split(+)) // returns an array
const [firstName, lastName] = "Duncan Samwell".split(' ') // creates an array
const newName = ['Mr.' firstName, lastName].join();

//Capitalize function
const capitalizeName = function(name) {
 const names = name.split(' ');
 for (const n of names) {
  n[0].toUpperClass() + n.slice(1);
  n.push(n.replace(n[0], n[0].toUpperCase());
 }
}

//Padding
string.padStart(n, p) // fill string with p until entire string is length of n
string.padEnd(n, p)

// Example for Credit Card
const maskCreditCard = function(cc) {
 const l = cc.length;
 const lastFour = cc.slice(-4);
 return lastFour.padStart(l, '*');
}


//repeat
string.repeat(x) // repeat x amount of times

String Methods Practice

/*
///////////////////////////////////////
// String Methods Practice
*/

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// 🔴 Delayed Departure from FAO to TXL (11h25)
//              Arrival from BRU to FAO (11h45)
//   🔴 Delayed Arrival from HEL to FAO (12h05)
//            Departure from FAO to LIS (12h30)

const getCode = flights.split('+');

const l = getCode[0].length;

for (const line of getCode) {
  const section = line.split(';');
  // Status
  let status = section[0].slice(1).replace('_', ' ');
  if (status.startsWith('Delayed')) status = '🔴 ' + status;
  // From
  let from = section[1][0] + section[1][1] + section[1][2];
  // To
  let to = section[2][0] + section[2][1] + section[2][2];
  // Clock
  let clock = section[3].replace(':', 'h');

  const output = status + ' from ' + from.toUpperCase() + ' to ' + to.toUpperCase() + ' (' + clock + ')';

  console.log(output.padStart(l - 6));
}

Alternative

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// 🔴 Delayed Departure from FAO to TXL (11h25)
//              Arrival from BRU to FAO (11h45)
//   🔴 Delayed Arrival from HEL to FAO (12h05)
//            Departure from FAO to LIS (12h30)

const getCode = str => str.slice(0, 3).toUpperCase();

for (const flight of flights.split('+')) {
  const [type, from, to, time] = flight.split(';');
  const output = `${type.startsWith('_Delayed') ? '🔴' : ''}${type.replaceAll(
    '_',
    ' '
  )} ${getCode(from)} ${getCode(to)} (${time.replace(':', 'h')})`.padStart(36);
  console.log(output);
}

Javascript – Sets & Maps

Sets and Maps new datasctructures introduced in ES6

Sets

A collection of unique data values (iterrables)

const mySet = new Set([
 'unique value 1', // iterable
 'unique value 2'
]);

Sets are also iterrables like arrays, however they differ for two reasons:

  • Can contain only unique elements
  • Order of elements are irrelevant

If a string (also an iterrable) is passed this will create a set with elements of each letter of the string (must be no duplicate letters though.

Set methods

console.log(mySet.size); // similar to .length
console.log(mySet.has('element'); // similar to .includes
orderSet.add('New Element'):
orderSet.delete('Old Element');
orderSet.clear()

Retrieving values from sets

Cannot be done as there is no point as all elements are unique. Just need to use .has() method

Looping sets

for ( const myDatavalue of mySet ) // action

Use cases for sets

Can be used to create a unique iterrable from an array which has multiple repeating datavalues.

const staff = ['Waiter', 'Chef', 'Waiter', 'Manager', 'Waiter', 'Chef'];
const uniqueStaff = [..new Set(staff)]; // creates an array which only includes waiter, chef and manager

New ES2025 set methods

const commonFoods = italianFoods.intersection(mexicanFoods) // creates a new set of common elements

const italianMexicanFusion = italianFoods.union(mexicanFoods); // Creates a new array combining all elements without duplicates. ( although this can me done with spread operator)

const uniqueItalianFoods = italianFoods.difference(mexicanFoods) // create a set of elements that are included only in Italian Foods

const uniqueItalianAndMexicanFoods = italianFoods.symetricDifference(mexicanFoods) 

.isDisjointFrom() // check if both arrays have unique values from each other

Maps

Maps are like arrays but with assignable keys.

Create with new Map();

// best practice is to create an empty map first
const restaurant = new Map();

Add items with .set()

restaurant.set('name', 'My Lovely Restaurant');
restaurant.set('open', '19').set('closed', '23'); // can also be chained
restaurant.set(true, 'We are open').set(false, 'We are closed'); // can use boolean or numbers too

Retrieve values with .get(key) – expressions can also be passed

console.log(restaurant.get('name')); // returns name value

let time = 21;
console.log(rest.get(time > restaurant.get('open') && time < restaurant.get('close'))); returns true, therefore returns 'we are open' (value of true).

Other map methods

.has()
.delete()
.size
.clear()

Objects and Arrays can also be Map Keys. Arrays must be created in the heap.

Populating maps without using .set() method

const question = new Map([
 ['question', 'What is the capital of Peru?'],
 [1, 'Lima'],
 [2, 'Lama'],
 [3, 'Lemur'],
 ['correct', 1],
 [true, 'Correct'],
 [false, 'Try Again']
]);

Converting objects to map

const myMap = new Map(Object.entries(myObject));

Destructing maps in the same way as destructing objects (although .entries method is not required

for(const [key, value] of myMap) {
 //
}
const answer = Number(
  prompt(
    `What is the capital of Peru\n1. ${question.get(1)}\n2. ${question.get(2)}\n3. ${question.get(3)}`
  )
);
console.log(question.get(answer === question.get('correct')));

Javascript – Looping

The for-of loop

Similar to PHP for as loop

for ( const item of myArray ) console.log(item) // Iterates through an array each item is the value

Getting the index of a value, use the .entries method

for ( const item of myArray.entries() ) {
  key = item[0]; value = item[1];
}

// Destructured approach
for ( const [i, el] of myArray.entries() ) {
  key = i; value = el;
}

Enhanced object literals

ES6 three ways to write object literals:

const miniObject = {
 day1: {
  open: 08:00,
  close: 16:00
 },
 day2: {
  open: 08:00,
  close: 12:00
 },
}

const mainObject = {
 miniObject,  // referencing the other object creates a new object within this object with the same property name
}

For methods (functions) within objects, there is no need to add the function keyword:

orderDelivery: function(arg1, arg2) {
 // action
}
// With ES6 you can do this instead...
orderDelivery(arg1, arg2) {
 // action
}

Compute property names instead of manually writing them:

const weekdays = ['mon', 'tues', 'wed', 'thu', 'fri', 'sat', 'sun'];
const openingHours = {
 [weekdays[3]]: {
  open: 12,
  close: 22
 }
}

You can also compute property names with expressions (ie maths)

Optional Chaining (?)

ES2020

Using ? operator after the potentially non-existent property prevents errors. It will return undefined (falsey) if not, otherwise it will continue down the chain.

restaurant.openingHours.mon?.open // return undefined if mon does not exist (shortcircuit), if it does then return open.
const weekdays = ['mon', 'tues', 'wed', 'thu', 'fri', 'sat', 'sun'];

for (const day of weekdays) {
 const open = restuarant.openingHours[day]?.open ?? 'closed';
}

Optional chaining on methods and arrays too.

const users = [{name: 'Duncan', email: 'duncan@samwell.me'}];
console.log(users[0]?.name ?? 'no name given';

Looping objects

Indirectly looping over objects

Looping over property names: Object.keys

for ( const day of Object.keys(openingHours)) {
 console.log(day) // array of all the property names within that object
}

Looping over property values: Object.values

for ( const values of Object.values(openingHours)) {
 console.log(values) // array of all the values within that object
}

Looping over entire objects: Object.entries

Transfers object into an array

for ( const [key, {propertyName1, propertyName2] of Object.entries(openinghours)) {

}

JavaScript – Modern Operators

&&, ||, ??

Operators can use ANY data type, return ANY data type, and can be used for short-circuiting

console.log(3 || 'Nothing'); // returns the first truthy value (3)
console.log('' || 'Nothing')// Nothing
console.log(true || 'Alternative') // true
console.log(undefined || null) // null (null is truthy, undefined is not

Returns the first true value of an argument and then stops (short-circuits)

Can use this instead of terniary arguments

const myVar = trueArgument ? trueArgument : falseAction;
const myVar = argument || falsAction;

&& operator works in the opposite way

Will return the first falsey or all of them if they are all truthy

myFunction && myFunction('arg'); Checks that functions exists, if not then argument ends.

?? Nullish Coalescing Operator

This is used to avoid the issue if 0 should not be a falsey value.

Nullish: null and undefined only (not 0 or ”)

Other logical operators (ES6)

||=, &&=, ??=

myVariable ??= 10; // sets value to 10 if it is nullish

myVariable &&= '<anonymous>'; // resets value to annonymous if it exists