Table of Contents
- Chapter 2. JavaScript for React
- 2.1 Declaring Variables
- 2.2 Creating Functions
- 2.2.1 Function Declarations
- 2.2.2 Function Expressions
- 2.2.3 Default Parameters
- 2.2.4 Arrow Functions
- 2.3 Compiling JavaScript
- 2.4 Objects and Arrays
- 2.4.1 Destructuring Objects
- 2.4.2 Destructuring Arrays
- 2.4.3 Object Literal Enhancement
- 2.4.4 The Spread Operator
- 2.5 Asynchronous JavaScript
- 2.6 Classes
- 2.7 ES6 Modules
Chapter 2. JavaScript for React
This chapter introduce necessary JS syntax for working with React.
2.1 Declaring Variables
Before ES2015, var
is the only way to declare variable. Now we have more ways in ES6.
2.1.1 The const
Keyword
A lot of variables we created in JS should not be overwritten, we'll use const
const pizza = true;
pizza = false; // Error
Error message in browser: Uncaught TypeError: Assignment to constant variable.
2.1.2 The let
Keyword
JS (since JS6) has now lexical (relating to the words or vocabulary of a language) variable scope, by create code blocks with {}
var
is scoped in function block, but not for other blocks (e.g.if/else
,for
).let
scope a variable to any code block
2.1.3 Template Strings
Template String:
- provide a second way of string concatenation (shown below)
- Allow us to insert variables into a string
- aka template strings, template literals, string templates
Traditional string concatenation:
console.log(lastName + ", " + firstName + " " + middlename);
String concatenation using template (insert variable values by surrounding them with ${}
):
console.log('${lastName}, ${firstName} ${middleName}');
Hence, template string is useful for long text
2.2 Creating Functions
2.2.1 Function Declarations
Function declaration and function definition in JavaScript is the same thing.
- How to create function using function declaration/definition: starts with
function
, followed by name of the function - Use function by call it
// Function declaration/definition
function logCompliment() {
console.log("You're doing great!");
}
// invoke
logCompliment();
Where:
logCompliment
is the function name.
2.2.2 Function Expressions
Function expression = another option of define function
- How to create function using function expression: creating function as a variable
const logCompliment = function() {
console.log("You're doing great!");
};
logCompliment(); // use function expression
Hoisting (悬浮):
- Function declaration are hoisted: you can invoke a function before the function declaration is written
- Function expressions are not: you cannot invoke a function before the expression is made.
hey(); // TypeError: hey is not a function
const hey = function() {
alert("hey!");
};
Passing arguments
Passing arguments by: adding named parameters into function parentheses ()
const logCompliment = function(firstName, message) {
console.log(`You're doing great, ${firstName}: ${message}`);
};
logCompliment("Molly", "You're so cool");
Function returns
Use return
statement to specify values returned by function
const createCompliment = function(firstName, message) {
return `${firstName}: ${message}`;
};
createCompliment("Molly", "You're so cool");
2.2.3 Default Parameters
New feature included in ES6
function logActivity(name = "Shane McConkey", activity = "skiing") {
console.log(`${name} loves ${activity}`);
}
2.2.4 Arrow Functions
New feature included in ES6.
Arrow function allow us to
- create functions without using
function
keyword. - no need to use
return
keyword - If function only takes one argument, parentheses around argument can be removed
- create multi-line function easily
// function declaration
const lordify = function(firstName) {
return `${firstName} of Cantebury`;
}
// simplified arrow function
const lordify = firstName => `${firstName} of Canteberry`;
Where:
- parenthese of arrow function arround parameter
(firstName)
can be removed
Arrow function with multiple arguments:
const lordify = (firstName, land) => `${firstName} of ${land}`;
Multi-line arrow function using curely brace {}
:
const lordify = (firstName, land) => {
if (!firstName) {
throw new Error("A firstName is required to lordify");
}
if (!land) {
throw new Error("A lord must have a land");
}
return `${firstName} of ${land}`;
}
console.log(lordify("Kelly", "Sonoma"));
Returning objects
If we want to specify that we want to return an object, wrap the returned object with ()
. Otherwise, we will see error Uncaught SyntaxError: Unexpected token
const person = (firstName, lastName) => {
({
first: firstName, last:lastName
})
};
Where:
- Outest
{}
is arrow function block ()
is for returned object- Inner
{}
is object constructor
Arrow functions and scope
Regular functions (function declaration/definition, expressions) do not block this
(i.e. global object is referred). Solution is using arrow function syntax to protect the scope of this
// Declare tahoe object
const tahoe = {
mountains: ["Freel", "Rose", "Tallac", "Rubicon", "Silver"],
print: function(delay = 1000) {
setTimeout(function() {
console.log(this.mountains.join(", "));
}, delay);
}
};
tahoe.print(); // Uncaught TypeError: Cannot read property 'join' of undefined
Where:
console.log(this);
will show that this this refer toWindow
object, as function does not blockthis
const tahoe = {
mountains: ["Freel", "Rose", "Tallac", "Rubicon", "Silver"],
print: function (delay = 1000) {
setTimeout(() => {
console.log(this.mountains.join(", "));
}, delay);
}
};
tahoe.print(); // Freel, Rose, Tallac, Rubicon, Silver
Where:
print
is a function in whichsetTimeout
take arrow function as parameter
Notice: arrow functions do not block off the scope of this
const tahoe = {
mountains: ["Freel", "Rose", "Tallac", "Rubicon", "Silver"],
print: (delay = 1000) => {
setTimeout(() => {
console.log(this.mountains.join(", "));
}, delay);
}
};
tahoe.print(); // Uncaught TypeError: Cannot read property 'join' of undefined
Where:
- Changing
print
function to an arrow function menas thatthis
is actually window.
2.3 Compiling JavaScript
Compiling = convert JS to more widely compatible code, so new features can be run on old browsers. Babel
2.4 Objects and Arrays
2.4.1 Destructuring Objects
- Destructring assignment = locally scope (isolate) fields within an object and to declare which values will be used.
- Destructed variables using
let
can be changed.
- Destructed variables using
const sandwich = {
bread: "dutch crunch",
meat: "tuna",
cheese: "swiss",
toppings: ["lettus", "tomato", "mustard"]
};
// Destructing assignment using const
const { bread, meat } = sandwich;
// Destructed variable using let can be changed
let { bread, meat } = sandwich;
bread = "garlic";
meat = "turkey";
console.log(sandwich.bread, sandwich.meat); // dutch crunch tuna
- Incoming function arguments can be destructed, so no need to parse entire object
const lordify = ({ firstname }) => {
console.log(`${firstname} of Cantebury`);
}
const regularPerson = {
firstname: "Bill",
lastname: "Wilson"
};
lordify(regularPerson); // Bill of Cantebury
- Using colon
:
and nested curly braces{}
, we can destruct nested object
const lordify = ({ spouse: {firstname} }) => {
console.log(`${firstname} of Cantebury`);
};
const regularPerson = {
firstname: "Bill",
lastname: "Wilson",
spouse: {
firstname: "Phil",
lastname: "Wilson"
}
};
lordify(regularPerson); // Phil of Cantebury
2.4.2 Destructuring Arrays
- Values can be destructured from arrays using Destructor
- We can skip unnecessary values with list matching using
,
(comma).
- We can skip unnecessary values with list matching using
const [firstAnimal] = ["Horse", "Mouse", "Cat"];
console.log(firstAnimal); // Horse
const [, , thirdAnimal] = ["Horse", "Mouse", "Cat"];
console.log(thirdAnimal); // Cat
2.4.3 Object Literal Enhancement
- Object literal enhancement = process of restructuring or putting object back together.
- original variable name become keys of the constructed object.
const name = "Tallac";
const elevantion = 9738;
const print = function() {
console.log(`Mt. ${this.name} is ${this.elevation} feet tall`); // use `this` to access object keys.
};
const funHike = { name, elevation, print }; // Object literal enhancement
funHike.print(); // Mt. Tallac is 9738 feet tall
- ES6 feature: object literal enhancement allow us to pull global variables into objects and reduces typing by making
function
keyword unnecessary.
const name = "Tallac";
const elevantion = 9738;
// Pre ES6
var skier = {
name: name,
sound: sound,
powderYell: function() {
var yell = this.sound.toUpperCase();
console.log(`${yell} ${yell} ${yell}!!!`);
},
speed: function(mph) {
this.speed = mph;
console.log("speed:", mph);
}
};
// ES6
const skier = {
name,
sound,
powderYell() {
let yell = this.sound.toUpperCase();
console.log(`${yell} ${yell} ${yell}!!!`);
}
speed(mph) {
this.speed = mph;
console.log("speed:", mph);
}
}
2.4.4 The Spread Operator
Spread Operator ...
have multiple usages:
- copy contents of arrays for combining a new one:
...array
will copy all objects as a new flat array to return. - get remaining items in array
- collect function arguments as an array
- Use on object (similar as on arrays)
Usage 1: Combine/Copy content of arrays
const peaks = ["Tallac", "Ralston", "Rose"];
const canyons = ["Ward", "Blackwood"];
const tahoe = [...peaks, ...canyons]; // flat/spread and combine
console.log(tahoe.join(", ")); // Tallac, Ralston, Rose, Ward, Blackwood
As copied/spreaded array is a copy version, array operation on copied version means original array is immutable
const peaks = ["Tallac", "Ralston", "Rose"];
const [last] = [...peaks].reverse(); // spread operator to copy array + destructor
console.log(last); // Rose
console.log(peaks.join(", ")); // Tallac, Ralston, Rose
Usage 2: Get remaining items in array
const lakes = ["Donner", "Marlette", "Fallen Leaf", "Cascade"];
const [first, ...others] = lakes;
console.log(others.join(", ")); // Marlette, Fallen Leaf, Cascade
Usage 3: Collect function arguments as an array
Argument collected as an array are called Rest Parameters
function directions(...args) { // collect function arguments as an array
let [start, ...remaining] = args;
let [finish, ...stops] = remaining.reverse();
console.log(`drive through ${args.length} towns`);
console.log(`start in ${start}`);
console.log(`the destination is ${finish}`);
console.log(`stopping ${stops.length} times in between`);
}
Usage 4: Use spread operator on objects to combine properties.
const morning = {
breakfast: "oatmeal",
lunch: "peanut butter and jelly"
};
const dinner = "mac and cheese";
const backpackingMeals = {
...morning,
dinner
};
console.log(backpackingMeals);
// {
// breakfast: "oatmeal",
// lunch: "peanut butter and jelly",
// dinner: "mac and cheese"
// }
2.5 Asynchronous JavaScript
Async tasks: tasks have to wait for some work to finish before they can be completed.
- e.g.
- Access a database
- Stream video or audio content.
- Fetch data from an API.
- Async JS's main thread can do others while waitinig for API to return data.
2.5.1 Simple Promises with Fetch
Steps of using fetch
fetch(URL)
to make a GET request to api, which returns a promise. Promise is an object that represent whether the async operation is pending, has been completed or failed.- Pending promise will be chained with multiple
.then()
functions, which take in a callback function..then()
function will run callback if previous operation was successful. - Use
.catch()
at end to invoke a callback iffetch
did not resolve successfully
fetch("https://api.randomuser.me/?nat=US&results=1")
.then(res => res.json())
.then(json => json.results)
.then(console.log)
.catch(console.error);
2.5.2 Async/Await
Another way of handling promise: create an async function.
- Instead of waiting promise to resolve and handling it with a chain of
then
function,async
functions are told to wait for the promise to resolve before further execution - function declared with
async
are async functions, which can wait for promises to resolve before further execution.await
in async function tell the async functioin there is a promise to resolve.
const getFakePerson = async() => {
try {
let res = await fetch("https://api.randomuser.me/?nat=US&results=1");
let { results } = res.json();
console.log(results);
} catch (error) {
console.error(error);
}
};
getFakePerson();
getFakePerson
is a async function declared usingasync
keyword, which makes it async so it can wait for promises to resolve before executing the code any further.await
tellsfetch(...)
is a promise to be resolved.
2.5.3 Building Promises
Async request can fail due to multiple reasons. Customerized promise can help us to simplify these complicated output to a simple pass or fail.
Create Promise
const getPeople = count =>
new Promise((resolves, rejects) => {
const api = 'https://api.randomuser.me/?nat=US&results=${count}`;
const request = new XMLHttpRequest();
request.open("GET", api); // Promise makes a request to the API
request.onload = () =>
request.status === 200
? resolves(JSON.parse(request.response).results) // If the promise is successful, the data will load
: reject(Error(request.statusText)); // If the promise is unsuccessful, an error will occur
request.onerror = err => rejects(err);
request.send();
});
Calling getPeople
getPeople(5)
.then(members => console.log(members))
.catch(error => console.error(`getPeople failed: ${error.message}`));
- This promise can be used by calling
getPeople
function and passing in the number of members that should be loaded. .then
function can be chained aftergetPeople
to do sth once promise has been fulfilled.- When a promise is rejected, any details are passed to
catch
.
2.6 Classes
Before ES2015 (i.e. before classes), JS use prototypical inheritance to create structures that feel OOP
function Vacation(destination, length) {
// Vaction has properties
this.destination = destination;
this.length = length;
}
// Vaction has method
Vaction.prototype.print = function() {
console.log(this.destination + " | " + this.length + " days");
};
const maui = new Vaction("Maui", 7);
maui
instance inheritsprint
method through prototype
ES2015 introduced class declaration, it's just a wrapper over the prototype syntax shown above.
- Functions are objects, inheritance is handled through prototype
// Define a class
class Vacation {
constructor(destination, length) { // Define constructor of this class
this.destination = destination;
this.length = length;
}
print() {
console.log(`${this.destination} will take ${this.length} days`);
}
}
// Instantiate an object
const trip = new Vacation("Santiago, Chile", 7);
trip.print(); // Chile will take 7 days.
Inheritance of JS classes are done through extend
:
- When a class is extended, the subclass inherits all properites and methods of superclass.
class Expedition extends Vacation {
constructor(destination, length, gear) {
super(destination, length); // inherit superclass's constructor
this.gear = gear;
}
print() { // add new properties/methods to subclasses
super.print();
console.log(`Bring your ${this.gear.join(" and your ")}`);
}
}
const trip = new Expedition("Mt. Whitney", 3, [
"sunglasses",
"prayer flags",
"camera"
]);
trip.print();
// Mt. Whitney will take 3 days.
// Bring your sunglasses and your prayer flags and your camera
2.7 ES6 Modules
JS module: reusable code import from other files
- Stored in separate files, one file per module (i.e. each file is a separate module)
- 2 options to export a module:
-
- Export multiple JS objects from a single module
-
- Export entire module as JS object
-
Export objects in module
export
can be used to export any JS type that will be consumed in another module.- If module only export 1 single object, use
export default
export
can be used on any JS type: primitives, objects, arrays, and functions.
e.g. Assume a module text-helpers.js
export 2 functions:
export const print = (message) => log(message, new Date())
export const log = (message, timestamp) => console.log(`${timestamp.toString()}: ${message}`)
Consume module in JS
import
is used to import JS objects from a module- Module with multiple exports can use destructor to import multiple objects at onece
- Module variable can be scoped/renamed locally using different name
- Import everything using
*
import { print, log } from "./text-helpers";
import { print as p, log as l } from "./text-helpers"; // scope locally
import * as fns from './text-helpers'
2.7.1 CommonJS
CommonJS is the module pattern supported by all versions of Node, hence Babel and webpack recognize it.
With CommonJS
- JS objects are exported using
module.exports
- Import moduels using
require
function
e.g. export module
const print(message) => log(message, new Date(0))
const log(message, timestamp) => console.log(`${timestamp.toString()}: ${message}`)
module.exports = {print, log}
e.g. consume module
const { log, print } = require("./txt-helpers");