DEV Community

frontendexp
frontendexp

Posted on • Originally published at javascript.plainenglish.io on

The JavaScript you need to know for React — Part 2

Agenda
Declaring Variables in ES6
Template Strings
Default Parameters
Arrow Functions
Transpiling ES6
Part 2
ES6 Objects and Arrays
Destructuring
Object Literals
The Spread Operator
Promises
ES6 Modules
CommonJS
Functional JavaScript — Part 3
ES6 Objects and Arrays
ES6 gives us new ways for working with objects and arrays and for scoping the variables within these datasets.
These features include destructuring, object literal enhancement, and the spread operator and rest operator.
Destructuring
Destructuring allows you to locally scope fields within an object and to declare which values will be used.
Consider this sandwich object. It has four keys, but we only want to use the values of two.
We can scope bread and meat to be used locally:
var sandwich = {
bread: “dutch crunch”,
meat: “tuna”,
cheese: “swiss”,
toppings: [“lettuce”, “tomato”, “mustard”]
}
var {bread, meat} = sandwich
console.log(bread, meat) // dutch crunch tuna
The code pulls bread and meat out of the object and creates local variables for them.
Also, the bread and meat variables can be changed:
var {bread, meat} = sandwich
bread = “garlic”
meat = “turkey”
console.log(bread) // garlic
console.log(meat) // turkey
console.log(sandwich.bread, sandwich.meat) // dutch crunch tuna
We can also destructure incoming function arguments. Consider this function that would log a person’s name as a lord:
var lordify = regularPerson => {
console.log(${regularPerson.firstname} of Canterbury)
}
var regularPerson = {
firstname: “Bill”,
lastname: “something “
}
lordify(regularPerson) // Bill of Canterbury
Instead of using dot notation syntax to dig into objects, we can destructure the values that we need out of regularPerson:
var lordify = ({firstname}) => {
console.log(${firstname} of Canterbury)
}
lordify(regularPerson) // Bill of Canterbury
Destructuring is also more declarative, meaning that our code is more descriptive about what we are trying to accomplish.
By destructuring firstname, we declare that we will only use the firstname variable.
Values can also be destructured from arrays.
Imagine that we wanted to assign the first value of an array to a variable name:
var [firstResort] = [“Kirkwood”, “Squaw”, “Alpine”]
console.log(firstResort) // Kirkwood
We can also pass over unnecessary values with list matching using commas. List matching occurs when commas take the place of elements that should be skipped.
With the same array, we can access the last value by replacing the first two values with commas:
var [,,thirdResort] = [“Kirkwood”, “Squaw”, “Alpine”]
console.log(thirdResort) // Alpine
Object Literals
Object literal enhancement is the opposite of destructuring.
It is the process of restructuring or putting back together.
With object literal enhancement, we can grab variables from the global scope and turn them into an object:
var name = “Tallac”
var elevation = 9738
var funHike = {name,elevation}
console.log(funHike) // {name: “Tallac”, elevation: 9738}
name and elevation are now keys of the funHike object.
We can also create object methods with object literal enhancement or restructuring:
var name = “Tallac”
var elevation = 9738
var print = function() {
console.log(Mt. ${this.name} is ${this.elevation} feet tall)
}
var funHike = {name,elevation,print}
funHike.print() // Mt. Tallac is 9738 feet tall
Notice we use this to access the object keys.
When defining object methods, it is no longer necessary to use the function keyword
// OLD
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)
}
}
// NEW
const skier = {
name,
sound,
powderYell() {
let yell = this.sound.toUpperCase()
console.log(${yell} ${yell} ${yell}!!!)
},
speed(mph) {
this.speed = mph
console.log(‘speed:’, mph)
}
}
Object literal enhancement allows us to pull global variables into objects and reduces typing by making the function keyword unnecessary.
The Spread Operator and The Rest Operator
The spread operator is three dots (…) that perform several different tasks.
First, the spread operator allows us to combine the contents of arrays.
For example, if we had two arrays, we could make a third array that combines the two arrays into one:
var peaks = [“Tallac”, “Ralston”, “Rose”]
var canyons = [“Ward”, “Blackwood”]
var tahoe = […peaks, …canyons]
console.log(tahoe.join(‘, ‘)) // Tallac, Ralston, Rose, Ward, Blackwood
All of the items from peaks and canyons are pushed into a new array called tahoe.
Let’s take a look at how the spread operator can help us deal with a problem.
Using the peaks array from the previous sample, let’s imagine that we wanted to grab the last item from the array rather than the first.
We could use the Array.reverse method to reverse the array in combination with array destructuring:
var peaks = [“Tallac”, “Ralston”, “Rose”]
var [last] = peaks.reverse()
console.log(last) // Rose
console.log(peaks.join(‘, ‘)) // Rose, Ralston, Tallac
See what happened? The reverse function has actually altered or mutated the array.
In a world with the spread operator, we don’t have to mutate the original array; we can create a copy of the array and then reverse it:
var peaks = [“Tallac”, “Ralston”, “Rose”]
var [last] = […peaks].reverse()
console.log(last) // Rose
console.log(peaks.join(‘, ‘)) // Tallac, Ralston, Rose
Since we used the spread operator to copy the array, the peaks array is still intact and can be used later in its original form.
The spread operator can also be used to get some, or the rest, of the items in the Array:
var lakes = [“Donner”, “Marlette”, “Fallen Leaf”, “Cascade”]
var [first, …rest] = lakes
console.log(rest.join(“, “)) // “Marlette, Fallen Leaf, Cascade”
We can also use the spread operator to collect function arguments as an array.
Here, we build a function that takes in n number of arguments using the spread operator and then uses those arguments to print some console messages:
Rest/Spread Properties
function directions(…args) {
var [start, …remaining] = args
var [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)
}
directions(
“Truckee”,
“Tahoe City”,
“Sunnyside”,
“Homewood”,
“Tahoma”
)
The directions function takes in the arguments using the spread operator.
The first argument is assigned to the start variable.
The last argument is assigned to a finish variable using Array.reverse. We then use the length of the arguments array to display how many towns we’re going through.
The number of stops is the length of the arguments array minus the finish stop.
This provides incredible flexibility because we could use the directions function to handle any number of stops. The spread operator can also be used for objects.
Using the spread operator with objects is similar to using it with arrays. In this example, we’ll use it the same way we combined two arrays into a third array, but instead of arrays, we’ll use objects:
var morning = {
breakfast: “oatmeal”,
lunch: “peanut butter and jelly”
}
var dinner = “mac and cheese”
var backpackingMeals = {
…morning,
dinner
}
console.log(backpackingMeals) // { breakfast: “oatmeal”,
// lunch: “peanut butter and jelly”,
// dinner: “mac and cheese”}
What about the rest operator?
The spread operator allows us to spread the value of an array (or any iterable) across zero or more arguments in a function or elements in an array (or any iterable).
The rest operator allows us to pass an indefinite number of parameters to a function and access them in an array.
Promises
Promises give us a way to make sense out of asynchronous behavior.
When we make a request we could try several ways to obtain the data to reach success, we could also receive multiple types of errors.
Imagine an API that has information like email address, name, phone number and location.
The promise makes a request to the API.
If the promise is successful, the data will load and if the promise is unsuccessful, an error will occur.
const getFakeMembers = count => new Promise((resolves, rejects) => {
const api = https://api.randomuser.me/?nat=US&results=${count}
const request = new XMLHttpRequest()
request.open(‘GET’, api)
request.onload = () =>
(request.status === 200) ?
resolves(JSON.parse(request.response).results) :
reject(Error(request.statusText))
request.onerror = (err) => rejects(err)
request.send()
})
With that, the promise has been created, but it hasn’t been used yet.
We can use the promise by calling the getFakeMembers function and passing in the number of members that should be loaded.
The then function can be chained on to do something once the promise has been fulfilled. This is called composition.
We’ll also use an additional callback that handles errors:
getFakeMembers(5).then(
members => console.log(members),
err => console.error(
new Error(“cannot load members from randomuser.me”))
)
Promises make dealing with asynchronous requests easier, which is good, because we
have to deal with a lot of asynchronous data in JavaScript.
Classes
Previously in JavaScript, there were no official classes.
Types were defined by functions. We had to create a function and then define methods on the function object using the prototype:
function Vacation(destination, length) {
this.destination = destination
this.length = length
}
Vacation.prototype.print = function() {
console.log(this.destination + “ | “ + this.length + “ days”)
}
var maui = new Vacation(“Maui”, 7)
maui.print() // Maui | 7 days
This is different from classical object oriented programming. JavaScript is quite unique in this topic.
ES6 introduces class declaration, but JavaScript still works the same way.
Functions are objects, and inheritance is handled through the prototype, but this syntax makes more sense if you come from classical object orientation:
class Vacation {
constructor(destination, length) {
this.destination = destination
this.length = length
}
print() {
console.log(${this.destination} will take ${this.length} days.)
}
}
Capitalization Conventions
The rule of thumb with capitalization is that all types should be capitalized. Due to that, we capitalize all class names.
Once you’ve created the class, you can create a new instance of the class using the new keyword. Then you can call the custom method on the class:
const trip = new Vacation(“Santiago, Chile”, 7)
trip.print() // Chile will take 7 days.
Now that a class object has been created, you can use it as many times as you’d like to create new vacation instances. Classes can also be extended.
When a class is extended, the subclass inherits the properties and methods of the superclass.
These properties and methods can be manipulated from here, but as a default, all will be inherited.
You can use Vacation as an abstract class to create different types of vacations.
For instance, an Expedition can extend the Vacation class to include gear:
class Expedition extends Vacation {
constructor(destination, length, gear) {
super(destination, length)
this.gear = gear
}
print() {
super.print()
console.log(Bring your ${this.gear.join(“ and your “)})
}
}
That’s simple inheritance: the subclass inherits the properties of the superclass.
By calling the print method of Vacation, we can append some new content onto what is
printed in the print method of Expedition. Creating a new instance works the exact same way — create a variable and use the new keyword:
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
Classes and Prototypal Inheritance
Using a class still means that you are using JavaScript’s prototypal inheritance.
Log Vacation.prototype, and you’ll notice the constructor and print methods on the prototype.
The reason for introducing these is because we’ll use them later for creating React components (but not only classes).
ES6 Modules
A JavaScript module is a piece of reusable code that can easily be incorporated into other JavaScript files.
Until recently, the only way to work with modular JavaScript was to incorporate a library that could handle importing and exporting modules.
Now, with ES6, JavaScript itself supports modules and those are stored in separate files, one file per module.
There are two options when creating and exporting a module: you can export multiple JavaScript objects from a single module, or one JavaScript object per module.
export const print(message) => log(message, new Date())
export const log(message, timestamp) =>
console.log(${timestamp.toString()}: ${message})
export can be used to export any JavaScript type that will be consumed in another module.
In this example, the print function and log function are being exported.
Any other variables declared in text-helpers.js will be local to that module. Sometimes you may want to export only one variable from a module. In these cases you can use export:
const freel = new Expedition(“Mt. Freel”, 2, [“water”, “snack”])
export default freel
export default can be used in place of export when you wish to export only one Type.
Again, both export and export default can be used on any JavaScript type: primitives, objects, arrays, and functions.
Modules can be consumed in other JavaScript files using the import statement. Modules with multiple exports can take advantage of object destructuring. Modules that use export default are imported into a single variable:
import { print, log } from ‘./text-helpers’
import freel from ‘./mt-freel’
print(‘printing a message’)
log(‘logging a message’)
freel.print()
You can scope module variables locally under different variable names:
import { print as p, log as l } from ‘./text-helpers’
p(‘printing a message’)
l(‘logging a message’)
You can also import everything into a single variable using *:
import * as fns from ‘./text-helpers
ES6 modules are not yet fully supported by all browsers. Babel does support ES6 modules.
CommonJS
CommonJS is the module pattern that is supported by all versions of Node.js
You can still use these modules with Babel and webpack.
With CommonJS, JavaScript objects are exported using module.exports
CommonJS does not support an import statement.
Instead, modules are imported with the require function:
const { log, print } = require(‘./txt-helpers’)
Many of the features that are included in ES6 are present because they support functional programming techniques.
Stay tuned for part 3 where I will be focusing on Functional JavaScript.

Discussion (0)