Advanced JavaScript (ES6+)

Advanced JavaScript in Practice

13 min Lesson 40 of 40

Advanced JavaScript in Practice

Congratulations on reaching the final lesson! In this capstone lesson, we'll bring together everything you've learned by building a complete real-world application, exploring modern development workflows, and discussing your next steps as a JavaScript developer.

Complete Real-World Project: Task Management App

Let's build a modern task management application that demonstrates advanced JavaScript concepts:

// Project Structure /* task-manager/ ├── index.html ├── css/ │ └── styles.css ├── js/ │ ├── app.js │ ├── store.js │ ├── api.js │ ├── components/ │ │ ├── TaskList.js │ │ ├── TaskItem.js │ │ └── TaskForm.js │ └── utils/ │ ├── debounce.js │ └── validators.js └── sw.js */ // store.js - State Management with Proxy class Store { constructor(initialState = {}) { this.state = new Proxy(initialState, { set: (target, property, value) => { target[property] = value; this.notify(property, value); return true; } }); this.listeners = new Map(); } subscribe(property, callback) { if (!this.listeners.has(property)) { this.listeners.set(property, new Set()); } this.listeners.get(property).add(callback); // Return unsubscribe function return () => { this.listeners.get(property).delete(callback); }; } notify(property, value) { if (this.listeners.has(property)) { this.listeners.get(property).forEach(callback => { callback(value); }); } } getState() { return { ...this.state }; } setState(updates) { Object.assign(this.state, updates); } } // Initialize store const store = new Store({ tasks: [], filter: 'all', isLoading: false, error: null }); // api.js - API Layer with Error Handling class TaskAPI { constructor(baseURL) { this.baseURL = baseURL; } async request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}`; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; try { const response = await fetch(url, config); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('API request failed:', error); throw error; } } async getTasks() { return this.request('/tasks'); } async createTask(task) { return this.request('/tasks', { method: 'POST', body: JSON.stringify(task) }); } async updateTask(id, updates) { return this.request(`/tasks/${id}`, { method: 'PATCH', body: JSON.stringify(updates) }); } async deleteTask(id) { return this.request(`/tasks/${id}`, { method: 'DELETE' }); } } const api = new TaskAPI('https://api.example.com'); // TaskItem.js - Component with Custom Events class TaskItem { constructor(task) { this.task = task; this.element = null; this.render(); } render() { this.element = document.createElement('div'); this.element.className = `task-item ${this.task.completed ? 'completed' : ''}`; this.element.innerHTML = ` <input type="checkbox" ${this.task.completed ? 'checked' : ''}> <span class="task-title">${this.escapeHtml(this.task.title)}</span> <span class="task-priority ${this.task.priority}">${this.task.priority}</span> <button class="edit-btn">Edit</button> <button class="delete-btn">Delete</button> `; this.attachEventListeners(); return this.element; } attachEventListeners() { const checkbox = this.element.querySelector('input[type="checkbox"]'); const editBtn = this.element.querySelector('.edit-btn'); const deleteBtn = this.element.querySelector('.delete-btn'); checkbox.addEventListener('change', () => { this.dispatchEvent('toggle', { id: this.task.id }); }); editBtn.addEventListener('click', () => { this.dispatchEvent('edit', { task: this.task }); }); deleteBtn.addEventListener('click', () => { this.dispatchEvent('delete', { id: this.task.id }); }); } dispatchEvent(type, detail) { this.element.dispatchEvent(new CustomEvent(type, { bubbles: true, detail })); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } update(task) { this.task = task; const newElement = this.render(); this.element.replaceWith(newElement); this.element = newElement; } } // TaskList.js - Container Component with Virtual Scrolling class TaskList { constructor(container) { this.container = container; this.tasks = []; this.components = new Map(); this.observer = null; this.setupIntersectionObserver(); this.attachEventListeners(); } setupIntersectionObserver() { this.observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, { threshold: 0.1 } ); } attachEventListeners() { this.container.addEventListener('toggle', async (e) => { const { id } = e.detail; await this.handleToggle(id); }); this.container.addEventListener('edit', (e) => { const { task } = e.detail; this.handleEdit(task); }); this.container.addEventListener('delete', async (e) => { const { id } = e.detail; await this.handleDelete(id); }); } async handleToggle(id) { try { const task = this.tasks.find(t => t.id === id); const updated = await api.updateTask(id, { completed: !task.completed }); this.updateTask(updated); } catch (error) { store.setState({ error: 'Failed to update task' }); } } handleEdit(task) { // Emit event for parent to handle document.dispatchEvent(new CustomEvent('edit-task', { detail: { task } })); } async handleDelete(id) { if (!confirm('Are you sure you want to delete this task?')) { return; } try { await api.deleteTask(id); this.removeTask(id); } catch (error) { store.setState({ error: 'Failed to delete task' }); } } render(tasks) { this.tasks = tasks; this.container.innerHTML = ''; if (tasks.length === 0) { this.container.innerHTML = '<p class="empty-state">No tasks found</p>'; return; } const fragment = document.createDocumentFragment(); tasks.forEach(task => { const taskItem = new TaskItem(task); this.components.set(task.id, taskItem); fragment.appendChild(taskItem.element); this.observer.observe(taskItem.element); }); this.container.appendChild(fragment); } updateTask(task) { const index = this.tasks.findIndex(t => t.id === task.id); if (index !== -1) { this.tasks[index] = task; const component = this.components.get(task.id); if (component) { component.update(task); } } } removeTask(id) { this.tasks = this.tasks.filter(t => t.id !== id); const component = this.components.get(id); if (component) { component.element.remove(); this.components.delete(id); } } destroy() { this.observer.disconnect(); this.components.clear(); } } // TaskForm.js - Form with Validation class TaskForm { constructor(formElement) { this.form = formElement; this.editingTask = null; this.validators = { title: (value) => { if (!value || value.trim().length === 0) { return 'Title is required'; } if (value.length > 100) { return 'Title must be less than 100 characters'; } return null; }, priority: (value) => { const validPriorities = ['low', 'medium', 'high']; if (!validPriorities.includes(value)) { return 'Invalid priority'; } return null; } }; this.attachEventListeners(); } attachEventListeners() { this.form.addEventListener('submit', async (e) => { e.preventDefault(); await this.handleSubmit(); }); // Real-time validation const titleInput = this.form.querySelector('[name="title"]'); titleInput.addEventListener('blur', () => { this.validateField('title', titleInput.value); }); } validateField(field, value) { const error = this.validators[field](value); const errorElement = this.form.querySelector(`[data-error="${field}"]`); if (error) { errorElement.textContent = error; return false; } else { errorElement.textContent = ''; return true; } } validateForm(data) { let isValid = true; Object.keys(this.validators).forEach(field => { if (!this.validateField(field, data[field])) { isValid = false; } }); return isValid; } async handleSubmit() { const formData = new FormData(this.form); const data = Object.fromEntries(formData); if (!this.validateForm(data)) { return; } try { store.setState({ isLoading: true }); if (this.editingTask) { await api.updateTask(this.editingTask.id, data); } else { await api.createTask(data); } await loadTasks(); this.reset(); } catch (error) { store.setState({ error: 'Failed to save task' }); } finally { store.setState({ isLoading: false }); } } edit(task) { this.editingTask = task; this.form.querySelector('[name="title"]').value = task.title; this.form.querySelector('[name="priority"]').value = task.priority; this.form.querySelector('[name="description"]').value = task.description || ''; } reset() { this.form.reset(); this.editingTask = null; } } // app.js - Application Initialization class TaskManagerApp { constructor() { this.taskList = null; this.taskForm = null; this.searchDebounced = null; this.init(); } async init() { // Initialize components this.taskList = new TaskList(document.querySelector('#task-list')); this.taskForm = new TaskForm(document.querySelector('#task-form')); // Setup search with debouncing this.searchDebounced = this.debounce(this.handleSearch.bind(this), 300); document.querySelector('#search').addEventListener('input', (e) => { this.searchDebounced(e.target.value); }); // Setup filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', () => { store.setState({ filter: btn.dataset.filter }); }); }); // Subscribe to state changes store.subscribe('tasks', (tasks) => { this.renderTasks(tasks); }); store.subscribe('filter', () => { this.renderTasks(store.state.tasks); }); store.subscribe('isLoading', (isLoading) => { this.toggleLoading(isLoading); }); store.subscribe('error', (error) => { if (error) { this.showError(error); } }); // Listen for edit events document.addEventListener('edit-task', (e) => { this.taskForm.edit(e.detail.task); }); // Load initial data await this.loadTasks(); // Register service worker this.registerServiceWorker(); } async loadTasks() { try { store.setState({ isLoading: true }); const tasks = await api.getTasks(); store.setState({ tasks, error: null }); } catch (error) { store.setState({ error: 'Failed to load tasks' }); } finally { store.setState({ isLoading: false }); } } renderTasks(tasks) { const { filter } = store.state; const filteredTasks = tasks.filter(task => { if (filter === 'active') return !task.completed; if (filter === 'completed') return task.completed; return true; }); this.taskList.render(filteredTasks); } handleSearch(query) { const { tasks } = store.state; if (!query) { this.renderTasks(tasks); return; } const filtered = tasks.filter(task => task.title.toLowerCase().includes(query.toLowerCase()) ); this.taskList.render(filtered); } debounce(func, delay) { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; } toggleLoading(isLoading) { document.body.classList.toggle('loading', isLoading); } showError(message) { const toast = document.createElement('div'); toast.className = 'toast error'; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('show'); }, 10); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 3000); } async registerServiceWorker() { if ('serviceWorker' in navigator) { try { await navigator.serviceWorker.register('/sw.js'); console.log('Service Worker registered'); } catch (error) { console.error('Service Worker registration failed:', error); } } } } // Initialize app const app = new TaskManagerApp();
Key Patterns Used: This application demonstrates Proxy-based state management, component architecture, custom events, API abstraction, form validation, debouncing, IntersectionObserver, and Service Workers.

Modern JavaScript Workflow

Professional JavaScript development involves more than just writing code:

1. Development Environment: // package.json { "name": "task-manager", "version": "1.0.0", "scripts": { "dev": "vite", "build": "vite build", "test": "jest", "lint": "eslint src", "format": "prettier --write src" }, "devDependencies": { "vite": "^5.0.0", "jest": "^29.0.0", "eslint": "^8.0.0", "prettier": "^3.0.0" } } 2. Code Quality: // .eslintrc.js module.exports = { env: { browser: true, es2021: true }, extends: ['eslint:recommended'], rules: { 'no-console': 'warn', 'no-unused-vars': 'error', 'prefer-const': 'error' } }; // .prettierrc { "semi": true, "singleQuote": true, "tabWidth": 4, "trailingComma": "es5" } 3. Testing: // task.test.js import { describe, it, expect } from 'jest'; import { TaskItem } from './TaskItem'; describe('TaskItem', () => { it('should create task element', () => { const task = { id: 1, title: 'Test task', completed: false, priority: 'medium' }; const taskItem = new TaskItem(task); expect(taskItem.element).toBeDefined(); expect(taskItem.element.querySelector('.task-title').textContent).toBe('Test task'); }); it('should mark task as completed', () => { const task = { id: 1, title: 'Test', completed: true }; const taskItem = new TaskItem(task); expect(taskItem.element.classList.contains('completed')).toBe(true); }); }); 4. Build Process: // vite.config.js import { defineConfig } from 'vite'; export default defineConfig({ build: { outDir: 'dist', minify: 'terser', sourcemap: true }, server: { port: 3000 } }); 5. Version Control: // .gitignore node_modules/ dist/ .env .DS_Store // Commit messages (Conventional Commits) feat: add task filtering fix: resolve task deletion bug docs: update README refactor: improve state management test: add TaskItem tests

Testing with Jest Basics

// Unit tests test('debounce function delays execution', (done) => { let count = 0; const increment = debounce(() => count++, 100); increment(); increment(); increment(); setTimeout(() => { expect(count).toBe(1); done(); }, 150); }); // Async tests test('API fetches tasks', async () => { const api = new TaskAPI('https://api.example.com'); const tasks = await api.getTasks(); expect(Array.isArray(tasks)).toBe(true); }); // Mocking jest.mock('./api', () => ({ getTasks: jest.fn(() => Promise.resolve([ { id: 1, title: 'Mock task' } ])) })); // Integration tests describe('TaskList integration', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it('should render tasks', () => { const taskList = new TaskList(container); const tasks = [ { id: 1, title: 'Task 1', completed: false }, { id: 2, title: 'Task 2', completed: true } ]; taskList.render(tasks); expect(container.querySelectorAll('.task-item').length).toBe(2); }); });

Linting with ESLint

// Install ESLint npm install --save-dev eslint // Initialize configuration npx eslint --init // Common rules { "rules": { // Error prevention "no-console": "warn", "no-debugger": "error", "no-unused-vars": "error", // Best practices "eqeqeq": ["error", "always"], "no-eval": "error", "no-implicit-globals": "error", // ES6+ "prefer-const": "error", "prefer-arrow-callback": "warn", "no-var": "error", "prefer-template": "warn", // Style "indent": ["error", 4], "quotes": ["error", "single"], "semi": ["error", "always"] } } // Run ESLint npm run lint // Fix automatically npm run lint -- --fix

Formatting with Prettier

// Install Prettier npm install --save-dev prettier // Configuration (.prettierrc) { "printWidth": 100, "tabWidth": 4, "useTabs": false, "semi": true, "singleQuote": true, "trailingComma": "es5", "bracketSpacing": true, "arrowParens": "always" } // Ignore files (.prettierignore) node_modules dist build coverage // Format code npm run format // Integrate with ESLint npm install --save-dev eslint-config-prettier eslint-plugin-prettier

Package Management with npm

// Initialize project npm init -y // Install dependencies npm install lodash axios // Install dev dependencies npm install --save-dev jest eslint // Update dependencies npm update // Audit security npm audit npm audit fix // Scripts in package.json { "scripts": { "start": "node server.js", "dev": "nodemon server.js", "build": "webpack --mode production", "test": "jest --coverage", "test:watch": "jest --watch", "lint": "eslint .", "format": "prettier --write ." } } // Use npm scripts npm run dev npm test npm run build

Build Tools and Deployment

Modern Build Tools: 1. Vite (Recommended for new projects) - Instant server start - Lightning-fast HMR - Optimized builds 2. Webpack (Mature, widely used) - Powerful configuration - Large ecosystem - Code splitting 3. Rollup (For libraries) - Tree shaking - ES modules - Small bundles Deployment Platforms: // Vercel (vercel.json) { "builds": [ { "src": "index.html", "use": "@vercel/static" } ] } // Netlify (netlify.toml) [build] command = "npm run build" publish = "dist" [[redirects]] from = "/*" to = "/index.html" status = 200 // Deploy commands npm run build # Build for production vercel --prod # Deploy to Vercel netlify deploy --prod # Deploy to Netlify CI/CD Pipeline: // .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node uses: actions/setup-node@v2 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Build run: npm run build - name: Deploy run: npm run deploy

Next Steps: TypeScript, React, Node.js

1. TypeScript - Add type safety // Install npm install --save-dev typescript // tsconfig.json { "compilerOptions": { "target": "ES2020", "module": "ESNext", "strict": true, "esModuleInterop": true } } // Example TypeScript code interface Task { id: number; title: string; completed: boolean; priority: 'low' | 'medium' | 'high'; } class TaskManager { private tasks: Task[] = []; addTask(task: Task): void { this.tasks.push(task); } getTask(id: number): Task | undefined { return this.tasks.find(t => t.id === id); } } 2. React - Component-based UI library // Install npx create-react-app my-app # or with Vite npm create vite@latest my-app -- --template react // Example React component import { useState, useEffect } from 'react'; function TaskList() { const [tasks, setTasks] = useState([]); useEffect(() => { fetchTasks().then(setTasks); }, []); return ( <div> {tasks.map(task => ( <TaskItem key={task.id} task={task} /> ))} </div> ); } 3. Node.js - Server-side JavaScript // Install # Download from nodejs.org // Example Express server const express = require('express'); const app = express(); app.use(express.json()); app.get('/api/tasks', (req, res) => { res.json(tasks); }); app.post('/api/tasks', (req, res) => { const task = req.body; tasks.push(task); res.status(201).json(task); }); app.listen(3000, () => { console.log('Server running on port 3000'); }); 4. Additional Technologies to Explore: - Vue.js or Angular (alternative frameworks) - Next.js or Nuxt.js (meta-frameworks) - GraphQL (alternative to REST APIs) - MongoDB or PostgreSQL (databases) - Docker (containerization) - AWS or Azure (cloud platforms)

Career Skills and Continuing Education

Essential Skills for JavaScript Developers: 1. Core JavaScript Mastery - ES6+ features - Async programming - Design patterns - Performance optimization 2. Framework Knowledge - React, Vue, or Angular - State management (Redux, Zustand) - Routing and navigation 3. Backend Development - Node.js and Express - RESTful APIs - Database operations - Authentication 4. Development Tools - Git version control - Package managers (npm, yarn) - Build tools (Webpack, Vite) - Testing frameworks 5. Soft Skills - Problem-solving - Code review - Documentation - Team collaboration Learning Resources: - MDN Web Docs (documentation) - JavaScript.info (tutorials) - Frontend Masters (courses) - GitHub (open source projects) - Dev.to and Medium (articles) - Stack Overflow (community) Building Your Portfolio: 1. Create 3-5 substantial projects 2. Contribute to open source 3. Write technical blog posts 4. Maintain an active GitHub profile 5. Participate in coding challenges Job Search Tips: - Build a strong portfolio - Practice coding interviews - Network in developer communities - Contribute to open source - Keep learning and staying current

Final Challenge:

Enhance the Task Manager application with these features:

  1. Add drag-and-drop task reordering
  2. Implement task categories/tags
  3. Add due dates with notifications
  4. Create a dark mode toggle
  5. Add data export to JSON/CSV
  6. Implement offline support with IndexedDB
  7. Add user authentication
  8. Create responsive mobile design

Apply all the concepts you've learned throughout this course!

Congratulations!

You've completed the Advanced JavaScript (ES6+) course! You've learned:

  • Modern Syntax: ES6+ features, arrow functions, destructuring, spread/rest operators
  • Functions: Closures, higher-order functions, this keyword, IIFE
  • Async Programming: Promises, async/await, Fetch API, event loop
  • Data Structures: Sets, Maps, Symbols, iterators, generators
  • OOP: Classes, inheritance, prototypes, Proxy, Reflect
  • Modules: ES6 modules, design patterns, error handling
  • Advanced Topics: Debugging, regex, performance, modern APIs
  • Real-World Skills: Testing, linting, build tools, deployment
What's Next? You're now ready to build professional JavaScript applications! Consider specializing in frontend frameworks (React, Vue, Angular), backend development with Node.js, or full-stack development. Keep coding, keep learning, and most importantly, keep building!

Final Thoughts

JavaScript is constantly evolving, and staying current is essential. Follow these principles:

  • Practice regularly: Code every day, even if just for 30 minutes
  • Build real projects: Theory is important, but practice is essential
  • Learn from others: Read code, contribute to open source, attend meetups
  • Stay curious: Explore new technologies and paradigms
  • Share knowledge: Teaching others reinforces your own understanding

Thank you for completing this course! Happy coding!

Tutorial Complete!

Congratulations! You have completed all lessons in this tutorial.