At a high level, what are some classic elements of modern JavaScript?
Overview
- ES6 Modules:
import
andexport
syntax - Arrow Functions:
const add = (a, b) => a + b;
- Destructuring:
const { name } = user;
- Spread & Rest Operators:
const newArray = [...oldArray, newItem];
- Promises & Async/Await: Handling async operations
- Template Literals:
console.log(`Hello ${name}`);
- Array Methods (
map
,filter
,reduce
): Functional programming concepts
1. ES6 Modules
Exporting
2 Types:
- Named Exports
- Default Export (only one)
You may export variables, functions, or classes for use in other files once imported. Export multiple using {}
.
export default sayGoodmorning = "Good morning!"; // default export
export const age = 30; // variable
export function sayGoodbye() { return; } // function
export const greet = (name) => `Hello, ${name}!`; // ...variable...function?
export { name1, name2, name3 }; // multiple export
Importing
- Named Exports must be imported using the name they were exported with and must be inside
{}
. - Default Export can be renamed if desired.
- Alternatively, everything can be imported using
*
and an alias, use.default
to access the default module.
import defaultFuncSillyName, { name1, name2 } from './helpers.js'; // import default AND some named exports
import * as utils from './utilities.js'; // import everything
console.log(utils.default(6, 3) + utils.add(3, 4)); // usage of .default()
2. Arrow Functions
Put simply, they 1) are a shortened way of writing functions and 2) automatically bind this
from their surrounding context.
function add(a, b) { return a + b; }
// Equivalent to this arrow function
const add = (a, b) => a + b;
Notes:
- Parameter parenthesis are optional if there is only one parameter
- If there is only one expression (effect) then the
{}
andreturn
are optional.
3. Destructuring (Pattern Matching)
Remember Rust? Or Ruby? Or Haskell?
(JSON) Object Destructuring:
const user = { name: 'Alice', age: 25 };
const { name, age } = user; // doing this method, the names must match the object
const { name, age, isHungry = false } = user; // example of default for nonexistant property
Array Destructuring:
There’s more to this and it tends to be pretty messy. But! My favorite feature is destructuring (pattern matching) with rest classically found in Haskell and other functional languages.
const colors = ['red', 'green', 'blue'];
const [first, second] = colors; // destructuring and throwing away rest
const [first, ...rest] = colors; // destructuring and keeping rest
const [fst, snd, thr, frt="purple"] = colors // destructuring and providing default value if missing
4. Spread and Rest Operators (...
)
Spread
Copying and merging arrays/objects. Think of the operator as “spreading” the array out into a comma separated list.
const oldArray = [1, 2, 3];
const newArray = [...oldArray, 4, 5]; // [1, 2, 3, 4, 5]
const person = { name: 'Alice', age: 25 };
const updatedPerson = { ...person, city: 'NYC' };
Rest
Think of variable arguments in C or Java and how it creates an array you can go through.
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
5. Promises & Async/Await
But first, a distinction. Asynchronous programming is a single chef in the kitchen that works on other tasks while waiting for the first ones to finish. Threaded programming is two chefs working alongside each other that must combine their output afterwards. JavaScript is single threaded and often requires asynchronous operations for longer operations like API fetching.
By learning the ins and outs of promises, it will be easier to understand await/async.
Promises
Can either be pending, fulfilled (with a value), or rejected (with an error).
// Reference this example
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (/* */) {
resolve("Data received");
} else {
reject("Error: Something went wrong");
}
}, 2000);
});
};
Resolve and Reject
Promises are given an arrow function with two parameters: resolve
and reject
. These parameters are functions that can be called inside the arrow function, and their value (usually the computed result or an error) will be passed to then
and catch
during usage (not definition) for further handling. Observe how we are able to use the value passed to resolve in the following snippet.
const myPromise = new Promise((resolve, reject) => {
resolve("Success!"); // Resolving with a string
});
myPromise.then(value => {
console.log(value); // Output: "Success!"
});
Timeouts
A timeout is optionally often used to delay execution or cancel the request in the event that it takes too long. Look into setTimeout
with AbortController
and signal
to abort a request.
Async/Await
async
is used to declare an asynchronous function that always returns a value wrapped in a promise. await
is used inside of an async
function to pause execution until a promise resolves.
async function fetchUser() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await response.json();
console.log("User:", user);
} catch (error) {
console.error("Error:", error);
}
}
fetchUser();
At a high level, async/await is built on top of promises with code that sometimes looks cleaner and more like synchronous code. And so, it is not unreasonable to use async/await in most scenarios (except parallel execution, or so I’ve heard).
This topic could potentially use some expanding. TODO
6. Template Literals
Comparable to printf
in many languages. Defined using backticks. Side note: single quotes vs double quotes are identical in JavaScript.
// Insert variables/expressions using `${expr}`
const result = `The sum of ${a} and ${b} is ${a + b}.`;
// Multiline strings
const paragraph = `Lorem
ipsum dolor`
7. Higher Order Functions
Classic functional programming. Notice the form of the arrow functions.
Map
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
Filter
const ages = [18, 25, 30, 15];
const adults = ages.filter(age => age >= 18); // [18, 25, 30]
Reduce
Here it is important that the first parameter of the arrow function is the accumulator and the second is the name given to the current element. The second parameter of the reduce function is the initial value (technically optional).
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, num) => acc + num, 0); // 10