Module 01: Getting Started with JavaScript
Learning Focus: JavaScript fundamentals, arrays, objects, functions, and real-world problem solving (Canadian tax calculator)
Table of Contents
- Module Overview
 - Core Concepts
 - Code Walkthrough
 - Testing Strategy
 - Bugs Fixed
 - 2021 vs 2025 Comparison
 - Key Takeaways
 
Module Overview
What We're Building
This module covers JavaScript fundamentals through practical exercises:
- Tax Calculator: Calculate Canadian federal income tax
 - Array Operations: Manipulation, filtering, summing
 - Dictionary/Object Handling: Key-value pair operations
 - Function Patterns: Pure functions, callbacks, higher-order functions
 - Calculator: Basic arithmetic operations
 
Why These Topics Matter
Moving beyond "hello world," we tackle real-world problems:
- Tax calculation teaches conditional logic and bracket systems
 - Arrays teach iteration patterns (map, filter, reduce)
 - Objects teach data modeling
 - Functions teach code organization and reusability
 
Core Concepts
1. Canadian Tax Brackets (2021)
Canada uses a progressive tax system with multiple brackets:
const TAX_BRACKETS = [
    { max: 49020, rate: 0.15 },    // 15% on first $49,020
    { max: 98040, rate: 0.205 },   // 20.5% on next chunk
    { max: 151978, rate: 0.26 },   // 26% on next chunk
    { max: 216511, rate: 0.29 },   // 29% on next chunk
    { max: Infinity, rate: 0.33 }  // 33% on remainder
];
How Progressive Taxation Works:
Income: $60,000
Bracket 1: $49,020 Γ 15% = $7,353.00
Bracket 2: $10,980 Γ 20.5% = $2,250.90
                     Total = $9,603.90
You DON'T pay 20.5% on the entire $60,000βonly on the amount over $49,020.
2. Array Methods (The Holy Trinity)
const numbers = [1, 2, 3, 4, 5];
// MAP: Transform each element
const doubled = numbers.map(n => n * 2);
// Result: [2, 4, 6, 8, 10]
// FILTER: Keep elements that pass test
const evens = numbers.filter(n => n % 2 === 0);
// Result: [2, 4]
// REDUCE: Combine elements into single value
const sum = numbers.reduce((total, n) => total + n, 0);
// Result: 15
Why these matter:
- Map: Transform data (e.g., convert prices to USD)
 - Filter: Search/query (e.g., active users only)
 - Reduce: Aggregation (e.g., total sales)
 
3. Pure Functions
// β IMPURE: Modifies external state
let total = 0;
function addToTotal(n) {
    total += n;  // Side effect!
    return total;
}
// β
 PURE: No side effects, same input = same output
function add(a, b) {
    return a + b;  // Predictable!
}
Pure functions are:
- Easier to test
 - Easier to debug
 - Easier to reason about
 - Safe to parallelize
 
Code Walkthrough
File Structure
01-getting-started/
βββ src/
β   βββ scripts/
β   β   βββ array.js          # Array operations
β   β   βββ array.test.js     # Array tests
β   β   βββ calculator.js     # Basic math
β   β   βββ calculator.test.js
β   β   βββ dict.js           # Object/dictionary ops
β   β   βββ dict.test.js
β   β   βββ functions.js      # Function exercises
β   β   βββ functions.test.js
β   β   βββ syntax.js         # JS syntax basics
β   β   βββ syntax.test.js
β   β   βββ tax.js            # β Tax calculator
β   β   βββ tax.test.js       # β Tax tests
β   βββ index.html
βββ package.json
tax.js - The Tax Calculator (Most Complex)
const TAX = [
    [49020, 0.15],
    [98040, 0.205],
    [151978, 0.26],
    [216511, 0.29],
    [Infinity, 0.33]
];
const taxCalc = {
    // Calculate tax for a specific bracket
    calcTaxForBracket: (previousMax, currentMax, rate, income) => {
        if (income <= previousMax) {
            return 0;  // Income doesn't reach this bracket
        }
        
        // How much income falls in this bracket?
        const taxableInThisBracket = Math.min(income, currentMax) - previousMax;
        
        return taxableInThisBracket * rate;
    },
    // Calculate total tax across all brackets
    calcTax: (income) => {
        let totalTax = 0;
        let previousMax = 0;
        // Iterate through each bracket
        for (let i = 0; i < TAX.length; i++) {
            const [currentMax, rate] = TAX[i];
            
            const taxForBracket = taxCalc.calcTaxForBracket(
                previousMax,
                currentMax,
                rate,
                income
            );
            
            totalTax += taxForBracket;
            previousMax = currentMax;
            
            // Stop if we've exceeded income
            if (income <= currentMax) break;
        }
        return totalTax;
    },
    // Calculate after-tax income
    calcTaxWithRemainder: (income) => {
        const tax = taxCalc.calcTax(income);
        return {
            tax: tax,
            afterTax: income - tax
        };
    }
};
export default taxCalc;
Key Design Decisions:
Why separate
calcTaxForBracket?- Single Responsibility Principle
 - Easier to test individual brackets
 - Reusable logic
 
Why use array destructuring?
const [currentMax, rate] = TAX[i]; // Instead of: const currentMax = TAX[i][0]; const rate = TAX[i][1];Cleaner, more readable code.
Why
Math.min(income, currentMax)?- Handles the case where income is in the middle of a bracket
 - Example: Income 55,β000inbracket[49,020 - $98,040]
 - Only $5,980 is taxed at 20.5%, not the full bracket range
 
array.js - Array Operations
const arrayFunctions = {
    // Add element to array and return it
    addElement: (arr, element) => {
        arr.push(element);
        return arr;  // β οΈ BUG WAS HERE: Originally didn't return
    },
    // Sum all numbers in array
    sumArray: (arr) => {
        return arr.reduce((total, element) => {
            return total + element;
        }, 0);
        // β οΈ BUG WAS HERE: Used global `arr` instead of parameter
    },
    // Remove element at specific index
    removeElement: (arr, index) => {
        arr.splice(index, 1);
        return arr;
    }
};
Bug Story: The Global Variable Mistake
Original broken code:
let arr = []; // Global variable
sumArray: (element) => {
    return arr.reduce((total, num) => total + num, 0);
    // β Uses global `arr`, not the parameter!
}
Why this failed:
sumArray([1, 2, 3]);  // Returns 0 (global arr is empty)
sumArray([5, 5]);     // Still returns 0!
The fix:
sumArray: (arr) => {  // β
 Use parameter name
    return arr.reduce((total, element) => total + element, 0);
}
dict.js - Dictionary/Object Operations
const dictFunctions = {
    // Loop 1: Return value by key
    returnTheSecondtItemByKey: (obj) => {
        return obj[Object.keys(obj)[1]];
    },
    // Loop 2: Return value when key matches condition
    returnTheThirdItemByValue: (obj) => {
        for (const key in obj) {
            if (obj[key] === key) {  // β οΈ BUG WAS HERE
                return obj[key];
            }
        }
        return "error!";
    }
};
Bug Story: The Always-Returning-Error Loop
Original broken code:
returnTheThirdItemByValue: (obj) => {
    for (const key in obj) {
        if (obj[key] === key) {
            return obj[key];
        }
        return "error!";  // β WRONG! Returns on first iteration
    }
}
Why this failed:
- The 
return "error!"was inside the loop - It would execute on the first iteration if the condition failed
 - The loop never got a chance to check other keys
 
The fix:
returnTheThirdItemByValue: (obj) => {
    for (const key in obj) {
        if (obj[key] === key) {
            return obj[key];
        }
    }
    return "error!";  // β
 Outside loop, runs only if nothing found
}
Testing Strategy
tax.test.js - Tax Calculator Tests
import taxCalc from "./tax.js";
test("Testing the function that calculates tax for a single bracket", () => {
    // Test bracket 1: $49,020 at 15%
    expect(taxCalc.calcTaxForBracket(0, 49020, 0.15, 30000))
        .toBeCloseTo(4500, 1);  // $30,000 Γ 15% = $4,500
        
    // Test bracket 2: Income partially in bracket
    expect(taxCalc.calcTaxForBracket(49020, 98040, 0.205, 60000))
        .toBeCloseTo(2250.9, 1);  // $10,980 Γ 20.5% = $2,250.90
});
test("Testing the function that calculates total tax", () => {
    // Income: $60,000
    // Bracket 1: $49,020 Γ 15% = $7,353.00
    // Bracket 2: $10,980 Γ 20.5% = $2,250.90
    // Total: $9,603.90
    expect(taxCalc.calcTax(60000)).toBeCloseTo(9603.9, 1);
    
    // Edge case: Exactly at bracket boundary
    expect(taxCalc.calcTax(49020)).toBeCloseTo(7353, 1);
});
test("Testing the function that calculates after-tax income", () => {
    const result = taxCalc.calcTaxWithRemainder(60000);
    expect(result.tax).toBeCloseTo(9603.9, 1);
    expect(result.afterTax).toBeCloseTo(50396.1, 1);
});
Why toBeCloseTo() instead of
toBe()?
JavaScript floating-point arithmetic isn't exact:
0.1 + 0.2 === 0.3  // false! (actually 0.30000000000000004)
toBeCloseTo(value, decimals) handles this:
expect(0.1 + 0.2).toBeCloseTo(0.3, 10);  // β
 Passes
array.test.js - Array Tests
import arrayFunctions from "./array.js";
test("Does the addElement function work?", () => {
    const arr = [1, 2, 3];
    const result = arrayFunctions.addElement(arr, 4);
    
    expect(result).toEqual([1, 2, 3, 4]);
    expect(result.length).toBe(4);
});
test("Does the sumArray function work?", () => {
    expect(arrayFunctions.sumArray([1, 2, 3])).toBe(6);
    expect(arrayFunctions.sumArray([0])).toBe(0);
    expect(arrayFunctions.sumArray([-1, 1])).toBe(0);
});
Bugs Fixed
During the 2025 audit, I discovered and fixed 5 critical bugs in this module:
Bug #1: Tax Array Indexing
// β WRONG: Incorrectly accessing tax bracket data
const previousMax = TAX[i - 1][1];  // Gets RATE instead of MAX
// β
 CORRECT: Access the right index
const previousMax = TAX[i - 1][0];  // Gets MAX value
Impact: Calculated wrong tax amounts for all brackets after the first.
Bug #2: Tax Income Calculation
// β WRONG: Used wrong variable names
const total_tax_income = Math.min(income, P[1]) - P[0];
// β
 CORRECT: Use consistent naming
const total_tax_income = Math.min(income, TAX[i][0]) - previousMax;
Impact: Logic errors in else-if blocks caused incorrect tax calculations.
Bug #3: Dictionary Loop Returns Early
// β WRONG: Always returns "error!" on first iteration
for (const key in obj) {
    if (condition) return value;
    return "error!";  // Inside loop!
}
// β
 CORRECT: Return after checking all keys
for (const key in obj) {
    if (condition) return value;
}
return "error!";  // Outside loop
Impact: Function never checked beyond first key.
Bug #4: Array Function Doesn't Return
// β WRONG: Modifies array but doesn't return it
addElement: (arr, element) => {
    arr.push(element);
    // Missing return statement!
}
// β
 CORRECT: Return the modified array
addElement: (arr, element) => {
    arr.push(element);
    return arr;
}
Impact: Chaining array operations failed.
Bug #5: sumArray Uses Wrong Variable
// β WRONG: Uses global `arr` instead of parameter
sumArray: (element) => {
    return arr.reduce((total, num) => total + num);
}
// β
 CORRECT: Use function parameter
sumArray: (arr) => {
    return arr.reduce((total, element) => total + element, 0);
}
Impact: Always returned 0 or crashed.
2021 vs 2025 Comparison
Coding Style Evolution
2021: Verbose, Imperative
// Old style - manual loops
function sumArray(arr) {
    let total = 0;
    for (let i = 0; i < arr.length; i++) {
        total = total + arr[i];
    }
    return total;
}
2025: Concise, Declarative
// Modern style - array methods
const sumArray = arr => arr.reduce((total, n) => total + n, 0);
Tax Calculator Comparison
2021 Approach:
// Lots of if-else statements
function calcTax(income) {
    if (income <= 49020) {
        return income * 0.15;
    } else if (income <= 98040) {
        return 49020 * 0.15 + (income - 49020) * 0.205;
    } else if (income <= 151978) {
        return 49020 * 0.15 + 49020 * 0.205 + (income - 98040) * 0.26;
    }
    // ... more nested conditions
}
Problems:
- Hard to maintain
 - Repetitive calculations
 - Difficult to add new brackets
 - Error-prone
 
2025 Approach:
// Data-driven with iteration
const TAX = [[49020, 0.15], [98040, 0.205], ...];
function calcTax(income) {
    let total = 0;
    let prev = 0;
    
    for (const [max, rate] of TAX) {
        if (income <= prev) break;
        total += Math.min(income, max) - prev) * rate;
        prev = max;
    }
    
    return total;
}
Benefits:
- β Easy to update tax brackets (just change data)
 - β Single source of truth
 - β No code duplication
 - β Scales to any number of brackets
 
Testing Philosophy
2021: Manual Console Testing
// Old way - manual verification
console.log(taxCalc.calcTax(60000));  // Eyeball the result
console.log(taxCalc.calcTax(100000)); // Check each case manually
2025: Automated Test Suite
// Modern way - automated verification
test("calculates tax correctly", () => {
    expect(taxCalc.calcTax(60000)).toBeCloseTo(9603.9, 1);
    expect(taxCalc.calcTax(100000)).toBeCloseTo(17991.9, 1);
    expect(taxCalc.calcTax(49020)).toBeCloseTo(7353, 1);
});
Key Takeaways
Technical Skills Gained
- Progressive Systems: Understanding stepped calculations (taxes, shipping tiers)
 - Array Mastery: map(), filter(), reduce() for data transformation
 - Object Manipulation: Iterating, accessing, transforming key-value pairs
 - Pure Functions: Writing predictable, testable code
 - Edge Case Handling: Boundary values, empty arrays, negative numbers
 
Problem-Solving Patterns
Pattern 1: Accumulation
// Start with initial value, build up result
const sum = numbers.reduce((acc, n) => acc + n, 0);
const product = numbers.reduce((acc, n) => acc * n, 1);
Pattern 2: Transformation
// Convert each item to new form
const doubled = numbers.map(n => n * 2);
const names = users.map(user => user.name);
Pattern 3: Filtering
// Keep only items that pass test
const evens = numbers.filter(n => n % 2 === 0);
const adults = users.filter(user => user.age >= 18);
Common Pitfalls Avoided
- Off-by-one errors in array indexing
 - Mutating vs returning new values
 - Global state pollution
 - Early returns in loops
 - Floating-point comparison issues
 
Further Learning
Practice Exercises
Tax Calculator Extensions:
- Add provincial tax rates
 - Calculate marginal vs effective tax rate
 - Handle different tax years
 
Array Challenges:
- Find median value
 - Remove duplicates
 - Flatten nested arrays
 
Object Operations:
- Deep clone objects
 - Merge multiple objects
 - Transform object shape
 
Recommended Resources
Real-World Applications
These patterns appear everywhere:
- E-commerce: Calculate shipping costs, apply discounts
 - Finance: Interest calculations, loan amortization
 - Analytics: Aggregate user data, generate reports
 - Forms: Validate inputs, transform data for API
 
Module Status: β
 Complete (8/8 tests passing)
Key Bugs Fixed: 5 (tax indexing, loop logic, array
operations)
Time Investment: ~4 hours (including debugging)
Next Module: Module 02: DOM
Manipulation β