Notes Week 5
ES6 Syntax
There was lots of new syntax introduced to JavaScript over the past several years, which makes our lives easier.
Arrow Functions
// old syntax
const add = function(a, b) {
return a + b
}
// new syntax
const add = (a, b) => a + b
add(1, 2) // 3
The Ternary Operator
// old syntax
let num
if (condition) {
num = 1
}
else {
num = 2
}
// new syntax
const num = condition ? 1 : 2
Template Strings
const name = "Jolene"
const color = "Auburn"
// old syntax
const text = "My name is " + name + " and I have " + color + " hair."
// new syntax
const text = `My name is ${name} and I have ${color} hair`
Destructuring
const jolene = {
name: "Jolene",
age: 18,
eyes: "emerald green"
}
// old syntax
const name = jolene.name
const age = jolene.age
const eyes = jolene.eyes
// new syntax
const { name, age, eyes } = jolene
const likes = ["pizza", "myself", 420]
// old syntax
const food = likes[0]
const person = likes[1]
const number = likes[2]
// new syntax
const [food, person, number] = likes
Default Values
// old syntax
function addOne(num) {
return num + 1
}
// new syntax
function addOne(num = 0) {
return num + 1
}
addOne() // old => NaN, new => 1
Shorthand for Objects
const name = "Jolene"
const hairColor = "Auburn"
const beautyLevel = "Beyond Compare"
// old syntax
const jolene = {
name: name,
hairColor: hairColor,
beautyLevel: beautyLevel
}
// new syntax
const jolene = {
name,
hairColor,
beautyLevel
}
The Spread/Rest Operator
const jolene1 = {
name: "Jolene",
age: 18
}
const jolene2 = {
skinColor: "ivory",
voiceSoftnessLevel: "like summer rain"
}
// old syntax
const combinedJolene = {
name: jolene1.name,
age: jolene1.age,
skinColor: jolene2.skinColor,
voiceSoftnessLevel: jolene2.voiceSoftnessLevel
}
// new syntax
const combinedJolene = {
...jolene1,
...jolene2
}
const someThings = [
"raindrops",
"roses"
]
const moreThings = [
"whiskers on kittens",
"brown paper packages tied up with string"
]
// old syntax
const allFavThings = [
someThings[0],
someThings[1],
moreThings[0],
moreThings[1]
]
// new syntax
const allFavThings = [
...someThings,
...moreThings
]
Array Prototype Methods
All objects come with a __proto__
property, which contains a list of properties and methods that we get for free, based on the class of object that we’re accessing.
For arrays, there are a few super useful methods that you should be aware of:
- Array.push() - add a new item to the array
- Array.forEach() - run a function for every item in the array
- Array.map() - mutate every item in the array
- Array.filter() - filter out some items in the array
- Array.reduce() - create a new value by looping over the array
Object Orientated Programming
Classses and Objects
In JavaScript, we often want to represent things in the real world as objects inside our programs. For example:
const jolene = {
name: "Jolene",
age: 21
}
We can define properties and methods on these objects, to add more and more color for how they behave in the real world.
We often find that we have overlapping code when we try to create similar objects:
const apple = {
name: "apple",
taste: "sweet",
shape: "round",
eat: () => console.log("Mmmm"),
throw: () => console.log("Take this!")
}
const banana = {
name: "banana",
taste: "semi-sweet",
shape: "arced",
eat: () => console.log("Mmmm"),
throw: () => console.log("Take this!")
}
We’re repeating functionality for our eat()
and throw()
methods here, meaning that we’re not able to keep our code DRY (Don’t Repeat Yourself!).
We solve this by using classes, which allow us to factor out common functionality for objects.
Classes in JavaScript are sort of like cookie cutters to a cookie. A class is a template or blueprint, which we can use to create an object (AKA a class instance).
Class Syntax
class Fruit {
constructor(name, taste, shape) {
this.name = name
this.taste = taste
this.shape = shape
this.eaten = false
}
eat() {
if (!this.eaten) {
console.log("Nom non nom")
this.eaten = true
}
else {
console.log("Awww... I ate that one already")
}
}
}
Loading Data
HTTP Requests
The way that we communicate to servers is through HTTP requests. After receiving our HTTP requests, the server can then respond to us with an HTTP response.
There are a few different types of HTTP request that we can send:
- GET - give me something
- POST - create something new
- PATCH - edit something
- PUT - replace something
- DELETE - delete something
APIs Explained
API stands for Application Programming Interface - it describes how we can interact with our code on our server.
An API typically consists of a set of routes, which define how we can interact with our server.
Typically, when we ask for and send data to our APIs, we make use of JavaScript Object Notation (JSON), which is a type of data fornat. JSON looks almost identical to an array of JavaScript objects, which makes it easy to handle in our code.
Asynchronous vs Synchronous Operations
When we make an HTTP request to a server, this takes a non-zero amount time, and so we say that it is an asynchronous operation (as opposed to a console.log()
statement, which gets executed immediately - as soon as the browser sees it).
Since JavaScript can only do one thing at a time (it is a single threaded language), we typically get around this by using a callback function:
// a common design pattern in JavaScript
const callback = (result, error) => {
if (error) { console.log(error) }
console.log(result)
}
asynchronousGetDataFunction(callback)
With this syntax, the asynchronousGetDataFunction
will execute immediately, but it won’t hold up the rest of the program’s execution, because it will simply wait until it receives a response, at which point it will call the callback function.
This can be troublesome, because we have to nest subsequent asynchronous calls inside of each other, leading to the eventual problem of callback hell, which makes it difficult to read what our program is doing.
Promises
We get around callback hell by using Promises. Promises are values in JavaScript which can be either pending, resovled, or rejected.
Rather than having to wait for our asynchronous call to resolve before we can keep the rest of the program running, Promises let us pass this indeterminate value around in our code, whilst it is still being resolved.
With a Promise, we can use the then()
method to define the callback function that we should use in the case that the Promise is eventually resolved with no errors. We can also use the catch()
method to handle any errors:
getData()
.then(result => {
// resolved result
console.log(result)
})
.catch(error => {
// this happens when Promise is rejected
console.log(error)
})
HTTP Requests in JavaScript
Typcially, when we want to make a GET request inside our JavaScript file, we can do it like this:
fetch("path/to/url")
.then(response => response.json())
.then(data => {
console.log(data)
// do stuff with the data here!
})
.catch(error => console.log(error))
fetch()
is a function that we get from JavfaScript for free, which we canuse to make HTTP requests. By default, fetch will try to make a GET request, unless we tell it otherwise.
Calling fetch() with a URL returns a Promise of the HTTP response that we get back. When this resolves, then we hit the first then()
method in this chain. The response object has a method call json()
, which also returns a promise, which eventually resolves to the data that we were asking for.
Async / Await Syntax
There’s a slighly nicer way to write this same code, using the “async/await” syntax:
async function getData() {
const response = await fetch("path/to/url")
const data = await response.json()
console.log(data)
}
getData() // invoke this function
Try / Catch Syntax
If we write our code with the async/await syntax, then we’ll want to handle errors using a try/catch block:
async function getData() {
try {
const response = await fetch("path/to/url")
const data = await response.json()
console.log(data)
}
catch (error) {
console.log(error)
}
}
If one of our await
statements results in a rejected Promise in this case, then we will hit the catch block, which will show us what the error is.