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

JavaScript Data Structuring

An ES6 feature that allows you to unpack array or an object and assign each value to a variable.

// Simple array
const arr = [2, 3, 4];
const [x, y, z] = arr; // creates 3 variables: x=2, y=3, z=4.

const [a, ,b] = arr; // misses out middle array value (a=2, b=4)

// switching variables
let first = "starter";let second = "main";
[first, second] = [second, first]; // first = main, second = starter

// nested arrays
const nested = [2, 4, [5, 6]];
const [, ,[j, k]] = nested; // j = 5, k = 6

// default values
const [p=1, q=1, r=1] = [8, 9]; // p=8, q=9, r=1

Destructuring Objects

Use curly braces to destructure objects. You use the property names as variable names.

const {propertyName1, propertyName2} = myObject; // desctructuring using property names.

const {propertyName1: myName, propertyName2: myName2} = myObject // destructuring with custom variable names

// setting default values (in case the property doesn't exist)
const {menu = [], starters: starterMenu = []} = myObject;

Mutating Objects

Surround the code with brackets to create a code block.

let a = 111;
let b = 999;
const obj = { a: 23, b: 7. c: 14 };
({ a,b } = obj); // not allowed to use const or let as variables are already declared - add brackets to prevent error and create a code block.

Nested objects

const { fri: { open: o, close } } = openingHours;
// fri is the property name of the first object which has open and close nested object. This syntax will create new variables "o" and "close" which will be Friday opening and closing hours

Destructuring objects with functions/methods

you can destructure objects passed in functions using curly brackets within the function parameters. You can also set default values as before.

restaurant = {
 name: "My Restaurant";
 // this method will destructure a passed object into four variables (using property names)
 orderDelivery: function ({starterIndex = 0, mainIndex = 1, time, address}) {
   console.log("delivery address:" + address);
 }
}

restaurant.orderDelivery({
 // create an object:
 time: '22:30',
 address: '29 This Street, This Town',
 mainIndex: 1,
 starterIndex: 2
});

Spread Operator

Scenario: Create a new array that has two values and then all the values of another array

const = oldArray = [3, 4, 5];
const = newArray = [1, 2, ...oldArray]; // newArray equals 1,2,3,4,5

Spread operator descructures a literal array but does not create new variables for each value.

Can be used to create shallow copies of arrays or merging arrays:

const copiedArray = [...originalArray]; // copy array
const firstAndSecondArray = [...firstArray, ...secondArray]; // merged arrays

Can also be used on any iterables (ie can break down a strings, maps, sets).

const str = 'word';
const letters = [...str]; Creates an array "w","o","r","d"

The spread operator can also be used as the parameter in a function to deconstruct an array and then pass the individual arguments.

Can be used to copy objects too as before.

Rest Pattern

Rest pattern does the opposite of the spread operator. Used to collect multiple elements and condense them into an array.

Uses the spread operator but on the left side of the equals operator

const [a, b, ...others] = [1,2,3,4,5]; // a = 1, b = 2, others = [3,4,5]

REST ELEMENT MUST BE THE LAST ELEMENT OF THE ARRAY. THERE CAN BE ONLY ONE.

Can be used in objects to pick properties out of existing objects.

const { sat, ...weekdays } = restuarant.openingHours; // creates an object of sat and an object of weekdays which includes all the properties of the remaining days less saturday.
Use case:

Can be used in functions when any number of arguments can be passed:

// rest parameter
const add = function (...numbers) {
  let sum = 0;
  for( let i = 0; i<numbers.length; i++) sum += numbers[i];
  return sum;
}

// can pass any number of arguments
add(2,3);
add(1,2,5,6);
const x = [23,5,7];
add(...x); // spread parameter to pass each value of the array.

The above example allows you to pass either an array or multiple single arguments through the function parameters.

Additional use in method

restaurant = {
 orderPizza = function(mainIngredient, ...otherIngredients){
  // will create a variable of first value of the arguement, and then an array of all the other arguments.
 }
};

The spread and the rest look exactly the same but work in opposite ways depending on where they are used.

Spread: Used where we would otherwise write values seperated by a comma

Rest: Used where we would otherwise write variable names separated by commas.

JavaScript overview

High-Level

Low-level languages like C need a dev to manage resources manually. JS is high-level so does not have to worry as system resource management happens automatically (abstractions).

+ easy to use and learn
– not as fast or optimised

Garbage-collected

Automatically clears unused objects from memory

Interpreted or Just-in-time compiled

ones and zeros (machine-code) via compiling / interpretation via the javascript engine. The engine compiles the source code to machine code and then immediately executes it without putting it in a portable file. Faster than compiling it line by line.

STEPS:

  • Parsing (AST) goes through the code and turns it into a tree
  • Compilation – turns it into machine code
  • Execution – (happens in the call stack) – Code is reoptimised during execution.

Multi-paradigm

More than one way to skin a cat. Imperative / declarative.

  • Procedural programming (linear)
  • Object-oriented programming (OOP)
  • Functional programming (FP)

Prototype-based object-oriented

Template/blueprint.

First-class functions

Functions used as variables. Can be passed to other functions.

Dynamic

Dynamically typed. Variable datatypes not assigned manually, js assigns them. A bit buggy – Typescript resolves

Non-blocking event loop

Concurrency model – JS engine handles multiples tasks at the same time although runs in one single thread. Event loops avoid long running tasks blocking.

JS ENGINE

Engine executes JS code. All browsers have their own engines. Chrome uses V8 (also powers node JS) for example.

All engines have a call stack and a heap.

CALL STACK
Is where code is executed.

HEAP
Is where objects are stored.

Javascript runtime in the browser

Runtime = is everything in the browser required to run javascript: engine, web APIs (accessed through global window), callback queue (click, timer, data)

Execution Concept

  • Variable environment
  • scope chain
  • this keyword

3 types of Scope

GLOBAL, FUNCTION, BLOCK (ES6)

Global Scope

These variables are declared outside of function and block and are accessible anywhere.

Function Scope (local scope)

These variables only accessible inside function.

Block Scope

Everything within curly braces (if or while loops). Only let or const variables (not var wehich is function-scoped). Functions are block scoped (in strict mode)

Scope Chain

Variable Lookup. A scope has access to variable from all outer (parent) scopes.

Hoisting

Hoisting makes some types of variables accessible/ usable in the code before they are actually declared (lifted to the top of the scope)

Hoisted: function dec & var, function expressions and arrows (depends on declared with let or const)

TEMPORAL DEAD ZONE

Javascript Fundementals (Part 1)

The Complete JavaScript Course 2025

High-level object-oreinted multi-paradigm programming language

VERSIONS:

ES6 is Modern Javascript (2015)

Values and Variables

  • Never use: function. new or name as variables. Prefix with _ or $ to use.
  • useCamelCase

Data Types

Every value is either OBJECT or PRIMITIVE

PRIMITIVE DATATYPES:

  • Number (floating point numbers)
  • Strings (sequence of characters
  • Boolean (true / false)
  • Undefined (empty value)
  • Null (empty value)
  • Symbol (ES2015) – Unique cannot be changed
  • BigInt – Massive numbers

Javascript has dynamic typing – it automatically assigns data type.

typeof – used to display what data type the variable is.

Declaring variables

var is legacy, use let or const. Use const where possible.

Use let if the variable can change.

Use const if the value of the variable cannot be changed (immutable) (also cannot be empty).

Operators

math (+ – / * x**y), assignment (= += -= *= ++ –), comparison (<> ===)

OPERATOR PRECEDENCE

Precedence Associativity Individual operators Notes
18: grouping n/a Grouping
(x)
[1]
17: access and call left-to-right Member access
x.y
[2]
Optional chaining
x?.y
n/a Computed member access
x[y]
[3]
new with argument list
new x(y)
[4]
Function call
x(y)
import(x)
16: new n/a new without argument list
new x
15: postfix operators n/a Postfix increment
x++
[5]
Postfix decrement
x--
14: prefix operators n/a Prefix increment
++x
[6]
Prefix decrement
--x
Logical NOT
!x
Bitwise NOT
~x
Unary plus
+x
Unary negation
-x
typeof x
void x
delete x [7]
await x
13: exponentiation right-to-left Exponentiation
x ** y
[8]
12: multiplicative operators left-to-right Multiplication
x * y
Division
x / y
Remainder
x % y
11: additive operators left-to-right Addition
x + y
Subtraction
x - y
10: bitwise shift left-to-right Left shift
x << y
Right shift
x >> y
Unsigned right shift
x >>> y
9: relational operators left-to-right Less than
x < y
Less than or equal
x <= y
Greater than
x > y
Greater than or equal
x >= y
x in y
x instanceof y
8: equality operators left-to-right Equality
x == y
Inequality
x != y
Strict equality
x === y
Strict inequality
x !== y
7: bitwise AND left-to-right Bitwise AND
x & y
6: bitwise XOR left-to-right Bitwise XOR
x ^ y
5: bitwise OR left-to-right Bitwise OR
x | y
4: logical AND left-to-right Logical AND
x && y
3: logical OR, nullish coalescing left-to-right Logical OR
x || y
Nullish coalescing operator
x ?? y
[9]
2: assignment and miscellaneous right-to-left Assignment
x = y
[10]
Addition assignment
x += y
Subtraction assignment
x -= y
Exponentiation assignment
x **= y
Multiplication assignment
x *= y
Division assignment
x /= y
Remainder assignment
x %= y
Left shift assignment
x <<= y
Right shift assignment
x >>= y
Unsigned right shift assignment
x >>>= y
Bitwise AND assignment
x &= y
Bitwise XOR assignment
x ^= y
Bitwise OR assignment
x |= y
Logical AND assignment
x &&= y
Logical OR assignment
x ||= y
Nullish coalescing assignment
x ??= y
right-to-left Conditional (ternary) operator
x ? y : z
[11]
right-to-left Arrow
x => y
[12]
n/a yield x
yield* x
Spread
...x
[13]
1: comma left-to-right Comma operator
x, y