javascript

exercises

exercises.js
// ============================================================================
// 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!');
Exercises - JavaScript Tutorial | DeepML