Web Dev Bootcamp ∙ Learn Javascript Part 2 ∙ ES6
Hieu Nguyen · May 05, 2020 · 10 min read
0
0 leave some love!
Goals
- understand what ES6 is
- Learn the most common ES6 features
What is ES6?
ES6 is the next version of Javascript following ES5. It was released in 2011 an introduced many useful changes for developers
ES6 Features
Let keyword
let
is an alternative to var
that allows you to scope a variable to a block as opposed to the global/function scope of a var
function fn() {
var globalVar = 2
if (true) {
let blockVar = 3 // can only be accessed inside this if block
console.log(globalVar, blockVar)
}
console.log(globalVar)
console.log(blockVar) // will produce an error because blockVar is not been defined in the function scope
}
fn() // will generate error
const keyword
the const
keyword lets us create variables that can only be assigned once and can not be changed
const myConst = "Dogs are great!"
myConst = "no they are not" // will generate an error because we are trying to change a constant
Arrow functions
Arrow functions are used to simplify how we write functions and to make them more concise
//traditional function
function double(num) {
return num * 2
}
double(2) // 4
//rewritten using arrow function
var double = num => num * 2
double(2) // 4
Arrow functions are denoted by =>
. On the left side we have the parameters and on the right side be have the function definition
There are several caveat we have to be aware of when using arrow functions
- Multiple parameters require a
()
ex.
const singlePara = param1 => console.log(param1)
const multiplePara = (param1, param2) => console.log(param1, param2)
- Function definitions that do not use
{}
are implicitly saying ‘return whatever is in the definition’
ex.
const double = num => num * 2 // since we are not using {} to encase the function definition, we are saying do num*2 and return it
const doSomething = () => ({
name: "charlie",
type: "dog",
}) // Not using {}, but since we are returning something that spans multiple line we are using ()
// Inside the (), we have an object {name : 'charlie', type: 'dog'} which will be returned
const fn = () => {
// some code here
return result
} // Now we are using {} to encase the function definition, so we have to add a return statement if we want to return something
- We can also pass arrow functions to other function
var arr = [1, 2, 3]
// passing function using anonymous function
arr.map(function(currentElement) {
return currentElement * 2
})
// the new way of doing things...
arr.map(currentElement => currentElement * 2)
classes
classes are part of OOP(object orientated programming) paradigm. This design insists that data should be represented as human readable objects or classes
Let’s learn how to create classes in Javascript
Declaring a class
class MyClass {}
Member variables and functions
class MyClass {
name
age
constructor(n, a) {
this.name = n
this.age = a
}
toString() {
return `name: ${this.name} age: ${this.age}`
}
}
Here we have two public member variables name
and age
We are initializing these members using the constructor
. Each class can only have one constructor.
The purpose of the constructor is to initialized our object. Here we are assigning our name
and age
We have a member function called toString()
which prints the name and age of our object
Creating an object from class
var obj = new MyClass("devsurvival", 1) // we use the new keyword to create a new object and pass in the name and the age for the constructor
console.log(obj.toString()) // accessing the member function toString()
console.log(obj.name) // getting the name
obj.age = 2 // setting age
Setters/Getters and Private Members
There is a rule of thumb in programming that states that all member variables should be private and they can only be modified or accessed via setters and getters.
class MyClass {
#name
#age
constructor(name, age) {
this.#name = name
this.#age = age
}
set name(n) {
this.#name = n
}
get name() {
return `name: ${this.#name}`
}
#myPrivateFunction() {
console.log(
"this is private and can only be accessed inside the class body"
)
}
}
var obj = new MyClass("devsurvival", 2)
obj.name = "charlie" // using setter to set the name
console.log(obj.name) // using getter to get the name
obj.#age = 2 // will generate an error
obj.myPrivateFunction() // will generate an error
Default
defaults allows to optionally pass in a parameter
function fn(value = 0) {
console.log(value)
}
fn() // 0
fn(1) //1
For of loop
allows you to loop over the elements on an array without explicitly knowing the length of the array
var arr = [1, 2, 3]
for (let v of arr) {
console.log(v)
}
// prints 1, 2,3
For in loop
looping through the keys of an object
var object = { name: "devsurvival", age: 2 }
for (let key in object) {
console.log(object[key])
}
Tempate Strings
You can embedded variable inside of strings without using messy string concatenation
//traditional Javascript
var str = "hello " + name + "You wanted " + donuts + " donuts"
// ES5
var str = `hello ${name}. You wanted ${donuts} donuts`
variables used in template string are encased in a ${}
Destructuring
destructing allows you parse an object and get certain keys
let’s say you want to get the key: ‘name’ from the object, traditionally you would do obj.name
each time you want to access the name
Wouldn’t it be better to parse the name once and use it? That is what destructuring allows you to do
var obj = { name: "devsurvival", age: 2 }
const { name } = obj
You can also provide an alias for a key!
var obj = { name: "devsurvival", age: 2 }
const { name: nameAlias } = obj
console.log(nameAlias) // nameAlias is an alias for the name key
Spread Operator
The spread operator is used to spread an array or an object. Let me explain using an example
var arr1 = [1, 2, 3]
var arr2 = [4, 5, 6]
// let's say we want to append arr2 to arr1
arr2.forEach(currentElement => arr1.push(currentElement))
// using spread operator, we can right this more concisely
var arr1 = [...arr1, ...arr2] // we are spreading the values of each array into a new array
How About Objects?
you can also spread objects!
function addKeys(obj1, obj2) {
return { ...obj1, ...obj2 }
}
var obj = { name: "devsurvival" }
var obj2 = { age: 2, favoriteFood: "donut" }
addKeys(obj, obj2)
here we created a function addKeys
that takes the key value pairs in the second object and spread them into the first object
Rest operator
The rest operators is kinda like spread
counter part. Instead of spreading things, it gathers them!
let’s see some use cases
we can use rest operators in functions to pass a variable number of parameters
function fn(firstParam, ...otherParams) {
console.log(firstParam)
console.log(otherParams)
}
fn(1, 2, 3, 4)
// prints 1
// prints [2,3,4]
It can also be used to gather the keys in an object
const object = { name: "devsurvival", age: 3, food: "chicken nuggets" }
const { food, ...restOfTheKeys } = object
// food = 'chicken nuggets'
// restOfTheKeys = {name: "devsurvival", age: 3}
import and export
importing and exporting are how we share codes across multiple files
// file1.js
//named export
export function fn() {
console.log("function from file1")
}
// file2.js
import { fn } from "file1.js"
fn()
default export
// file1.js
export default function() {
console.log("another function from file 1")
}
// file2.js
import fn from "file1.js"
fn()
Sets and Map
Sets
are just a list of unique things. A set can contain numbers and strings
var set = new Set()
set.add(1)
set.add("one")
set.add(1) // will not be added again since 1 already exists in the set
// set = [1, "one"]
Map
Maps are like dictionaries that allows you to search for a key and get the value based on the key
var map = new Map()
const key = "name"
map.set(key, "devsurvival")
map.get(key) // name: 'devsurvival'
setTimeout and setInterval
setTimeout
allows you to execute some code after a specified amount of milliseconds has passed
const timeoutId = setTimeOut(() => {
console.log("one second has passed!")
}, 1000)
// it will print out the message after one second has passed
What if something happens and we want to cancel the setTimeout?
const timeoutId = setTimeOut(() => console.log("one second has passed!"), 1000)
clearTimeout(timeoutId) // each setTimeout() returns an id of the timeout which is used for clearing/cancelling the timeout
setInterval
is used to perform some operation with a given time interval
const setIntervalId = setInterval(
() => console.log("another second has passed"),
1000
)
// this will print the message every second
Promises
promises are asynchronous operations meaning they do not block the program execution after being invoked.
This is commonly used when fetching data from some external source which is unpredictable in how long it takes to respond. It could be instantaneous, 2 seconds, or a couple of hours. We just don’t know
console.log("doing something...")
console.log("fetched data: ", fetchData()) // this is asynchronous
console.log("finished doing something...")
//output:
// "doing something..."
// "finished doing something..."
// "fetched data: ..."
In the program above, Javascript will not wait for fetching the data to complete before printing ‘finished doing something…’
How do we wait to get a response before proceeding?
console.log("doing something...")
fetchData()
.then(data => {
console.log("fetched data: ", data)
console.log("finished doing something...")
})
.catch(error => {
console.log("there was an error :( ", error)
})
We use .then()
and .catch()
. .then()
will wait for fetching data to complete and pass the result into the data
parameter
.catch()
will catch any error that is returned
Creating a Promise
var newProm = new Promise((resolve, reject) => {
// do something ...
if (2 === 2) {
resolve(2)
// if error
reject("sorry")
}
})
newProm.then(res => console.log(res)) // prints 2
Async and Await
What happens when we have a series of request that depends of the result of the previous request
using .then()
function makeRequests() {
request1().then(result => {
request2(result).then(result => {
request3(result).then(result => {
request(4).then(result => {
//...
})
})
})
})
}
this nesting is hard to read and harder to debug.
To avoid this, they introduced Async and Await
Same implementation using Async and Await
async function makeRequests() {
const result1 = await request1()
const result2 = await request2(result1)
const result3 = await request3(result2)
const result4 = await request4(result3)
}
To use await
to wait for our requests, we have to mark the function with async
Cool right? But there are some caveats when using async functions:
- All functions marked with
async
returns a promise - Don’t stick an
await
in front of every promises! If you don’t really care about the return value, just let them be asynchronous.
Error Handling
How do we handle errors?
try {
// do something that can cause an error
// let's see.. I know! let's reference a variable that doesn't exists!'
value / 0
} catch (err) {
console.log(err)
}
What’s Next?
Start learning bash commands