You are viewing a preview of this lesson. Sign in to start learning
Back to React

JavaScript ES6+ Fundamentals

Master modern JavaScript features critical for React development

Last generated

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 function keyword and use => instead
  • Implicit return: Single-expression functions automatically return without return keyword
  • Lexical this: Arrow functions don't have their own this context—they inherit from the surrounding scope
  • No arguments object: Use rest parameters instead

When to Use Arrow Functions:

ScenarioUse Arrow?Reason
Callback functions✅ YesCleaner syntax, preserves `this`
Array methods (map, filter)✅ YesConcise, readable
Event handlers in classes✅ YesBinds `this` automatically
Object methods❌ NoNeed dynamic `this` binding
Constructor functions❌ NoCannot 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:

Featurevarletconst
ScopeFunctionBlockBlock
Reassignment✅ Yes✅ Yes❌ No
HoistingYes (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

FeatureSyntaxReact Use Case
Arrow Functionconst fn = (x) => x * 2Event handlers, callbacks
Destructuringconst {name} = propsExtracting props/state
Template Literal`Hello ${name}`Dynamic strings in JSX
Spread{...obj, key: val}Immutable state updates
Rest(...args) => {}Flexible component props
Async/Awaitawait fetch(url)API calls in useEffect
Maparr.map(x => x * 2)Rendering lists
Filterarr.filter(x => x > 5)Conditional rendering
Importimport X from './X'Loading components
Exportexport default CompSharing components

📚 Further Study

  1. MDN Web Docs - JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript - Comprehensive reference for all ES6+ features

  2. JavaScript.info - Modern JavaScript Tutorial: https://javascript.info/ - In-depth explanations with interactive examples

  3. 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!