javascript

exercises

exercises.js
/**
 * =====================================================
 * 5.5 CALL, APPLY, AND BIND - EXERCISES
 * =====================================================
 * Practice controlling function context
 */

/**
 * Exercise 1: Basic call()
 *
 * Use call() to invoke greet with different contexts.
 */
function greet() {
  return `Hello, ${this.name}!`;
}

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

// TODO: Use call to greet both users
console.log('Exercise 1:');
// console.log(greet.call(???));  // "Hello, Alice!"
// console.log(greet.call(???));  // "Hello, Bob!"

/**
 * Exercise 2: call() with Arguments
 *
 * Use call() to invoke introduce with context and arguments.
 */
function introduce(greeting, profession) {
  return `${greeting}, I'm ${this.name}, a ${profession}.`;
}

const person = { name: 'Charlie' };

// TODO: Use call with greeting "Hi" and profession "developer"
console.log('\nExercise 2:');
// console.log(introduce.call(???));

/**
 * Exercise 3: apply() with Array
 *
 * Use apply() to find max and min of an array.
 */
const numbers = [5, 2, 9, 1, 7, 3];

// TODO: Use apply to get max and min
console.log('\nExercise 3:');
// const max = Math.max.apply(???);
// const min = Math.min.apply(???);
// console.log("Max:", max);  // 9
// console.log("Min:", min);  // 1

/**
 * Exercise 4: bind() Basic
 *
 * Create a bound version of sayHello.
 */
const speaker = {
  name: 'Diana',
  sayHello() {
    return `Hello from ${this.name}`;
  },
};

// TODO: Create bound function
console.log('\nExercise 4:');
// const boundSayHello = ???;
// console.log(boundSayHello());  // "Hello from Diana"

/**
 * Exercise 5: bind() for Partial Application
 *
 * Create specialized multiply functions using bind.
 */
function multiply(a, b) {
  return a * b;
}

// TODO: Create double, triple using bind
console.log('\nExercise 5:');
// const double = ???;
// const triple = ???;
// console.log(double(7));   // 14
// console.log(triple(7));   // 21

/**
 * Exercise 6: Method Borrowing
 *
 * Borrow array methods for an array-like object.
 */
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
};

// TODO: Use call/apply to:
// 1. Convert to real array
// 2. Join elements with "-"
// 3. Map to uppercase
console.log('\nExercise 6:');
// const realArray = ???;
// const joined = ???;
// const uppercased = ???;

/**
 * Exercise 7: Constructor Borrowing
 *
 * Use call to borrow Vehicle constructor in Car.
 */
function Vehicle(type, wheels) {
  this.type = type;
  this.wheels = wheels;
}

function Car(brand, model) {
  // TODO: Borrow Vehicle constructor with type="car" and wheels=4
  this.brand = brand;
  this.model = model;
}

// Test:
console.log('\nExercise 7:');
// const myCar = new Car("Toyota", "Camry");
// console.log(myCar);
// { type: "car", wheels: 4, brand: "Toyota", model: "Camry" }

/**
 * Exercise 8: Event Handler Binding
 *
 * Create a Counter class with a properly bound increment method.
 */
class Counter {
  constructor(name) {
    this.name = name;
    this.count = 0;
    // TODO: Bind increment method
  }

  increment() {
    this.count++;
    return `${this.name}: ${this.count}`;
  }
}

// Test:
console.log('\nExercise 8:');
// const counter = new Counter("MyCounter");
// const inc = counter.increment;
// console.log(inc());  // "MyCounter: 1"
// console.log(inc());  // "MyCounter: 2"

/**
 * Exercise 9: Logger with Partial Application
 *
 * Create specialized loggers using bind.
 */
function log(level, category, message) {
  return `[${level}] [${category}] ${message}`;
}

// TODO: Create specialized loggers
console.log('\nExercise 9:');
// const infoLogger = ???;  // Pre-fill level="INFO"
// const errorLogger = ???;  // Pre-fill level="ERROR"
// const dbInfoLogger = ???;  // Pre-fill level="INFO", category="Database"

// console.log(infoLogger("App", "Starting"));   // "[INFO] [App] Starting"
// console.log(errorLogger("App", "Failed"));    // "[ERROR] [App] Failed"
// console.log(dbInfoLogger("Connected"));       // "[INFO] [Database] Connected"

/**
 * Exercise 10: Safe Type Checking
 *
 * Create a getType function using Object.prototype.toString.
 */
function getType(value) {
  // TODO: Use call with Object.prototype.toString
  // Return just the type name (e.g., "Array", "Object", "Null")
}

// Test:
console.log('\nExercise 10:');
// console.log(getType(null));        // "Null"
// console.log(getType([]));          // "Array"
// console.log(getType({}));          // "Object"
// console.log(getType(() => {}));    // "Function"
// console.log(getType(new Date()));  // "Date"

// =====================================================
// INTERMEDIATE EXERCISES
// =====================================================

/**
 * Exercise 11: Function Mixin
 *
 * Create a mixin that adds event capabilities to any object.
 */
const eventMixin = {
  // TODO: Add on(), off(), emit() methods
  // Use apply to maintain correct 'this' in handlers
};

// Test:
console.log('\nExercise 11:');
// const obj = { name: "TestObject" };
// Object.assign(obj, eventMixin);
// obj.on("test", function(data) { console.log(this.name, data); });
// obj.emit("test", "Hello!");  // "TestObject Hello!"

/**
 * Exercise 12: Bound Method Decorator
 *
 * Create a function that binds all methods of an object.
 */
function bindAllMethods(obj) {
  // TODO: Bind all methods to the object
}

// Test:
console.log('\nExercise 12:');
// const user = {
//     name: "Alice",
//     greet() { return `Hello, ${this.name}`; },
//     bye() { return `Goodbye, ${this.name}`; }
// };
// bindAllMethods(user);
// const { greet, bye } = user;
// console.log(greet());  // "Hello, Alice"
// console.log(bye());    // "Goodbye, Alice"

/**
 * Exercise 13: Delayed Execution with bind
 *
 * Create a delay function that preserves 'this'.
 */
function delay(fn, ms) {
  // TODO: Return a function that delays fn by ms
  // Preserve 'this' and arguments
}

// Test:
console.log('\nExercise 13:');
// const obj = {
//     name: "Timer",
//     log() { console.log(this.name, "executed"); }
// };
// obj.delayedLog = delay(obj.log, 100);
// obj.delayedLog();  // After 100ms: "Timer executed"

/**
 * Exercise 14: Context Wrapper
 *
 * Create a function that wraps another function with a fixed context.
 */
function withContext(fn, context) {
  // TODO: Return wrapped function
}

// Test:
console.log('\nExercise 14:');
// const ctx = { multiplier: 10 };
// const multiply = function(n) { return n * this.multiplier; };
// const boundMultiply = withContext(multiply, ctx);
// console.log(boundMultiply(5));  // 50

/**
 * Exercise 15: Method Chain with bind
 *
 * Create a calculator class with method chaining where
 * methods can be passed as callbacks.
 */
class Calculator {
  constructor(value = 0) {
    this.value = value;
    // TODO: Bind methods
  }

  add(n) {
    this.value += n;
    return this;
  }

  multiply(n) {
    this.value *= n;
    return this;
  }

  getResult() {
    return this.value;
  }
}

// Test:
console.log('\nExercise 15:');
// const calc = new Calculator(10);
// const { add, multiply, getResult } = calc;
// add(5).multiply(2);
// console.log(getResult());  // 30

// =====================================================
// SOLUTIONS (Uncomment to check your answers)
// =====================================================

/*
// Exercise 1 Solution:
console.log(greet.call(user1));  // "Hello, Alice!"
console.log(greet.call(user2));  // "Hello, Bob!"

// Exercise 2 Solution:
console.log(introduce.call(person, "Hi", "developer"));

// Exercise 3 Solution:
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);

// Exercise 4 Solution:
const boundSayHello = speaker.sayHello.bind(speaker);

// Exercise 5 Solution:
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);

// Exercise 6 Solution:
const realArray = Array.prototype.slice.call(arrayLike);
const joined = Array.prototype.join.call(arrayLike, "-");
const uppercased = Array.prototype.map.call(arrayLike, x => x.toUpperCase());

// Exercise 7 Solution:
function Car(brand, model) {
    Vehicle.call(this, "car", 4);
    this.brand = brand;
    this.model = model;
}

// Exercise 8 Solution:
class Counter {
    constructor(name) {
        this.name = name;
        this.count = 0;
        this.increment = this.increment.bind(this);
    }
    
    increment() {
        this.count++;
        return `${this.name}: ${this.count}`;
    }
}

// Exercise 9 Solution:
const infoLogger = log.bind(null, "INFO");
const errorLogger = log.bind(null, "ERROR");
const dbInfoLogger = log.bind(null, "INFO", "Database");

// Exercise 10 Solution:
function getType(value) {
    const str = Object.prototype.toString.call(value);
    return str.slice(8, -1);
}

// Exercise 11 Solution:
const eventMixin = {
    on(event, handler) {
        this._events = this._events || {};
        (this._events[event] = this._events[event] || []).push(handler);
    },
    off(event, handler) {
        if (this._events?.[event]) {
            this._events[event] = this._events[event].filter(h => h !== handler);
        }
    },
    emit(event, ...args) {
        (this._events?.[event] || []).forEach(h => h.apply(this, args));
    }
};

// Exercise 12 Solution:
function bindAllMethods(obj) {
    for (const key of Object.keys(obj)) {
        if (typeof obj[key] === 'function') {
            obj[key] = obj[key].bind(obj);
        }
    }
}

// Exercise 13 Solution:
function delay(fn, ms) {
    return function(...args) {
        setTimeout(() => fn.apply(this, args), ms);
    };
}

// Exercise 14 Solution:
function withContext(fn, context) {
    return function(...args) {
        return fn.apply(context, args);
    };
}

// Exercise 15 Solution:
class Calculator {
    constructor(value = 0) {
        this.value = value;
        this.add = this.add.bind(this);
        this.multiply = this.multiply.bind(this);
        this.getResult = this.getResult.bind(this);
    }
    
    add(n) {
        this.value += n;
        return this;
    }
    
    multiply(n) {
        this.value *= n;
        return this;
    }
    
    getResult() {
        return this.value;
    }
}
*/
Exercises - JavaScript Tutorial | DeepML