javascript
exercises
exercises.js⚡javascript
/**
* =====================================================
* 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;
}
}
*/