Lesson 1.6: Practice - Calculator
Duration: 60 minutes
Learning Objectives
By the end of this lesson, you will be able to:
- Apply all JavaScript concepts learned in this module
- Build a functional calculator step by step
- Handle user input and edge cases
- Practice combining variables, operators, conditions, and loops
Project Overview
We will build a command-line calculator that can:
- Perform basic math operations (+, -, *, /)
- Handle multiple calculations
- Validate user input
- Display formatted results
This project combines everything from Module 1:
- Variables: Store numbers and results
- Data Types: Work with numbers and strings
- Operators: Perform calculations
- Conditions: Choose operations and validate input
- Loops: Allow multiple calculations
Step 1: Basic Calculation
Let's start with the simplest version - two numbers and an operation.
// Basic calculator - Step 1
const num1 = 10;
const num2 = 5;
const operation = '+';
let result;
if (operation === '+') {
result = num1 + num2;
} else if (operation === '-') {
result = num1 - num2;
} else if (operation === '*') {
result = num1 * num2;
} else if (operation === '/') {
result = num1 / num2;
} else {
result = 'Invalid operation';
}
console.log(`${num1} ${operation} ${num2} = ${result}`);
// Output: 10 + 5 = 15
Try It Yourself
Change the values of num1, num2, and operation to test different calculations.
Step 2: Using Switch Statement
A switch statement is cleaner for choosing between operations.
// Calculator with switch - Step 2
const num1 = 20;
const num2 = 4;
const operation = '/';
let result;
switch (operation) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num1 / num2;
break;
default:
result = 'Unknown operation';
}
console.log(`${num1} ${operation} ${num2} = ${result}`);
// Output: 20 / 4 = 5
Step 3: Adding Input Validation
Real calculators need to handle edge cases.
// Calculator with validation - Step 3
const num1 = 10;
const num2 = 0;
const operation = '/';
// Validate inputs
if (typeof num1 !== 'number' || typeof num2 !== 'number') {
console.log('Error: Both inputs must be numbers');
} else if (isNaN(num1) || isNaN(num2)) {
console.log('Error: Invalid number');
} else {
let result;
let hasError = false;
switch (operation) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 === 0) {
console.log('Error: Cannot divide by zero');
hasError = true;
} else {
result = num1 / num2;
}
break;
default:
console.log(`Error: Unknown operation "${operation}"`);
hasError = true;
}
if (!hasError) {
console.log(`${num1} ${operation} ${num2} = ${result}`);
}
}
Step 4: Creating a Calculate Function
Let's organize our code into a reusable function.
// Calculator function - Step 4
function calculate(num1, operation, num2) {
// Validate numbers
if (typeof num1 !== 'number' || typeof num2 !== 'number') {
return 'Error: Both inputs must be numbers';
}
if (isNaN(num1) || isNaN(num2)) {
return 'Error: Invalid number';
}
// Perform calculation
let result;
switch (operation) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 === 0) {
return 'Error: Cannot divide by zero';
}
result = num1 / num2;
break;
case '%':
if (num2 === 0) {
return 'Error: Cannot divide by zero';
}
result = num1 % num2;
break;
case '**':
result = num1 ** num2;
break;
default:
return `Error: Unknown operation "${operation}"`;
}
return `${num1} ${operation} ${num2} = ${result}`;
}
// Test the function
console.log(calculate(10, '+', 5)); // 10 + 5 = 15
console.log(calculate(10, '-', 3)); // 10 - 3 = 7
console.log(calculate(10, '*', 4)); // 10 * 4 = 40
console.log(calculate(10, '/', 2)); // 10 / 2 = 5
console.log(calculate(10, '/', 0)); // Error: Cannot divide by zero
console.log(calculate(2, '**', 8)); // 2 ** 8 = 256
Step 5: Multiple Calculations
Use an array to perform multiple calculations.
// Multiple calculations - Step 5
function calculate(num1, operation, num2) {
if (typeof num1 !== 'number' || typeof num2 !== 'number') {
return { error: 'Both inputs must be numbers' };
}
let result;
switch (operation) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 === 0) {
return { error: 'Cannot divide by zero' };
}
result = num1 / num2;
break;
default:
return { error: `Unknown operation: ${operation}` };
}
return { result, expression: `${num1} ${operation} ${num2} = ${result}` };
}
// Array of calculations to perform
const calculations = [
{ num1: 100, op: '+', num2: 50 },
{ num1: 100, op: '-', num2: 30 },
{ num1: 25, op: '*', num2: 4 },
{ num1: 144, op: '/', num2: 12 },
{ num1: 10, op: '/', num2: 0 },
];
console.log('Calculator Results:');
console.log('===================');
for (const calc of calculations) {
const output = calculate(calc.num1, calc.op, calc.num2);
if (output.error) {
console.log(`${calc.num1} ${calc.op} ${calc.num2} = Error: ${output.error}`);
} else {
console.log(output.expression);
}
}
Step 6: Calculator with History
Track all calculations for review.
// Calculator with history - Step 6
const history = [];
function calculate(num1, operation, num2) {
let result;
let error = null;
switch (operation) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 === 0) {
error = 'Division by zero';
} else {
result = num1 / num2;
}
break;
default:
error = 'Unknown operation';
}
// Store in history
const entry = {
num1,
operation,
num2,
result: error ? null : result,
error,
timestamp: new Date().toLocaleTimeString(),
};
history.push(entry);
return entry;
}
function showHistory() {
console.log('\n--- Calculation History ---');
if (history.length === 0) {
console.log('No calculations yet.');
return;
}
for (let i = 0; i < history.length; i++) {
const h = history[i];
if (h.error) {
console.log(`${i + 1}. ${h.num1} ${h.operation} ${h.num2} = ERROR (${h.error})`);
} else {
console.log(`${i + 1}. ${h.num1} ${h.operation} ${h.num2} = ${h.result}`);
}
}
console.log('---------------------------\n');
}
function getLastResult() {
if (history.length === 0) {
return null;
}
return history[history.length - 1].result;
}
// Perform some calculations
calculate(50, '+', 25);
calculate(100, '-', 33);
calculate(12, '*', 12);
calculate(100, '/', 4);
calculate(10, '/', 0);
// Show all history
showHistory();
// Show statistics
const successfulCalcs = history.filter((h) => h.error === null);
console.log(`Total calculations: ${history.length}`);
console.log(`Successful: ${successfulCalcs.length}`);
console.log(`Errors: ${history.length - successfulCalcs.length}`);
Step 7: Complete Calculator
Here is the full calculator with all features combined.
// Complete Calculator - Final Version
// ============ Configuration ============
const OPERATIONS = {
'+': { name: 'Addition', fn: (a, b) => a + b },
'-': { name: 'Subtraction', fn: (a, b) => a - b },
'*': { name: 'Multiplication', fn: (a, b) => a * b },
'/': { name: 'Division', fn: (a, b) => a / b },
'%': { name: 'Modulo', fn: (a, b) => a % b },
'**': { name: 'Power', fn: (a, b) => a ** b },
};
// ============ State ============
const calculatorHistory = [];
// ============ Core Functions ============
function performCalculation(num1, operation, num2) {
// Validate inputs
if (typeof num1 !== 'number' || typeof num2 !== 'number') {
return { success: false, error: 'Invalid input: numbers required' };
}
if (isNaN(num1) || isNaN(num2)) {
return { success: false, error: 'Invalid input: NaN detected' };
}
// Check operation exists
if (!OPERATIONS[operation]) {
return { success: false, error: `Unknown operation: ${operation}` };
}
// Check division by zero
if ((operation === '/' || operation === '%') && num2 === 0) {
return { success: false, error: 'Cannot divide by zero' };
}
// Perform calculation
const result = OPERATIONS[operation].fn(num1, num2);
// Handle edge cases
if (!isFinite(result)) {
return { success: false, error: 'Result is not a finite number' };
}
// Record in history
const entry = {
id: calculatorHistory.length + 1,
num1,
operation,
num2,
result,
timestamp: new Date().toISOString(),
};
calculatorHistory.push(entry);
return {
success: true,
result,
formatted: formatResult(num1, operation, num2, result),
};
}
function formatResult(num1, operation, num2, result) {
// Format numbers nicely
const formatNum = (n) => {
if (Number.isInteger(n)) {
return n.toString();
}
return n.toFixed(4).replace(/\.?0+$/, '');
};
return `${formatNum(num1)} ${operation} ${formatNum(num2)} = ${formatNum(result)}`;
}
// ============ History Functions ============
function getHistory() {
return [...calculatorHistory];
}
function clearHistory() {
calculatorHistory.length = 0;
console.log('History cleared.');
}
function printHistory() {
console.log('\n========== Calculator History ==========');
if (calculatorHistory.length === 0) {
console.log('No calculations performed yet.');
} else {
for (const entry of calculatorHistory) {
console.log(
`#${entry.id}: ${formatResult(entry.num1, entry.operation, entry.num2, entry.result)}`
);
}
}
console.log('=========================================\n');
}
function getStatistics() {
if (calculatorHistory.length === 0) {
return null;
}
const results = calculatorHistory.map((h) => h.result);
return {
count: calculatorHistory.length,
sum: results.reduce((a, b) => a + b, 0),
average: results.reduce((a, b) => a + b, 0) / results.length,
max: Math.max(...results),
min: Math.min(...results),
};
}
// ============ Demo ============
console.log('JavaScript Calculator Demo');
console.log('==========================\n');
// Basic operations
console.log('Basic Operations:');
console.log(performCalculation(10, '+', 5).formatted);
console.log(performCalculation(10, '-', 3).formatted);
console.log(performCalculation(10, '*', 4).formatted);
console.log(performCalculation(10, '/', 4).formatted);
// More operations
console.log('\nMore Operations:');
console.log(performCalculation(17, '%', 5).formatted);
console.log(performCalculation(2, '**', 10).formatted);
// Decimal numbers
console.log('\nDecimal Numbers:');
console.log(performCalculation(3.14159, '*', 2).formatted);
console.log(performCalculation(100, '/', 3).formatted);
// Error handling
console.log('\nError Handling:');
const divByZero = performCalculation(10, '/', 0);
console.log(`10 / 0 = ${divByZero.error}`);
const unknownOp = performCalculation(10, '^', 2);
console.log(`10 ^ 2 = ${unknownOp.error}`);
// Show history
printHistory();
// Show statistics
console.log('Statistics:');
const stats = getStatistics();
if (stats) {
console.log(`- Calculations: ${stats.count}`);
console.log(`- Sum of results: ${stats.sum.toFixed(2)}`);
console.log(`- Average result: ${stats.average.toFixed(2)}`);
console.log(`- Max result: ${stats.max}`);
console.log(`- Min result: ${stats.min}`);
}
Running Your Calculator
Option 1: Node.js
Save the code to a file called calculator.js and run:
node calculator.js
Option 2: Browser Console
- Open your browser
- Press F12 to open Developer Tools
- Go to the Console tab
- Paste the code and press Enter
Exercises
Exercise 1: Add Square Root
Add a square root operation to the calculator. It only needs one number.
// Hint: Use Math.sqrt()
Solution
function squareRoot(num) {
if (num < 0) {
return { success: false, error: 'Cannot calculate square root of negative number' };
}
const result = Math.sqrt(num);
return {
success: true,
result,
formatted: `sqrt(${num}) = ${result}`,
};
}
console.log(squareRoot(16).formatted); // sqrt(16) = 4
console.log(squareRoot(2).formatted); // sqrt(2) = 1.4142...
console.log(squareRoot(-4).error); // Cannot calculate...
Exercise 2: Chain Calculations
Use the result of one calculation as input for the next.
// Start with 10, add 5, multiply by 2, subtract 10
// Expected: ((10 + 5) * 2) - 10 = 20
Solution
let value = 10;
value = value + 5; // 15
console.log(`After +5: ${value}`);
value = value * 2; // 30
console.log(`After *2: ${value}`);
value = value - 10; // 20
console.log(`After -10: ${value}`);
console.log(`Final result: ${value}`);
// Or using our calculate function:
function chainCalculate(start, operations) {
let result = start;
for (const op of operations) {
const calc = performCalculation(result, op.operation, op.value);
if (!calc.success) {
return { success: false, error: calc.error };
}
console.log(calc.formatted);
result = calc.result;
}
return { success: true, result };
}
const chain = chainCalculate(10, [
{ operation: '+', value: 5 },
{ operation: '*', value: 2 },
{ operation: '-', value: 10 },
]);
console.log(`Chain result: ${chain.result}`); // 20
Exercise 3: Add Memory Feature
Add memory functions: store, recall, and clear.
// MC - Memory Clear
// MR - Memory Recall
// M+ - Add to Memory
Solution
let memory = 0;
function memoryClear() {
memory = 0;
console.log('Memory cleared');
}
function memoryRecall() {
console.log(`Memory: ${memory}`);
return memory;
}
function memoryAdd(value) {
memory += value;
console.log(`Added ${value} to memory. Memory: ${memory}`);
}
function memorySubtract(value) {
memory -= value;
console.log(`Subtracted ${value} from memory. Memory: ${memory}`);
}
// Demo
memoryClear(); // Memory cleared
memoryAdd(50); // Added 50. Memory: 50
memoryAdd(25); // Added 25. Memory: 75
memorySubtract(10); // Subtracted 10. Memory: 65
console.log(memoryRecall()); // Memory: 65
memoryClear(); // Memory cleared
console.log(memoryRecall()); // Memory: 0
Key Takeaways
- Break problems into steps - Start simple, add features gradually
- Validate inputs - Check for invalid data before processing
- Handle edge cases - Division by zero, invalid operations
- Keep history - Useful for debugging and user experience
- Format output - Make results readable
- Organize code - Use functions to separate concerns
What You Built
Congratulations! You have created a calculator that demonstrates:
- Variable declaration and assignment
- Working with numbers and strings
- Arithmetic and comparison operators
- Conditional logic with if/else and switch
- Loops for processing multiple calculations
- Arrays for storing history
- Functions for code organization
Resources
| Resource | Type | Description |
|---|---|---|
| MDN: Math Object | Documentation | Built-in math functions |
| JavaScript.info: Functions | Tutorial | Functions tutorial |
| Node.js Documentation | Documentation | Running JavaScript with Node |
Module Complete!
You have finished Module 1: JavaScript Basics. You now understand:
- How to store data in variables
- Different data types and their uses
- Operators for calculations and comparisons
- Making decisions with conditions
- Repeating code with loops
Next Step: Continue to Module 2 to learn why TypeScript improves on JavaScript and how to set it up.