JavaScript for Web Development

Table of contents

1. Basics

1.1 Arrow Functions

Arrow functions are frequently used in JavasScript based web development.

Instead of writing a function like this:

function foo() {
  //...
}

We can write it in this form:

() => {
  //...
}

Notice that we don’t have a function name here. Arrow functions are anonymous. We must assign them to a variable.

We can assign a regular function to a variable, like this:

let foo = function foo() {
  //...
}

When we do so, we can remove the name from the function and invoke the function using the variable name:

let foo = function() {
  //...
}

foo()

We do same thing with arrow functions:

let foo = () => {
  //...
}
foo()

If the function body contains just a single statement, we can omit the parentheses and write it all on one line:

const foo = () => console.log('I love sashimi')

Parameters are passed in the parentheses:

const test = (param1, param2) => console.log(param1, param2)

And if we have one (and just one) parameter, we can omit the parentheses completely:

const test = param => console.log(param)

Arrow functions allow us to have an implicit return (i.e. values are returned without having to use the return keyword).

const test = () => 'I love sashimi'

foo()

>>> I love sashimi

And, just like with regular functions, we can have default parameters, in case they are not passed:

const test = (color = 'black', age = 2) => {
  //do something
}

2. Array Manipulation

2.1 Iteration with map() and forEach()

The map() method is used to loop over an existing array and returns a new array, with the result of running a function for each item of the array.

const a = [1, 2, 3]

const b = a.map(() => {})

>>> b
>>> [undefined, undefined, undefined]

// Undefined because we didn’t return anything 
// from the function we passed to map()

const b = a.map(() => 5)

>>> b
>>> [5, 5, 5]

The function we pass to map() is called a ‘callback function’.

const b = a.map(item => item)

// now b is a copy of a

>>> b
>>> [1, 2, 3]

const b = a.map(item => item * 10)

>>> b
>>> [10, 20, 30]

The forEach() of an array instance is used to execute a function also known as a “callback function”, for all elements of an array. This callback function is passed the current item:

const list = [1, 2, 3]

list.forEach(element => {
  console.log(element * 2)
})

It’s similar to map(), however nothing is returned from forEach() while using map() we get a new array with the result of the function executed in the callback.

We can also get the index of the element as the second parameter to the callback function:

const list = [1, 2, 3]

list.forEach((element, index) => {
  console.log(element, index)
})

2.2 Filtering

Create a new array by filtering on elements from an existing one.
How do you choose? In the callback function that filter() accepts, we return true to include that item in the new array.

Example – Filter on even numbers:

const a = [1, 2, 3, 4, 5]

const even = a.filter((item) => {
  if (item % 2 === 0) return true
  return false
})

or 

// take an item and return true if the item % 2 is 0
const even = a.filter(item => item % 2 === 0)

>>> even
>>> [2, 4]

Example – Find a specific element in the array:

const a = [1, 2, 3, 4, 5]

// take an item and return true if the item is 4
// filter() returns an array
// shift() returns the first item in the array
const b = a.filter(item => item === 4).shift()

>>> b
>>> 4

Example – Remove an item from an array:

const a = [1, 2, 3, 4, 5]

// take an item and return true if the item is not 2
const b = a.filter(item => item !== 2)

>>> b
>>> [1, 3, 4, 5]

Example – Remove multiple items using includes():

const items = ['a', 'b', 'c', 'd', 'e', 'f']

const valuesToRemove = ['c', 'd']

const b = items.filter(item => !valuesToRemove.includes(item))

>>> b
>>> ["a", "b", "e", "f"]

2.3 Sorting

To sort an array alphabetically, we just use sort():

const a = ['b', 'd', 'c', 'a']
a.sort()

>>> a
>>> ['a', 'b', 'c', 'd']

Note: This does not work for numbers, as it sorts ASCII value (0-9A-Za-z).

To sort by number values we need to use custom functions:

const a = [1, 4, 3, 2, 5]

a.sort((a, b) => (a > b ? 1 : -1)) 

>>> a
>>> [1, 2, 3, 4, 5]

We can reverse the sort order of an array with reverse():

const a = [1, 2, 3, 4, 5]

>>> a.reverse()
>>> [5, 4, 3, 2, 1]

2.4 Searching

we can search arrays with find() and findIndex().

find() is used to find an item in the array:

We pass a function, and we get back the first item that returns true from it and returns undefined if not found.

a.find((element, index, array) => {
  //return true or undefined
})

const itemFound = items.find((item) => item.name === 'b')

findIndex() is similar but instead of the item, it returns the index of
the first item that returns true, and if not found, it returns undefined:

a.findIndex((element, index, array) => {
  //return true or undefined
})

3. Asynchronous

3.1 Asynchronous Functions

We normally write promises like this:

isFinished.then(ok => {
  console.log(ok)
})

With async functions, we can write it like this:

const result = await isFinished
console.log(result)

When the JavaScript engine sees the await keyword, the execution of the current function will halt until the isFinished promise is resolved or rejected. But the enclosing function must be declared as async. We basically can’t call await isFinished outside of a function not defined as async.

const doSomething = async () => {
  const result = await isFinished
  console.log(result)
}

And error handling can be done in this way:

try {
  const result = await isFinished
  console.log(result)
} catch(err) {
  console.error(err)
}

Instead of this way:

isFinished.then(ok => {
  console.log(ok)
}).catch(err => {
  console.error(err)
})

3.2 Chaining Promises

In JavaScript, we call fetch() to get a resource from the internet (e.g. call an API endpoint).

For example, to get the JSON for my GitHub profile using the URL https://api.github.com/users/abechallah:

fetch('https://api.github.com/users/abechallah')

fetch() returns a promise which resolves to a Response object. When we receive that object, we need to call its json() method to get the body of the response:

fetch('https://api.github.com/users/abechallah')
  .then(response => response.json())

This also returns a promise, so we can add another .then() with a callback function that is called when the JSON processing is finished:

fetch('https://api.github.com/users/abechallah')
  .then(response => response.json())
  .then(profile_data => {
    console.log('Profile data', profile_data)
  })

To write this code using async functions, we can do it in this way:

const getProfile = async () => {
  const response = await fetch('https://api.github.com/users/abechallah')
  const profileData = await response.json()
  console.log('Profile data', profileData)
}

getProfile()