JavaScript ES6+ Fundamentals
Master modern JavaScript features critical for React development
JavaScript ES6+ Fundamentals
Master modern JavaScript with free flashcards and spaced repetition practice. This lesson covers arrow functions, destructuring, promises, modules, and async/await—essential concepts for building React applications and working with contemporary JavaScript codebases.
Welcome to Modern JavaScript 💻
JavaScript has evolved dramatically since ES6 (ECMAScript 2015) introduced powerful new features that transformed how we write code. Understanding these fundamentals is crucial before diving into React, as React applications heavily rely on ES6+ syntax and patterns. Whether you're handling component props with destructuring, managing asynchronous data with promises, or organizing code with modules, these features form the backbone of modern web development.
💡 Pro Tip: React documentation and tutorials assume you're comfortable with ES6+ syntax. Mastering these fundamentals now will make learning React significantly smoother!
Core Concepts
1. Arrow Functions ➡️
Arrow functions provide a concise syntax for writing functions and handle the this keyword differently than traditional functions.
Traditional Function vs Arrow Function:
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => {
return a + b;
};
// Concise arrow function (implicit return)
const add = (a, b) => a + b;
Key Characteristics:
- Concise syntax: Omit
functionkeyword and use=>instead - Implicit return: Single-expression functions automatically return without
returnkeyword - Lexical
this: Arrow functions don't have their ownthiscontext—they inherit from the surrounding scope - No
argumentsobject: Use rest parameters instead
When to Use Arrow Functions:
| Scenario | Use Arrow? | Reason |
|---|---|---|
| Callback functions | ✅ Yes | Cleaner syntax, preserves `this` |
| Array methods (map, filter) | ✅ Yes | Concise, readable |
| Event handlers in classes | ✅ Yes | Binds `this` automatically |
| Object methods | ❌ No | Need dynamic `this` binding |
| Constructor functions | ❌ No | Cannot be used with `new` |
💡 React Connection: Arrow functions are everywhere in React—from component methods to event handlers to array transformations:
// Arrow functions in React
const UserList = ({ users }) => {
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
};
2. Destructuring Assignment 📦
Destructuring extracts values from arrays or properties from objects into distinct variables, making code cleaner and more readable.
Object Destructuring:
// Without destructuring
const user = { name: 'Alice', age: 30, email: 'alice@example.com' };
const name = user.name;
const age = user.age;
// With destructuring
const { name, age } = user;
console.log(name); // 'Alice'
console.log(age); // 30
// Renaming variables
const { name: userName, age: userAge } = user;
// Default values
const { country = 'USA' } = user;
// Nested destructuring
const user = { name: 'Bob', address: { city: 'NYC', zip: '10001' } };
const { address: { city } } = user;
console.log(city); // 'NYC'
Array Destructuring:
// Basic array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second] = colors;
console.log(first); // 'red'
console.log(second); // 'green'
// Skipping elements
const [, , third] = colors;
console.log(third); // 'blue'
// Rest operator
const [primary, ...others] = colors;
console.log(others); // ['green', 'blue']
// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
Function Parameter Destructuring:
// Destructuring in function parameters
const displayUser = ({ name, age }) => {
console.log(`${name} is ${age} years old`);
};
displayUser({ name: 'Charlie', age: 25, email: 'charlie@example.com' });
// Logs: "Charlie is 25 years old"
💡 React Connection: Destructuring is essential in React for extracting props and state:
// Destructuring props in React
const UserProfile = ({ name, avatar, bio }) => {
return (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{bio}</p>
</div>
);
};
// Destructuring useState
const [count, setCount] = useState(0);
3. Template Literals 📝
Template literals use backticks (`) and allow embedded expressions, multi-line strings, and easier string formatting.
// String concatenation (old way)
const name = 'Dana';
const greeting = 'Hello, ' + name + '! Welcome.';
// Template literal
const greeting = `Hello, ${name}! Welcome.`;
// Expressions inside ${}
const price = 19.99;
const total = `Total: $${(price * 1.1).toFixed(2)}`;
// Multi-line strings
const html = `
<div>
<h1>Title</h1>
<p>Content</p>
</div>
`;
// Nested template literals
const items = ['apple', 'banana', 'cherry'];
const list = `
<ul>
${items.map(item => `<li>${item}</li>`).join('')}
</ul>
`;
4. Spread and Rest Operators ⚡
The spread operator (...) expands iterables, while the rest operator (same syntax) collects multiple elements.
Spread Operator:
// Array spreading
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// Copying arrays (shallow copy)
const original = [1, 2, 3];
const copy = [...original];
// Object spreading
const user = { name: 'Eve', age: 28 };
const updatedUser = { ...user, age: 29, city: 'LA' };
console.log(updatedUser); // { name: 'Eve', age: 29, city: 'LA' }
// Function arguments
const numbers = [5, 2, 8, 1];
const max = Math.max(...numbers); // Same as Math.max(5, 2, 8, 1)
Rest Operator:
// Function parameters
const sum = (...numbers) => {
return numbers.reduce((total, num) => total + num, 0);
};
console.log(sum(1, 2, 3, 4)); // 10
// Array destructuring
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
// Object destructuring
const { name, ...otherProps } = { name: 'Frank', age: 35, city: 'SF' };
console.log(otherProps); // { age: 35, city: 'SF' }
💡 React Connection: Spread operator is crucial for immutable state updates:
// Updating state immutably in React
const [user, setUser] = useState({ name: 'Grace', age: 30 });
// Update age while keeping other properties
setUser({ ...user, age: 31 });
// Passing props
const props = { name: 'Helen', age: 25 };
return <UserCard {...props} />;
5. Enhanced Object Literals 🎯
ES6 enhanced object literals with shorthand property and method syntax.
// Property shorthand
const name = 'Ivy';
const age = 32;
// Old way
const user = { name: name, age: age };
// ES6 shorthand
const user = { name, age };
// Method shorthand
const calculator = {
// Old way
add: function(a, b) {
return a + b;
},
// ES6 shorthand
subtract(a, b) {
return a - b;
},
// Arrow function as property
multiply: (a, b) => a * b
};
// Computed property names
const propName = 'score';
const game = {
[propName]: 100,
['level' + '1']: 'easy'
};
console.log(game.score); // 100
console.log(game.level1); // 'easy'
6. Promises and Async/Await 🔄
Promises represent eventual completion (or failure) of asynchronous operations. Async/await provides cleaner syntax for working with promises.
Basic Promise:
// Creating a promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: 'Jay' };
resolve(data); // Success
// reject(new Error('Failed')); // Failure
}, 1000);
});
};
// Using promises
fetchData()
.then(data => {
console.log('Data:', data);
return data.id;
})
.then(id => {
console.log('ID:', id);
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
console.log('Cleanup');
});
Async/Await:
// Async function returns a promise
const getData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
};
// Using async function
getData()
.then(data => console.log(data))
.catch(error => console.error(error));
// Or with await (inside another async function)
const processData = async () => {
const data = await getData();
console.log(data);
};
Promise Combinators:
// Promise.all - waits for all promises
const [users, posts, comments] = await Promise.all([
fetch('/users').then(r => r.json()),
fetch('/posts').then(r => r.json()),
fetch('/comments').then(r => r.json())
]);
// Promise.race - returns first settled promise
const fastest = await Promise.race([
fetch('/api1'),
fetch('/api2'),
fetch('/api3')
]);
// Promise.allSettled - waits for all, returns all results
const results = await Promise.allSettled([
Promise.resolve('success'),
Promise.reject('error'),
Promise.resolve('done')
]);
💡 React Connection: Async/await is essential for data fetching in React:
// Fetching data in React with useEffect
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadUser = async () => {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (error) {
console.error('Failed to load user:', error);
} finally {
setLoading(false);
}
};
loadUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
};
7. Modules (Import/Export) 📦
ES6 modules allow you to organize code into reusable files with explicit imports and exports.
Named Exports:
// math.js
export const PI = 3.14159;
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// Alternative syntax
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
export { multiply, divide };
// Importing named exports
import { PI, add, subtract } from './math.js';
import { multiply as mult, divide } from './math.js'; // Rename
import * as MathUtils from './math.js'; // Import all
Default Exports:
// user.js
const User = {
name: 'Kim',
login() {
console.log(`${this.name} logged in`);
}
};
export default User;
// Importing default export
import User from './user.js'; // No curly braces
import MyUser from './user.js'; // Can rename freely
// Mixing default and named exports
// config.js
export const API_URL = 'https://api.example.com';
export default {
timeout: 3000,
retries: 3
};
// Importing both
import config, { API_URL } from './config.js';
💡 React Connection: React components are typically default exports:
// Button.js
import React from 'react';
const Button = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};
export default Button;
// App.js
import Button from './Button';
import { useState } from 'react'; // Named export from React
const App = () => {
const [count, setCount] = useState(0);
return <Button label="Click me" onClick={() => setCount(count + 1)} />;
};
8. Array Methods (map, filter, reduce) 🔧
These functional programming methods transform arrays without mutation.
map() - Transform Each Element:
const numbers = [1, 2, 3, 4, 5];
// Double each number
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Extract property from objects
const users = [
{ id: 1, name: 'Leo' },
{ id: 2, name: 'Mia' },
{ id: 3, name: 'Nina' }
];
const names = users.map(user => user.name);
console.log(names); // ['Leo', 'Mia', 'Nina']
// Create new objects
const formatted = users.map(user => ({
...user,
displayName: user.name.toUpperCase()
}));
filter() - Select Elements:
const numbers = [1, 2, 3, 4, 5, 6];
// Get even numbers
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]
// Filter objects
const products = [
{ name: 'Laptop', price: 999, inStock: true },
{ name: 'Mouse', price: 29, inStock: false },
{ name: 'Keyboard', price: 79, inStock: true }
];
const available = products.filter(p => p.inStock && p.price < 500);
console.log(available); // [{ name: 'Keyboard', ... }]
reduce() - Accumulate Values:
const numbers = [1, 2, 3, 4, 5];
// Sum all numbers
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15
// Find maximum
const max = numbers.reduce((acc, num) => Math.max(acc, num), -Infinity);
// Group objects
const people = [
{ name: 'Oscar', age: 25 },
{ name: 'Pam', age: 30 },
{ name: 'Quinn', age: 25 }
];
const groupedByAge = people.reduce((acc, person) => {
const age = person.age;
if (!acc[age]) acc[age] = [];
acc[age].push(person);
return acc;
}, {});
// { 25: [{name: 'Oscar'...}, {name: 'Quinn'...}], 30: [{name: 'Pam'...}] }
Chaining Methods:
const products = [
{ name: 'Laptop', price: 999, category: 'electronics' },
{ name: 'Shirt', price: 29, category: 'clothing' },
{ name: 'Phone', price: 699, category: 'electronics' },
{ name: 'Pants', price: 49, category: 'clothing' }
];
// Get names of electronics under $800
const affordable = products
.filter(p => p.category === 'electronics')
.filter(p => p.price < 800)
.map(p => p.name);
console.log(affordable); // ['Phone']
💡 React Connection: These methods are fundamental for rendering lists:
// Rendering filtered and transformed data
const ProductList = ({ products, maxPrice }) => {
return (
<div>
{products
.filter(p => p.price <= maxPrice)
.map(p => (
<div key={p.id}>
<h3>{p.name}</h3>
<p>${p.price}</p>
</div>
))}
</div>
);
};
9. Let, Const, and Block Scope 🔒
ES6 introduced let and const for block-scoped variable declarations, replacing var.
Comparison:
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Reassignment | ✅ Yes | ✅ Yes | ❌ No |
| Hoisting | Yes (undefined) | No (TDZ) | No (TDZ) |
| Global property | ✅ Yes | ❌ No | ❌ No |
| Redeclaration | ✅ Allowed | ❌ Error | ❌ Error |
Block Scope Examples:
// var is function-scoped
if (true) {
var x = 10;
}
console.log(x); // 10 (accessible outside block)
// let is block-scoped
if (true) {
let y = 20;
}
console.log(y); // ReferenceError: y is not defined
// const must be initialized
const z = 30;
z = 40; // TypeError: Assignment to constant variable
// const with objects (properties can change)
const user = { name: 'Rita' };
user.name = 'Sam'; // ✅ Allowed
user = {}; // ❌ Error: Assignment to constant variable
// const with arrays
const numbers = [1, 2, 3];
numbers.push(4); // ✅ Allowed
numbers = []; // ❌ Error
Temporal Dead Zone (TDZ):
console.log(a); // undefined (var hoisted)
var a = 1;
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2; // TDZ from block start until declaration
💡 Best Practice: Use const by default, let when reassignment is needed, avoid var.
10. Class Syntax 🏛️
ES6 classes provide cleaner syntax for object-oriented programming (syntactic sugar over prototypes).
// ES6 class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hi, I'm ${this.name}`;
}
// Static method
static species() {
return 'Homo sapiens';
}
}
const person = new Person('Tina', 28);
console.log(person.greet()); // "Hi, I'm Tina"
console.log(Person.species()); // "Homo sapiens"
// Inheritance
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age); // Call parent constructor
this.jobTitle = jobTitle;
}
// Override method
greet() {
return `${super.greet()}, I'm a ${this.jobTitle}`;
}
}
const emp = new Employee('Uma', 32, 'Developer');
console.log(emp.greet()); // "Hi, I'm Uma, I'm a Developer"
💡 React Connection: Class components use ES6 class syntax (though hooks are now preferred):
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
Practical Examples
Example 1: Building a User Profile Component 👤
This example combines multiple ES6+ features to create a reusable React component:
// UserProfile.js
import React, { useState, useEffect } from 'react';
// Using destructuring in parameters
const UserProfile = ({ userId, theme = 'light' }) => {
// Array destructuring with useState
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Async/await for data fetching
const fetchUser = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('User not found');
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]); // Dependency array
// Early returns
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
// Object destructuring
const { name, email, avatar, skills = [] } = user;
return (
<div className={`profile profile--${theme}`}>
<img src={avatar} alt={`${name}'s avatar`} />
{/* Template literals */}
<h2>{name}</h2>
<p>{email}</p>
{/* Array methods */}
<ul>
{skills
.filter(skill => skill.level > 3)
.map(skill => (
<li key={skill.id}>
{skill.name} - Level {skill.level}
</li>
))}
</ul>
</div>
);
};
export default UserProfile;
Features Used:
- ✅ Arrow functions
- ✅ Destructuring (props, objects, arrays)
- ✅ Template literals
- ✅ Async/await
- ✅ Default parameters
- ✅ Array methods (filter, map)
- ✅ Import/export
Example 2: Shopping Cart Logic 🛒
Demonstrating immutable state updates with spread operator:
import { useState } from 'react';
const ShoppingCart = () => {
const [cart, setCart] = useState([]);
// Add item (spreading to create new array)
const addItem = (product) => {
const existingItem = cart.find(item => item.id === product.id);
if (existingItem) {
// Update quantity immutably
setCart(cart.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 } // Spread object
: item
));
} else {
// Add new item
setCart([...cart, { ...product, quantity: 1 }]);
}
};
// Remove item
const removeItem = (productId) => {
setCart(cart.filter(item => item.id !== productId));
};
// Calculate total with reduce
const total = cart.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
// Clear cart
const clearCart = () => setCart([]);
return (
<div>
<h2>Shopping Cart</h2>
{cart.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
<ul>
{cart.map(({ id, name, price, quantity }) => (
<li key={id}>
{name} - ${price} x {quantity}
<button onClick={() => removeItem(id)}>Remove</button>
</li>
))}
</ul>
<p>Total: ${total.toFixed(2)}</p>
<button onClick={clearCart}>Clear Cart</button>
</>
)}
</div>
);
};
Key Concepts:
- Immutable updates using spread operator
- Array methods (find, map, filter, reduce)
- Destructuring in map callback
- Conditional rendering
Example 3: API Service Module 🌐
Creating a reusable API utility with promises and async/await:
// api.js
const API_BASE = 'https://api.example.com';
// Default headers
const defaultHeaders = {
'Content-Type': 'application/json'
};
// Generic fetch wrapper
const apiCall = async (endpoint, options = {}) => {
const config = {
...options,
headers: {
...defaultHeaders,
...options.headers
}
};
try {
const response = await fetch(`${API_BASE}${endpoint}`, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('API Error:', error);
throw error;
}
};
// Named exports for specific operations
export const getUsers = () => apiCall('/users');
export const getUser = (id) => apiCall(`/users/${id}`);
export const createUser = (userData) => apiCall('/users', {
method: 'POST',
body: JSON.stringify(userData)
});
export const updateUser = (id, userData) => apiCall(`/users/${id}`, {
method: 'PUT',
body: JSON.stringify(userData)
});
export const deleteUser = (id) => apiCall(`/users/${id}`, {
method: 'DELETE'
});
// Default export for batch operations
export default {
// Fetch multiple resources in parallel
fetchAll: async (...endpoints) => {
const promises = endpoints.map(endpoint => apiCall(endpoint));
return await Promise.all(promises);
},
// Race multiple endpoints
fetchFastest: async (...endpoints) => {
const promises = endpoints.map(endpoint => apiCall(endpoint));
return await Promise.race(promises);
}
};
// Usage in component:
// import { getUsers, createUser } from './api';
// const users = await getUsers();
Example 4: Form Validation Utility ✅
Demonstrating class syntax and method chaining:
// validator.js
class Validator {
constructor(value) {
this.value = value;
this.errors = [];
}
required(message = 'This field is required') {
if (!this.value || this.value.trim() === '') {
this.errors.push(message);
}
return this; // Enable chaining
}
minLength(length, message = `Minimum ${length} characters required`) {
if (this.value && this.value.length < length) {
this.errors.push(message);
}
return this;
}
email(message = 'Invalid email address') {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (this.value && !emailRegex.test(this.value)) {
this.errors.push(message);
}
return this;
}
pattern(regex, message = 'Invalid format') {
if (this.value && !regex.test(this.value)) {
this.errors.push(message);
}
return this;
}
// Return validation result
validate() {
return {
isValid: this.errors.length === 0,
errors: this.errors
};
}
// Static helper method
static create(value) {
return new Validator(value);
}
}
export default Validator;
// Usage:
import Validator from './validator';
const emailValidation = Validator.create('user@example.com')
.required()
.email()
.validate();
const passwordValidation = Validator.create('pass123')
.required('Password is required')
.minLength(8, 'Password must be at least 8 characters')
.pattern(/[A-Z]/, 'Password must contain uppercase letter')
.validate();
console.log(emailValidation); // { isValid: true, errors: [] }
console.log(passwordValidation); // { isValid: false, errors: [...] }
Common Mistakes ⚠️
1. Forgetting return in Arrow Functions
// ❌ WRONG: No return in block body
const double = (x) => {
x * 2; // Missing return!
};
// ✅ CORRECT: Implicit return (no braces)
const double = (x) => x * 2;
// ✅ CORRECT: Explicit return with braces
const double = (x) => {
return x * 2;
};
2. Mutating Objects/Arrays Instead of Creating New Ones
// ❌ WRONG: Direct mutation
const user = { name: 'Vince', age: 30 };
user.age = 31; // Mutates original
const numbers = [1, 2, 3];
numbers.push(4); // Mutates original
// ✅ CORRECT: Create new object/array
const updatedUser = { ...user, age: 31 };
const newNumbers = [...numbers, 4];
3. Misunderstanding const with Objects/Arrays
// ❌ WRONG: Thinking const makes objects immutable
const config = { api: 'v1' };
config.api = 'v2'; // ✅ This works! (properties can change)
config = {}; // ❌ This fails (can't reassign)
// 💡 const prevents reassignment, not property changes
4. Not Using await with Async Functions
// ❌ WRONG: Forgetting await
const getData = async () => {
const data = fetchData(); // Returns promise, not data!
console.log(data); // Promise { <pending> }
};
// ✅ CORRECT: Use await
const getData = async () => {
const data = await fetchData();
console.log(data); // Actual data
};
5. Destructuring Non-Existent Properties
// ❌ WRONG: No default value
const { name, age } = {};
console.log(age); // undefined
// ✅ CORRECT: Provide defaults
const { name = 'Unknown', age = 0 } = {};
console.log(age); // 0
6. Incorrect Module Export/Import
// ❌ WRONG: Mixing default and named incorrectly
// file.js
export default const myFunc = () => {}; // Syntax error!
// ✅ CORRECT:
export default () => {};
// OR
const myFunc = () => {};
export default myFunc;
// ❌ WRONG: Destructuring default export
import { myFunc } from './file'; // Wrong if myFunc is default
// ✅ CORRECT:
import myFunc from './file';
7. Using Arrow Functions as Object Methods
// ❌ WRONG: Arrow function doesn't bind 'this'
const obj = {
name: 'Wendy',
greet: () => {
console.log(this.name); // 'this' is undefined or window
}
};
// ✅ CORRECT: Use regular function or method shorthand
const obj = {
name: 'Wendy',
greet() {
console.log(this.name); // 'Wendy'
}
};
8. Forgetting to Chain Promises or Handle Errors
// ❌ WRONG: Unhandled promise rejection
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
// No .catch()!
// ✅ CORRECT: Always handle errors
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// ✅ BETTER: Use async/await with try-catch
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
Key Takeaways 🎯
✅ Arrow functions provide concise syntax and lexical this binding—perfect for callbacks and React components
✅ Destructuring extracts values cleanly from objects and arrays—essential for working with React props and state
✅ Template literals make string formatting readable with embedded expressions and multi-line support
✅ Spread/rest operators enable immutable updates and flexible function parameters—crucial for React state management
✅ Promises and async/await handle asynchronous operations elegantly—vital for API calls in React apps
✅ Modules organize code into reusable, maintainable files—the foundation of React component architecture
✅ Array methods (map, filter, reduce) transform data functionally—used constantly for rendering lists in React
✅ Let and const provide block scope and prevent common bugs—always prefer them over var
✅ Classes offer clean OOP syntax—used in React class components (though hooks are now preferred)
✅ Enhanced object literals reduce boilerplate with shorthand syntax—common in React component definitions
📋 ES6+ Quick Reference Card
| Feature | Syntax | React Use Case |
|---|---|---|
| Arrow Function | const fn = (x) => x * 2 | Event handlers, callbacks |
| Destructuring | const {name} = props | Extracting props/state |
| Template Literal | `Hello ${name}` | Dynamic strings in JSX |
| Spread | {...obj, key: val} | Immutable state updates |
| Rest | (...args) => {} | Flexible component props |
| Async/Await | await fetch(url) | API calls in useEffect |
| Map | arr.map(x => x * 2) | Rendering lists |
| Filter | arr.filter(x => x > 5) | Conditional rendering |
| Import | import X from './X' | Loading components |
| Export | export default Comp | Sharing components |
📚 Further Study
MDN Web Docs - JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript - Comprehensive reference for all ES6+ features
JavaScript.info - Modern JavaScript Tutorial: https://javascript.info/ - In-depth explanations with interactive examples
React Documentation - JavaScript in JSX: https://react.dev/learn/javascript-in-jsx-with-curly-braces - How ES6+ features are used specifically in React
🎉 Congratulations! You now have a solid foundation in ES6+ JavaScript. These features are the building blocks of modern React development. Practice using them together—destructuring props, mapping arrays to components, fetching data with async/await—and you'll be ready to dive into React with confidence!