Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
675d70d
Complete assignment 01: Deep Clone
valovaViktoria Dec 13, 2025
13ab980
Complete assignment 02: Debounce & Throttle
valovaViktoria Dec 13, 2025
ab01443
Complete assignment 03: Custom Bind
valovaViktoria Dec 13, 2025
ca49b6d
Complete assignment 04: Memoization
valovaViktoria Dec 13, 2025
9c37fb0
Complete assignment 05: Promise Utilities
valovaViktoria Dec 13, 2025
341c553
Complete assignment 06: Async Queue
valovaViktoria Dec 13, 2025
356666e
Complete assignment 07: Retry With Backoff
valovaViktoria Dec 13, 2025
456d364
Complete assignment 08: Event Emitter
valovaViktoria Dec 14, 2025
1cce7a3
Complete assignment 09: Observable
valovaViktoria Dec 14, 2025
b2add10
Complete assignment 10: LRU Cache
valovaViktoria Dec 14, 2025
edf9704
Complete assignment 11: Singleton
valovaViktoria Dec 14, 2025
1f707fe
Complete assignment 12: Factory Pattern
valovaViktoria Dec 14, 2025
11705f7
Complete assignment 13: Decorator Pattern
valovaViktoria Dec 14, 2025
c7a216f
Complete assignment 14: Middleware Pipeline
valovaViktoria Dec 14, 2025
baa6391
Complete assignment 15: Dependency Injection
valovaViktoria Dec 14, 2025
57d94a9
Complete assignment 16: State Machine
valovaViktoria Dec 15, 2025
2857efd
Complete assignment 17: Command Pattern
valovaViktoria Dec 15, 2025
4c949df
Complete assignment 18: Strategy Pattern
valovaViktoria Dec 15, 2025
6e079ec
Complete assignment 19: Proxy Pattern
valovaViktoria Dec 15, 2025
0f8fa36
Complete assignment 20: Builder Pattern
valovaViktoria Dec 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions 01-deep-clone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,83 @@
* @returns {*} A deep clone of the input value
*/
function deepClone(value, visited = new WeakMap()) {
// TODO: Implement deep cloning

// Step 1: Handle primitives (return as-is)
// Primitives: null, undefined, number, string, boolean, symbol, bigint
if (value == null || typeof value !== 'object')
return value;

// Step 2: Check for circular references using the visited WeakMap
// If we've seen this object before, return the cached clone
if (visited.has(value))
return visited.get(value);

// Step 3: Handle Date objects
// Create a new Date with the same time value
if (value instanceof Date) {
const clonedDate = new Date(value.getTime());
visited.set(value, clonedDate);

return clonedDate;
}

// Step 4: Handle RegExp objects
// Create a new RegExp with the same source and flags
if (value instanceof RegExp) {
const clonedRegExp = new RegExp(value.source, value.flags);
visited.set(value, clonedRegExp);

return clonedRegExp;
}

// Step 5: Handle Map objects
// Create a new Map and deep clone each key-value pair
if (value instanceof Map) {
const clonedMap = new Map();
visited.set(value, clonedMap);

for (const [key, val] of value) {
clonedMap.set(deepClone(key, visited), deepClone(val, visited));
}

return clonedMap;
}

// Step 6: Handle Set objects
// Create a new Set and deep clone each value
if (value instanceof Set) {
const clonedSet = new Set();
visited.set(value, clonedSet);

for (const item of value) {
clonedSet.add(deepClone(item, visited));
}

return clonedSet;
}

// Step 7: Handle Arrays
// Create a new array and deep clone each element
if (Array.isArray(value)) {
const clonedArray = [];
visited.set(value, clonedArray);

for (let i = 0; i < value.length; i++) {
clonedArray[i] = deepClone(value[i], visited);
}

return clonedArray;
}

// Step 8: Handle plain Objects
// Create a new object and deep clone each property
const clonedObject = {};
visited.set(value, clonedObject);

for (const key in value) {
clonedObject[key] = deepClone(value[key], visited);
}

return undefined; // Broken: Replace with your implementation
return clonedObject;
}

module.exports = { deepClone };
58 changes: 50 additions & 8 deletions 02-debounce-throttle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,39 @@
* @returns {Function} The debounced function with a cancel() method
*/
function debounce(fn, delay) {
// TODO: Implement debounce
// Parameter validation
if (typeof fn !== 'function') {
throw new TypeError(`debounce: invalid type 'fn' argument: ${typeof fn}`);
}

if (typeof delay !== 'number' || isNaN(delay)) {
throw new TypeError(`debounce: invalid type 'delay' argument: ${typeof fn}`);
}

// Ensure delay is non-negative
delay = Math.max(0, delay);

// Step 1: Create a variable to store the timeout ID
let timerId = null;

// Step 2: Create the debounced function that:
// - Clears any existing timeout
// - Sets a new timeout to call fn after delay
// - Preserves `this` context and arguments
function debounced(...args) {
clearTimeout(timerId);

timerId = setTimeout(() => fn.apply(this, args), delay);
}

// Step 3: Add a cancel() method to clear pending timeout
debounced.cancel = () => {
clearTimeout(timerId);
timerId = null;
};

// Step 4: Return the debounced function

// Return a placeholder that doesn't work
throw new Error("Not implemented");
return debounced;
}

/**
Expand All @@ -37,23 +55,47 @@ function debounce(fn, delay) {
* @returns {Function} The throttled function with a cancel() method
*/
function throttle(fn, limit) {
// TODO: Implement throttle
// Parameter validation
if (typeof fn !== 'function') {
throw new TypeError(`throttle: invalid type 'fn' argument: ${typeof fn}`);
}

if (typeof limit !== 'number' || isNaN(limit)) {
throw new TypeError(`throttle: invalid type 'limit' argument: ${typeof fn}`);
}

// Ensure limit is non-negative
limit = Math.max(0, limit);

// Step 1: Create variables to track:
// - Whether we're currently in a throttle period
// - The timeout ID for cleanup
let isThrottled = false;
let timerId = null;

// Step 2: Create the throttled function that:
// - If not throttling, execute fn immediately and start throttle period
// - If throttling, ignore the call
// - Preserves `this` context and arguments
function throttled(...args) {
if (!isThrottled) {
fn.apply(this, args);

isThrottled = true;

timerId = setTimeout(() => (isThrottled = false), limit);
}
}

// Step 3: Add a cancel() method to reset throttle state
throttled.cancel = () => {
isThrottled = false;
clearTimeout(timerId);
timerId = null;
};

// Step 4: Return the throttled function

// Return a placeholder that doesn't work
throw new Error("Not implemented");
return throttled;
}

module.exports = { debounce, throttle };
34 changes: 21 additions & 13 deletions 03-custom-bind/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,37 @@
* @returns {Function} A new bound function
*/
function customBind(fn, context, ...boundArgs) {
// TODO: Implement custom bind

// Step 1: Validate that fn is a function
// Throw TypeError if not
if (typeof fn !== 'function')
throw new TypeError(`customBind: invalid type 'fn' argument: ${typeof fn}`);

// Step 2: Create the bound function
// It should:
// - Combine boundArgs with any new arguments
// - Call the original function with the combined arguments
// - Use the correct `this` context
function bound(...args) {
// Step 3: Handle constructor calls (when used with `new`)
// When called as a constructor:
// - `this` should be a new instance, not the bound context
// - The prototype chain should be preserved
if (new.target) {
const result = fn.apply(this, [...boundArgs, ...args]);

return result && result instanceof Object ? result : this;
}

// Step 3: Handle constructor calls (when used with `new`)
// When called as a constructor:
// - `this` should be a new instance, not the bound context
// - The prototype chain should be preserved
return fn.apply(context, [...boundArgs, ...args]);
}

// Step 4: Preserve the prototype for constructor usage
// boundFunction.prototype = Object.create(fn.prototype)
if (fn.prototype) {
bound.prototype = Object.create(fn.prototype);
}

// Step 5: Return the bound function

// Return placeholder that doesn't work
throw new Error("Not implemented");
return bound;
}

/**
Expand All @@ -44,8 +52,8 @@ function customBind(fn, context, ...boundArgs) {
*/

// Uncomment and implement:
// Function.prototype.customBind = function(context, ...boundArgs) {
// // Your implementation
// };
Function.prototype.customBind = function (context, ...boundArgs) {
return customBind(this, context, ...boundArgs);
};

module.exports = { customBind };
63 changes: 42 additions & 21 deletions 04-memoization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,67 @@
* @returns {Function} Memoized function with cache control methods
*/
function memoize(fn, options = {}) {
// TODO: Implement memoization

// Step 1: Extract options with defaults
// const { maxSize, ttl, keyGenerator } = options;
const {
maxSize = Infinity,
ttl = Infinity,
keyGenerator = defaultKeyGenerator,
} = options;

// Step 2: Create the cache (use Map for ordered keys)
// const cache = new Map();
const cache = new Map();

// Step 3: Create default key generator
// Default: JSON.stringify(args) or args.join(',')
function defaultKeyGenerator(args) {
return JSON.stringify(args);
}

// Step 4: Create the memoized function
// - Generate cache key from arguments
// - Check if key exists and is not expired (TTL)
// - If cached, return cached value
// - If not cached, call fn and store result
// - Handle maxSize eviction (remove oldest)
function memoized(...args) {
const key = keyGenerator(args);
const now = Date.now();

const cached = cache.get(key);

if (cached) {
if (now - cached.timestamp < ttl) {
return cached.value;
}

cache.delete(key);
}

const value = fn.apply(this, args);

if (cache.size >= maxSize) {
const oldestKey = cache.keys().next().value;
cache.delete(oldestKey);
}

cache.set(key, {
value,
timestamp: now,
});

return value;
}

// Step 5: Add cache control methods
// memoized.cache = {
// clear: () => cache.clear(),
// delete: (key) => cache.delete(key),
// has: (key) => cache.has(key),
// get size() { return cache.size; }
// };

// Step 6: Return memoized function

// Return placeholder that doesn't work
const memoized = function () {
return undefined;
};
memoized.cache = {
clear: () => {},
delete: () => false,
has: () => false,
clear: () => cache.clear(),
delete: key => cache.delete(key),
has: key => cache.has(key),
get size() {
return -1;
return cache.size;
},
};

return memoized;
}

Expand Down
Loading