javascript
exercises
exercises.js⚡javascript
// ============================================================================
// 14.1 Module Systems Overview - Exercises
// ============================================================================
// Practice creating modules using different patterns.
// Complete each exercise and check your understanding.
// ============================================================================
// EXERCISE 1: Namespace Pattern
// ============================================================================
// Create a namespace called 'GameEngine' with the following structure:
// - GameEngine.utils.randomInt(min, max) - returns random integer
// - GameEngine.utils.clamp(value, min, max) - clamps value to range
// - GameEngine.entities.Player - constructor function
// - GameEngine.entities.Enemy - constructor function
// Your code here:
var GameEngine = GameEngine || {};
// TODO: Create GameEngine.utils with randomInt and clamp functions
// TODO: Create GameEngine.entities with Player and Enemy constructors
// Test your code:
// console.log(GameEngine.utils.randomInt(1, 10));
// console.log(GameEngine.utils.clamp(15, 0, 10)); // Should be 10
// var player = new GameEngine.entities.Player('Hero', 100);
// ============================================================================
// EXERCISE 2: IIFE Module Pattern
// ============================================================================
// Create a TodoModule using the IIFE pattern with:
// - Private: todos array, nextId counter
// - Public: add(text), remove(id), toggle(id), getAll(), getCompleted()
// Your code here:
var TodoModule = (function () {
// TODO: Private variables
// TODO: Return public API
return {
// add, remove, toggle, getAll, getCompleted
};
})();
// Test your code:
// TodoModule.add('Learn JavaScript');
// TodoModule.add('Build a project');
// console.log(TodoModule.getAll());
// TodoModule.toggle(1);
// console.log(TodoModule.getCompleted());
// ============================================================================
// EXERCISE 3: Revealing Module Pattern
// ============================================================================
// Create a ShoppingCart module with:
// - Private: items array, taxRate
// - Methods: addItem(name, price, qty), removeItem(name), getSubtotal(),
// getTax(), getTotal(), clear(), getItems()
// Your code here:
var ShoppingCart = (function () {
// TODO: Implement the shopping cart
return {};
})();
// Test your code:
// ShoppingCart.addItem('Book', 29.99, 2);
// ShoppingCart.addItem('Pen', 1.99, 5);
// console.log('Subtotal:', ShoppingCart.getSubtotal());
// console.log('Tax:', ShoppingCart.getTax());
// console.log('Total:', ShoppingCart.getTotal());
// ============================================================================
// EXERCISE 4: Module with Dependencies
// ============================================================================
// Create a UserAuthentication module that depends on:
// - A storage service (for saving tokens)
// - A crypto service (for hashing passwords)
// The module should have: login(user, pass), logout(), isAuthenticated()
// Mock services (use these as dependencies):
var StorageService = {
data: {},
set: function (key, value) {
this.data[key] = value;
},
get: function (key) {
return this.data[key];
},
remove: function (key) {
delete this.data[key];
},
};
var CryptoService = {
hash: function (str) {
// Simple mock hash (don't use in production!)
return btoa(str).split('').reverse().join('');
},
compare: function (str, hash) {
return this.hash(str) === hash;
},
};
// Your code here:
var UserAuthentication = (function (storage, crypto) {
// TODO: Implement authentication logic
// Mock users database
var users = {
admin: crypto.hash('password123'),
};
return {
// login, logout, isAuthenticated, getCurrentUser
};
})(StorageService, CryptoService);
// Test your code:
// console.log(UserAuthentication.login('admin', 'password123')); // true
// console.log(UserAuthentication.isAuthenticated()); // true
// console.log(UserAuthentication.getCurrentUser()); // 'admin'
// UserAuthentication.logout();
// console.log(UserAuthentication.isAuthenticated()); // false
// ============================================================================
// EXERCISE 5: Event Emitter Module
// ============================================================================
// Create an EventEmitter module (pub/sub pattern) with:
// - on(event, callback) - subscribe to event
// - off(event, callback) - unsubscribe from event
// - emit(event, data) - trigger event with data
// - once(event, callback) - subscribe for single emission
// Your code here:
var EventEmitter = (function () {
// TODO: Private events object { eventName: [callbacks] }
return {
// on, off, emit, once
};
})();
// Test your code:
// EventEmitter.on('userLogin', function(user) {
// console.log('User logged in:', user);
// });
// EventEmitter.emit('userLogin', { name: 'Alice' });
// EventEmitter.once('notification', function(msg) {
// console.log('Notification:', msg);
// });
// EventEmitter.emit('notification', 'Hello!'); // Triggers
// EventEmitter.emit('notification', 'Hello again!'); // Doesn't trigger
// ============================================================================
// EXERCISE 6: State Management Module
// ============================================================================
// Create a Store module (similar to Redux) with:
// - getState() - returns current state
// - dispatch(action) - updates state based on action
// - subscribe(listener) - called when state changes
// Actions: { type: 'INCREMENT' }, { type: 'DECREMENT' }, { type: 'SET', value: n }
// Your code here:
var Store = (function () {
// TODO: Implement a simple state management store
return {
// getState, dispatch, subscribe
};
})();
// Test your code:
// Store.subscribe(function(state) {
// console.log('State changed:', state);
// });
// Store.dispatch({ type: 'INCREMENT' });
// Store.dispatch({ type: 'INCREMENT' });
// Store.dispatch({ type: 'SET', value: 10 });
// console.log('Final state:', Store.getState());
// ============================================================================
// EXERCISE 7: Logger Module with Levels
// ============================================================================
// Create a Logger module with:
// - Levels: DEBUG, INFO, WARN, ERROR (in order of severity)
// - setLevel(level) - only logs at or above this level
// - debug(msg), info(msg), warn(msg), error(msg)
// - getHistory() - returns all logged messages
// - clear() - clears history
// Your code here:
var Logger = (function () {
// TODO: Implement the logger
return {
// setLevel, debug, info, warn, error, getHistory, clear
};
})();
// Test your code:
// Logger.setLevel('INFO');
// Logger.debug('This won\'t show');
// Logger.info('Application started');
// Logger.warn('Memory usage high');
// Logger.error('Connection failed');
// console.log(Logger.getHistory());
// ============================================================================
// EXERCISE 8: API Client Module
// ============================================================================
// Create an APIClient module with:
// - setBaseURL(url) - sets the base URL
// - setHeader(key, value) - sets a header
// - get(endpoint) - simulated GET request
// - post(endpoint, data) - simulated POST request
// Note: Use setTimeout to simulate async behavior
// Your code here:
var APIClient = (function () {
// TODO: Implement the API client
return {
// setBaseURL, setHeader, get, post
};
})();
// Test your code:
// APIClient.setBaseURL('https://api.example.com');
// APIClient.setHeader('Authorization', 'Bearer token123');
// APIClient.get('/users').then(response => console.log(response));
// APIClient.post('/users', { name: 'Alice' }).then(response => console.log(response));
// ============================================================================
// EXERCISE 9: Validator Module
// ============================================================================
// Create a Validator module with chainable validation:
// - value(v) - sets the value to validate
// - required() - value must exist
// - minLength(n) - string min length
// - maxLength(n) - string max length
// - email() - must be valid email format
// - isValid() - returns true/false
// - getErrors() - returns array of error messages
// Your code here:
var Validator = (function () {
// TODO: Implement chainable validator
return {
// value, required, minLength, maxLength, email, isValid, getErrors
};
})();
// Test your code:
// var result = Validator.value('').required().minLength(3).isValid();
// console.log('Valid:', result); // false
// console.log('Errors:', Validator.getErrors()); // ['Value is required', 'Minimum length is 3']
// Validator.value('test@example.com').email().isValid(); // true
// ============================================================================
// EXERCISE 10: Plugin System
// ============================================================================
// Create a PluginSystem module that allows:
// - register(name, plugin) - register a plugin object
// - unregister(name) - remove a plugin
// - use(name, ...args) - execute a plugin's execute method
// - list() - list all registered plugins
// Plugins should have: { name, version, execute(args) }
// Your code here:
var PluginSystem = (function () {
// TODO: Implement the plugin system
return {
// register, unregister, use, list
};
})();
// Test your code:
// PluginSystem.register('uppercase', {
// name: 'Uppercase Transform',
// version: '1.0.0',
// execute: function(text) { return text.toUpperCase(); }
// });
// PluginSystem.register('reverse', {
// name: 'String Reverse',
// version: '1.0.0',
// execute: function(text) { return text.split('').reverse().join(''); }
// });
// console.log(PluginSystem.list());
// console.log(PluginSystem.use('uppercase', 'hello')); // 'HELLO'
// console.log(PluginSystem.use('reverse', 'hello')); // 'olleh'
// ============================================================================
// BONUS CHALLENGE: Implement Your Own Module Loader
// ============================================================================
// Create a simple module loader that supports:
// - define(name, deps, factory) - define a module
// - require(deps, callback) - load modules and execute callback
// Similar to AMD but simplified
// Your code here:
var ModuleLoader = (function () {
// TODO: Implement module loader
return {
// define, require
};
})();
// Test your code:
// ModuleLoader.define('greet', [], function() {
// return {
// hello: function(name) { return 'Hello, ' + name + '!'; }
// };
// });
// ModuleLoader.define('app', ['greet'], function(greet) {
// return {
// run: function() { console.log(greet.hello('World')); }
// };
// });
// ModuleLoader.require(['app'], function(app) {
// app.run(); // 'Hello, World!'
// });
// ============================================================================
// SOLUTIONS (Uncomment to check your work)
// ============================================================================
/*
// Exercise 1 Solution:
GameEngine.utils = {
randomInt: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
clamp: function(value, min, max) {
return Math.min(Math.max(value, min), max);
}
};
GameEngine.entities = {
Player: function(name, health) {
this.name = name;
this.health = health;
},
Enemy: function(type, damage) {
this.type = type;
this.damage = damage;
}
};
// Exercise 2 Solution:
var TodoModule = (function() {
var todos = [];
var nextId = 1;
return {
add: function(text) {
todos.push({ id: nextId++, text: text, completed: false });
},
remove: function(id) {
todos = todos.filter(function(t) { return t.id !== id; });
},
toggle: function(id) {
todos = todos.map(function(t) {
if (t.id === id) t.completed = !t.completed;
return t;
});
},
getAll: function() { return [...todos]; },
getCompleted: function() {
return todos.filter(function(t) { return t.completed; });
}
};
})();
// Exercise 5 Solution:
var EventEmitter = (function() {
var events = {};
return {
on: function(event, callback) {
if (!events[event]) events[event] = [];
events[event].push(callback);
},
off: function(event, callback) {
if (!events[event]) return;
events[event] = events[event].filter(function(cb) {
return cb !== callback;
});
},
emit: function(event, data) {
if (!events[event]) return;
events[event].forEach(function(callback) {
callback(data);
});
},
once: function(event, callback) {
var self = this;
function wrapper(data) {
callback(data);
self.off(event, wrapper);
}
this.on(event, wrapper);
}
};
})();
*/
console.log('Module Systems Exercises loaded. Complete each TODO section!');