2. Variables and Data Structures
1) let keyword
- let is helping us enforce block scoping in JavaScript.
// Example 1(1) - var
var topic = "JavaScript";
if (topic) {
var topic = "ECMAScript";
console.log("block", topic);
}
console.log("global", topic); // expected "global ECMAScript"
// Example 1(2) - let
var topic = "JavaScript";
if (topic) {
let topic = "ECMAScript";
console.log("block", topic);
}
console.log("global", topic); // expected "global JavaScript"
// Example 2
<!DOCTYPE html>
<html>
<head>
<title>ECMAScript</title>
<style>
#box {
display: flex;
justify-content: space-around;
}
#box > div {
height: 5em;
width: 5em;
background-color: purple;
}
</style>
</head>
<body>
<h1>Let</h1>
<div id="box"></div>
<script type="text/javascript">
var div;
var box = document.getElementById("box");
for (let i = 0; i < 5; i++) {
div = document.createElement("div");
div.onclick = function () {
alert("This is box # " + i);
};
box.appendChild(div);
}
</script>
</body>
</html>
for (var i = 0; i < 5; i++) {
div = document.createElement("div");
div.onclick = function () {
alert("This is box # " + i);
};
box.appendChild(div);
}
- In this case, the alert will only print "This is box # 5" because var is not scoped protected. The value of i is kept changing when looping.
2) const keyword
<!DOCTYPE html>
<html>
<head>
<title>ECMAScript</title>
</head>
<body>
<h1>Const</h1>
<script type="text/javascript">
const pizza = true;
pizza = false;
console.log(pizza);
</script>
</body>
</html>
3) template strings
function print(firstName) {
console.log(`Hello ${firstName}`);
}
print("Jenny");
function createEmail(firstName, price) {
let shipping = 5.95;
console.log(`Hi ${firstName}! Thanks!
Total: $${price}
Shipping: $${shipping}
Grand Total: $${price + shipping}
`);
}
createEmail("Guy", 100);
4) Searching strings
- startsWith, endsWith, includes, and search functions
- search function tells me where (position) the string shows up.
const planet = "Earth";
console.log(planet.startsWith("E")); // case sensitive
console.log(planet.endsWith("h"));
console.log(planet.includes("h"));
console.log(planet.search("h")); // expected 1
5) Symbols
- You can give a unique identifier. If you want to capture a unique identifier and associate that with an object, you can use the symbol.
- If you set an object with a key named id, there is no naming conflict.
const id = Symbol();
const courseInfo = {
title: "JavaScript",
topics: ["strings", "arrays", "objects"],
id: "js-course"
};
courseInfo[id] = 41284;
console.log(courseInfo);
6) Maps
- In a map, any value, both objects, and primitive values may be used either as a key or a value.
- map is different than an object, so you can't just access the keys with dot notation.
let course = new Map();
course.set("react", { description: "ui" });
course.set("jest", { description: "testing" });
// console.log(course);
// console.log(course.react); // expected undefined
// console.log(course.get("react"));
let details = new Map([
[new Date(), "today"],
[2, { javascript: ["js", "node", "react"] }],
["items", [1, 2]]
]);
// console.log(details.size);
details.forEach(function (item) {
console.log(item);
});
7) Sets
- Each value must be unique.
let books = new Set();
books.add("Pride and Prejudice");
books.add("War and Peace").add("Oliver Twist");
books.add("Pride and Prejudice"); // expected not included in the set.
console.log(books);
console.log(books.size);
books.delete("Oliver Twist");
console.log(
"has Oliver Twist",
books.has("Oliver Twist")
);
let books = new Set();
books.map(function (item) {
console.log(item);
});
- This triggers an error that says "book.map is not a function."
3. Arrays and Array Methods
1) Spread Operator
- It is used to spread the data in an array from multiple arrays.
let cats = ["Biscuit", "Jungle"];
let dogs = ["Stella", "Camper"];
let animals = [
"Smoky",
"Miro",
"Swimmy",
...cats,
...dogs
];
2) Destructuring Arrays
- It can be used to assign a variable name to a position in an array
// Not using destructuring
let cities = [
"Spokane",
"Boston",
"Los Angeles",
"Seattle",
"Portland"
];
console.log(cities[0]);
// Using desctructuring
let [first, , , , fifth] = [
"Spokane",
"Boston",
"Los Angeles",
"Seattle",
"Portland"
];
console.log(first);
console.log(fifth);
3) .includes function
- Before, you had to use third-party libraries to do this function.
let cities = [
"Spokane",
"Boston",
"Los Angeles",
"Seattle",
"Portland"
];
console.log(cities.includes("Boston"));
console.log(cities.includes("Santa Barbara"));
4. ECMAScript Objects
1) Object Literal Enhancement
// No object literal enhancement
function skier(name, sound) {
return {
name: name,
sound: name,
powderYell: function () {
let yell = this.sound.toUpperCase();
console.log(`${yell}! ${yell}!`);
}
};
}
// Object literal enhancement
function skier(name, sound) {
return {
name,
sound,
powderYell: function () {
let yell = this.sound.toUpperCase();
console.log(`${yell}! ${yell}!`);
}
};
}
skier("Sendy", "yeah").powderYell();
2) Spread Operator for objects
const daytime = {
breakfast: "oatmeal",
lunch: "peanut butter and jelly"
};
const nighttime = "mac and cheese";
const backpackingMeals = {
...daytime,
nighttime
};
console.log(backpackingMeals);
3) Destructuring objects
- Difference between arrays is objects always use curly braces.
- Example 1: put vacation in the argument and the marketing function is using only the destination and activity of the vacation object.
// Example 1
const vacation = {
destination: "Chile",
travelers: 2,
activity: "skiing",
cost: "so much"
};
function marketing({ destination, activity }) {
return `Come to ${destination} and do some ${activity}`;
}
console.log(marketing(vacation));
// Example 2
const { title, price } = {
title: "Reuben",
price: 7,
description: "A classic",
ingredients: [
"bread",
"corned beef",
"dressing",
"sauerkraut",
"cheese"
]
};
console.log(title);
console.log(price);
4) for/of Loop
for (let letter of "JavaScript") {
console.log(letter);
}
let topics = new Map();
topics.set("HTML", "/topic/html");
topics.set("CSS", "/topic/css");
topics.set("JavaScript", "/topic/javascript");
for (let topic of topics.entries()) {
console.log(topic);
}
5) classes
class Vehicle {
constructor(description, wheels) {
this.description = description;
this.wheels = wheels;
}
describeYourself() {
console.log(
`I am a ${this.description}
with ${this.wheels} wheels.`
);
}
}
let coolSkiVan = new Vehicle("cool ski van", 4);
console.log(coolSkiVan);
coolSkiVan.describeYourself();
6) Inheritance
class Vehicle {
constructor(description, wheels) {
this.description = description;
this.wheels = wheels;
}
describeYourself() {
console.log(
`I am a ${this.description}
with ${this.wheels} wheels.`
);
}
}
class SemiTruck extends Vehicle {
constructor() {
super("semi truck", 18);
}
}
// groceryStoreSemi object can use describeYourself function because it extends Vehicle object.
let groceryStoreSemi = new SemiTruck();
groceryStoreSemi.describeYourself();
7) Getter and Setter
// Example 1
let attendance = {
_list: [],
set addName(name){
this._list.push(name);
},
get list(){
return this._list.join(", ");
}
};
attendance.addName = "Joanne Starr";
attendance.addName = "Charlie Charlson";
// Example 2
class Hike {
constructor(distance, pace) {
this.distance = distance;
this.pace = pace;
}
get lengthInHours() {
return `${this.calcLength()} hours`;
}
calcLength() {
return this.distance / this.pace;
}
}
const mtTallac = new Hike(10, 2);
console.log(mtTallac.lengthInHours);
- Setter and Getter look like a function but it is not considered as a function in JavaScript. If you try to call them with braces, it will give you an error.
5. ECMAScript Functions
1) string.repeat Function
let yell = "woo!";
let party = yell.repeat(20);
console.log(party);
let cat = {
meow(times) {
console.log("meow".repeat(times));
},
purr(times) {
console.log("prrr".repeat(times));
},
snore(times) {
console.log("ZzZzZ".repeat(times));
}
};
cat.meow(3);
cat.purr(3);
cat.snore(6);
2) Default function parameters
- If you don't put a parameter, it uses the default parameters.
function add(x = 5, y = 6) {
console.log(x + y);
}
add(1, 2);
function haveFun(
activityName = "hiking",
time = 3
) {
console.log(`Today I will go ${activityName}
for ${time} hours.`);
}
haveFun("biking", 2.5);
haveFun("biking");
3) Arrow Function
- If there is only one statement inside the function, you can erase curly braces.
// =>
// Example 1
// No arrow function
let studentList = function(students) {
console.log(students);
}
// Arrow function
let studentList = (students) =>
console.log(students);
studentList(["A", "B", "C"]);
// Example 2
let list = ["apple", "banana", "strawberry"];
// No arrow function
list.map(function(item){
console.log(item);
})
// Arrow function
list.map((item) => console.log(item));
4) this in Arrow Function
// this with no arrow (1)
let person = {
first: "Angie",
hobbies: ["bike", "hike", "ski"],
printHobbies: function () {
this.hobbies.forEach(function(hobby) {
let string = `${this.first} likes to ${hobby}`;
console.log(string);
});
}
};
person.printHobbies();
- It does not recognize what this is if you use the function without an arrow.
// this with no arrow (2)
let person = {
first: "Angie",
hobbies: ["bike", "hike", "ski"],
printHobbies: function () {
let _this = this;
this.hobbies.forEach(function(hobby) {
let string = `${_this.first} likes to ${hobby}`;
console.log(string);
});
}
};
// this with arrow
let person = {
first: "Angie",
hobbies: ["bike", "hike", "ski"],
printHobbies: function () {
this.hobbies.forEach((hobby) => {
let string = `${this.first} likes to ${hobby}`;
console.log(string);
});
}
};
person.printHobbies();
- A little workaround that you often see in JavaScript code is using other variables to scope "this" to the function.
- Using an arrow is the better approach. The arrow is helping us keep "this" in scope.
5) Generators
- You know you are looking at a generator when you see an asterisk.
- It allows us to pause functions in the middle of execution to be resumed later.
- The yield keyword is going to be used anytime we want to hit pause inside the execution of a function.
- In order to skip to the next yield, we just call the next function.
function* director() {
yield "Three";
yield "Two";
yield "One";
yield "Action";
}
let countdown = director();
console.log(countdown.next().value);
console.log(countdown.next().value);
console.log(countdown.next().value);
console.log(countdown.next());
console.log(countdown.next());
6. Asynchronous JavaScript
1) Promises
- When something is asynchronous, it just means that some sort of waiting is going on. There's a delay between when we ask for something and when we receive it.
- resolve means if everything goes well as expected with the promise, it will resolve, it's the function that's going to be called.
- Anything we are passing into the .then function is "resolve".
const delay = (seconds) =>
new Promise((resolve, reject) => {
if (typeof seconds !== "number") {
reject(
new Error("seconds must be a number")
);
}
setTimeout(resolve, seconds * 1000);
});
console.log("Zero seconds");
delay("one").then(() => console.log("1 sec"));
2) Loading remote data with promises
const spacePeople = () => {
return new Promise((resolves, rejects) => {
const api =
"http://api.open-notify.org/astros.json";
const request = new XMLHttpRequest();
request.open("GET", api);
request.onload = () => {
if (request.status === 200) {
resolves(JSON.parse(request.response));
} else {
rejects(Error(request.statusText));
}
};
request.onerror = (err) => rejects(err);
request.send();
});
};
spacePeople().then(
(spaceData) => console.log(spaceData),
(err) =>
console.error(new Error("Can't load people"))
);
3) Returning promises with fetch (This is to make #2 in a better way)
- The fetch function is built into every single browser, so you can use this instead of writing those long XML, HTTP requests.
- The fetch returns a promise. It is just a cleaner wrapper around them.
let getSpacePeople = () =>
fetch(
"http://api.open-notify.org/astros.json"
).then((res) => res.json());
let spaceNames = () =>
getSpacePeople()
.then((json) => json.people)
.then((people) => people.map((p) => p.name))
.then((names) => names.join(", "));
spaceNames().then(console.log);
// spaceNames().then((data) => console.log(data));
// is also allowed but I don't want to repeat the same word 'data'.
4) async/await syntax
- Async functions return a promise and resolve the value of the promise.
- Await is only valid in the async function.
const delay = (seconds) =>
new Promise((resolves) =>
setTimeout(resolves, seconds * 1000)
);
const countToFive = async () => {
console.log("zero seconds");
await delay(1);
console.log("one second");
await delay(2);
console.log("two seconds");
await delay(3);
console.log("three seconds");
};
countToFive();
5) Incorporating fetch with async/await
- It usually is used to fetch data from external APIs.
- We wait for all sorts of things to occur.
- First, we're waiting for a fetch from an API. Then we're going to convert that response to JSON. Once we have it, we can log it into the console.
const githubRequest = async (login) => {
let response = await fetch(
`https://api.github.com/users/${login}`
);
let json = await response.json();
let summary = `${json.name}, ${json.company}`;
console.log(summary);
};
githubRequest("eveporcello");
All the sources and codes are from a lecture on LinkedIn Learning called "Learning ECMAScript 6+ (ES6+)" lectured by Eve Porcello.