INFO 153A/253A - Week 6
UC Berkeley School of Information
Instructor: Kay Ashaolu
// Basic function declaration
function greetUser(name) {
return "Hello, " + name + "!";
}
// Function invocation
const greeting = greetUser("Alice");
console.log(greeting); // "Hello, Alice!"
function greetUser(name) - creates named function with parametername acts as placeholder for passed valuesreturn statement specifies function outputgreetUser("Alice") - invocation passes "Alice" to parameterlet globalMessage = "I'm available everywhere";
function demonstrateScope() {
let localMessage = "I only exist in this function";
if (true) {
let blockMessage = "I only exist in this block";
const alsoBlockScoped = "Me too!";
console.log(globalMessage); // ✅ Accessible
console.log(localMessage); // ✅ Accessible
console.log(blockMessage); // ✅ Accessible
}
console.log(globalMessage); // ✅ Accessible
console.log(localMessage); // ✅ Accessible
// console.log(blockMessage); // ❌ Error: not defined
}
// console.log(localMessage); // ❌ Error: not defined
let/const exist only within curly braces| Traditional Function | Arrow Function | Best Use Case |
|---|---|---|
function add(a, b) { return a + b; } |
const add = (a, b) => a + b; |
Simple operations |
function square(x) { return x * x; } |
const square = x => x * x; |
Single parameter |
function process() { /* multiple lines */ } |
const process = () => { /* multiple lines */ }; |
No parameters |
=>// Functions with default parameters
function createTask(title, priority = 'medium', dueDate = null) {
return {
id: Date.now(),
title: title,
priority: priority,
dueDate: dueDate,
completed: false
};
}
// Usage examples showing parameter flexibility
const urgentTask = createTask("Fix critical bug", "high", "2024-10-15");
const normalTask = createTask("Review documentation", "medium");
const simpleTask = createTask("Update README");
console.log(simpleTask);
// { id: 1729123456789, title: "Update README", priority: "medium",
// dueDate: null, completed: false }
undefined or not provided// Functions that return different types
function validateEmail(email) {
if (!email || !email.includes('@')) {
return { valid: false, error: 'Invalid email format' };
}
return { valid: true, error: null };
}
function processUserInput(input) {
// Early returns for different conditions
if (!input) return null;
if (input.length < 3) return 'Too short';
if (input.length > 50) return 'Too long';
return input.trim().toLowerCase();
}
// Using return values in practice
const emailCheck = validateEmail('user@example.com');
if (emailCheck.valid) {
console.log('Email is valid!');
} else {
console.log('Error:', emailCheck.error);
}
// Chaining function calls
const userInput = processUserInput(' Hello World ');
if (userInput) {
console.log('Processed:', userInput); // "hello world"
}
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Basic conditional with comprehensive logic
function determineAccessLevel(user) {
if (!user) {
return 'none';
}
if (user.role === 'admin') {
return 'full';
} else if (user.role === 'moderator') {
return user.verified ? 'elevated' : 'standard';
} else if (user.role === 'user') {
return user.premiumMember ? 'premium' : 'basic';
} else {
return 'guest';
}
}
// Guard clause pattern for cleaner code
function processUser(user) {
// Handle edge cases first
if (!user) return null;
if (!user.id) return null;
if (user.banned) return { error: 'User is banned' };
// Main processing logic
return {
id: user.id,
displayName: user.name || 'Anonymous',
permissions: determineAccessLevel(user)
};
}
// Switch statement for multiple discrete options
function getStatusMessage(status) {
switch (status.toLowerCase()) {
case 'pending':
return 'Your request is being processed';
case 'approved':
return 'Request approved! You can proceed';
case 'rejected':
return 'Request denied. Please contact support';
case 'cancelled':
return 'Request was cancelled by user';
default:
return 'Unknown status';
}
}
// Modern alternative: Object-based lookup
const STATUS_MESSAGES = {
pending: 'Your request is being processed',
approved: 'Request approved! You can proceed',
rejected: 'Request denied. Please contact support',
cancelled: 'Request was cancelled by user',
default: 'Unknown status'
};
function getStatusMessageModern(status) {
return STATUS_MESSAGES[status.toLowerCase()] || STATUS_MESSAGES.default;
}
// Basic ternary usage
const userType = user.premium ? 'Premium User' : 'Standard User';
// Ternary with function calls
const greeting = isLoggedIn ? getPersonalizedGreeting(user) : getDefaultGreeting();
// Nested ternary (use sparingly!)
const priority =
urgency > 8 ? 'critical' :
urgency > 5 ? 'high' :
urgency > 2 ? 'medium' : 'low';
// Ternary for conditional assignment
const theme = user.preferences?.darkMode ? 'dark' : 'light';
// Ternary in template literals
const message = `Welcome, ${user.name || 'Guest'}! You have ${
user.notifications ? user.notifications.length : 0
} new messages.`;
condition ? value-if-true : value-if-falsefalse - boolean false0 - number zero"" - empty stringnull - intentional absenceundefined - uninitializedNaN - not a number"0" - string with zero[] - empty array{} - empty object"false" - string "false"-1 - negative numbersInfinity - infinite values// Practical examples of truthy/falsy usage
function processInput(input) {
// Check for any falsy value
if (!input) {
return 'No input provided';
}
// Default value using short-circuit evaluation
const name = input.name || 'Anonymous';
// Existence checking
if (input.items && input.items.length) {
return `Processing ${input.items.length} items for ${name}`;
}
return `Hello, ${name}!`;
}
// Logical AND (&&) - both conditions must be true
const canEdit = user.isAuthenticated && user.hasPermission;
// Logical OR (||) - either condition can be true
const displayName = user.firstName || user.username || 'Guest';
// Logical NOT (!) - reverses truthiness
const isGuest = !user.isAuthenticated;
// Short-circuit evaluation for conditional execution
user.isAdmin && performAdminAction(); // Only runs if user.isAdmin is truthy
// Nullish coalescing (??) - only null or undefined trigger default
const timeout = user.settings?.timeout ?? 5000; // 0 won't trigger default
// Optional chaining (?.) - safely access nested properties
const email = user.profile?.contact?.email;
&& returns first falsy value or last value if all truthy|| returns first truthy value or last value if all falsy?? (nullish coalescing) more precise than ||?. (optional chaining) safely accesses nested properties??) and optional chaining (?.) provide more precise control over default values and property access, especially when dealing with 0, empty strings, or nested objects.
// For loop - when you need precise control
for (let i = 0; i < 5; i++) {
console.log(`Count: ${i}`);
}
// While loop - when you don't know iteration count
let userInput = '';
while (userInput !== 'quit') {
userInput = getUserInput(); // Hypothetical function
processInput(userInput);
}
// For...of loop - cleaner iteration over arrays
const colors = ['red', 'green', 'blue'];
for (const color of colors) {
console.log(`Processing color: ${color}`);
}
// For...in loop - for object properties (use carefully)
const person = { name: 'Alice', age: 30, city: 'Berkeley' };
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
for loop: maximum control, explicit initialization/condition/incrementwhile loop: continue until condition false, unknown iteration countfor...of loop: clean array iteration, no index managementfor...in loop: object properties only (avoid with arrays)const students = [
{ name: 'Alice', grade: 92, major: 'Computer Science' },
{ name: 'Bob', grade: 78, major: 'Information Science' },
{ name: 'Charlie', grade: 85, major: 'Computer Science' },
{ name: 'Diana', grade: 96, major: 'Information Science' }
];
// forEach - perform action on each element (no return value)
students.forEach(student => {
console.log(`${student.name}: ${student.grade}%`);
});
// map - transform each element into new array
const studentNames = students.map(student => student.name);
// Result: ['Alice', 'Bob', 'Charlie', 'Diana']
// filter - create new array with elements that pass a test
const csStudents = students.filter(student => student.major === 'Computer Science');
// Result: [Alice and Charlie objects]
// find - get first element that matches condition
const topStudent = students.find(student => student.grade > 90);
// Result: Alice object
// some - check if ANY element matches condition
const hasFailingStudent = students.some(student => student.grade < 60);
// Result: false
// every - check if ALL elements match condition
const allPassing = students.every(student => student.grade >= 70);
// Result: true
const purchases = [
{ item: 'Coffee', price: 4.50, category: 'beverage' },
{ item: 'Sandwich', price: 8.99, category: 'food' },
{ item: 'Tea', price: 3.25, category: 'beverage' },
{ item: 'Salad', price: 12.50, category: 'food' }
];
// Sum total prices
const totalSpent = purchases.reduce((sum, purchase) => {
return sum + purchase.price;
}, 0);
// Result: 29.24
// Group items by category
const itemsByCategory = purchases.reduce((groups, purchase) => {
const category = purchase.category;
if (!groups[category]) {
groups[category] = [];
}
groups[category].push(purchase.item);
return groups;
}, {});
// Result: { beverage: ['Coffee', 'Tea'], food: ['Sandwich', 'Salad'] }
// Find most expensive item
const mostExpensive = purchases.reduce((max, purchase) => {
return purchase.price > max.price ? purchase : max;
});
// Result: Salad object
const salesData = [
{ product: 'Laptop', price: 999, category: 'Electronics', inStock: true },
{ product: 'Mouse', price: 25, category: 'Electronics', inStock: false },
{ product: 'Keyboard', price: 75, category: 'Electronics', inStock: true },
{ product: 'Monitor', price: 300, category: 'Electronics', inStock: true },
{ product: 'Webcam', price: 50, category: 'Electronics', inStock: false }
];
// Complex data processing pipeline
const availableElectronicsReport = salesData
// Step 1: Only items in stock
.filter(item => item.inStock)
// Step 2: Sort by price (highest first)
.sort((a, b) => b.price - a.price)
// Step 3: Take only top 3 most expensive
.slice(0, 3)
// Step 4: Transform for display
.map((item, index) => ({
rank: index + 1,
name: item.product,
price: `$${item.price}`,
discountPrice: `$${(item.price * 0.9).toFixed(2)}`
}))
// Step 5: Add summary information
.map(item => ({
...item,
savings: `Save $${(item.price.slice(1) * 0.1).toFixed(2)}`
}));
console.log('Top Available Electronics:', availableElectronicsReport);
// Working with array indices
const items = ['apple', 'banana', 'cherry'];
// forEach with index
items.forEach((item, index) => {
console.log(`${index + 1}. ${item}`);
});
// map with index for positioning
const numberedItems = items.map((item, index) => ({
id: index + 1,
name: item,
position: index
}));
// Using entries() for both index and value
for (const [index, item] of items.entries()) {
console.log(`Position ${index}: ${item}`);
}
// Combining multiple arrays
const names = ['Alice', 'Bob', 'Charlie'];
const ages = [25, 30, 35];
const locations = ['Berkeley', 'Oakland', 'San Francisco'];
const people = names.map((name, index) => ({
name: name,
age: ages[index],
location: locations[index]
}));
// Processing nested arrays
const categories = [
{ name: 'Fruits', items: ['apple', 'banana'] },
{ name: 'Vegetables', items: ['carrot', 'broccoli'] }
];
const allItems = categories.flatMap(category =>
category.items.map(item => ({
item,
category: category.name
}))
);
// Validation functions for data processing
function createDataProcessor() {
const validators = {
required: (value, fieldName) =>
value ? null : `${fieldName} is required`,
email: (value) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : 'Invalid email format',
minLength: (value, min) =>
value.length >= min ? null : `Must be at least ${min} characters`
};
return { validators };
}
// Process and validate data collections
function processData(records) {
return records
.map(record => ({
...record,
errors: validateRecord(record),
processed: true,
timestamp: new Date().toISOString()
}))
.filter(record => record.errors.length === 0)
.sort((a, b) => new Date(a.createdDate) - new Date(b.createdDate));
}
function validateRecord(record) {
const errors = [];
// Check required fields
['name', 'email'].forEach(field => {
const error = validators.required(record[field], field);
if (error) errors.push(error);
});
return errors;
}
// Core state management functions
function createStateManager(initialState = {}) {
let state = { ...initialState };
let listeners = [];
function getState() {
return { ...state }; // Return copy to prevent mutations
}
function setState(updates) {
const oldState = { ...state };
state = { ...state, ...updates };
// Notify listeners of changes
listeners.forEach(listener => {
listener(state, oldState);
});
}
return { getState, setState };
}
// Higher-level state operations with array methods
function addStateHelpers(stateManager) {
function updateItem(id, updates) {
const currentState = stateManager.getState();
stateManager.setState({
items: currentState.items.map(item =>
item.id === id ? { ...item, ...updates } : item
)
});
}
function addItem(newItem) {
const currentState = stateManager.getState();
stateManager.setState({
items: [...currentState.items, { ...newItem, id: Date.now() }]
});
}
function removeItem(id) {
const currentState = stateManager.getState();
stateManager.setState({
items: currentState.items.filter(item => item.id !== id)
});
}
return { updateItem, addItem, removeItem };
}
// Custom error types for specific scenarios
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class NetworkError extends Error {
constructor(message, status) {
super(message);
this.name = 'NetworkError';
this.status = status;
}
}
// Process data with comprehensive error handling
function processUserData(users) {
const results = { successful: [], failed: [], errors: [] };
users.forEach(user => {
try {
if (!user.email) {
throw new ValidationError('Email is required', 'email');
}
const processedUser = {
...user,
id: user.id || Date.now().toString(36),
processedAt: new Date().toISOString(),
status: 'active'
};
results.successful.push(processedUser);
} catch (error) {
results.failed.push(user);
results.errors.push({
user: user,
error: error.message,
type: error.name
});
}
});
return results;
}
Let's explore any concepts that need clarification!