Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
55 changes: 54 additions & 1 deletion 01-deep-clone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,82 @@ function deepClone(value, visited = new WeakMap()) {

// Step 1: Handle primitives (return as-is)
// Primitives: null, undefined, number, string, boolean, symbol, bigint
if (typeof value !== "object" || value === null) {
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) {
return new Date(value.getTime());
}

// Step 4: Handle RegExp objects
// Create a new RegExp with the same source and flags
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}

// Step 5: Handle Map objects
// Create a new Map and deep clone each key-value pair
if (value instanceof Map) {
const clone = new Map();
visited.set(value, clone);
for (const [k, v] of value) {
clone.set(deepClone(k, visited), deepClone(v, visited));
}
return clone;
}

// Step 6: Handle Set objects
// Create a new Set and deep clone each value
if (value instanceof Set) {
const clone = new Set();
visited.set(value, clone);
for (const item of value) {
clone.add(deepClone(item, visited));
}
return clone;
}

// Step 7: Handle Arrays
// Create a new array and deep clone each element
if (Array.isArray(value)) {
const clone = [];
visited.set(value, clone);
for (let i = 0; i < value.length; i++) {
clone[i] = deepClone(value[i], visited);
}
return clone;
}

// Step 8: Handle plain Objects
// Create a new object and deep clone each property
const clone = {};
visited.set(value, clone);
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clone[key] = deepClone(value[key], visited);
}
}

return undefined; // Broken: Replace with your implementation
const everyKeys = Object.getOwnPropertyNames(value);
for (const key of everyKeys) {
if (!(key in clone)) {
const desc = Object.getOwnPropertyDescriptor(value, key);
Object.defineProperty(clone, key, {
...desc,
value: deepClone(decs.value, visited),
});
}
}
return clone;
}

module.exports = { deepClone };
59 changes: 49 additions & 10 deletions 02-debounce-throttle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,31 @@ function debounce(fn, delay) {
// TODO: Implement debounce

// Step 1: Create a variable to store the timeout ID

let timeoutID = 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
const debounced = function (...args) {
const context = this;
if (timeoutID !== null) {
clearTimeout(timeoutID);
}

timeoutID = setTimeout(() => {
fn.apply(context, args);
timeoutID = null;
}, delay);
};
// Step 3: Add a cancel() method to clear pending timeout

debounced.cancel = function () {
if (timeoutID !== null) {
clearTimeout(timeoutID);
timeoutID = null;
}
};
// Step 4: Return the debounced function

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

/**
Expand All @@ -42,18 +55,44 @@ function throttle(fn, limit) {
// Step 1: Create variables to track:
// - Whether we're currently in a throttle period
// - The timeout ID for cleanup

let lastCall = 0;
let timeoutID = 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
const throttled = function (...args) {
const context = this;
const now = Date.now();

// Step 3: Add a cancel() method to reset throttle state
if (now - lastCall >= limit) {
fn.apply(context, args);
lastCall = now;

// Step 4: Return the throttled function
if (timeoutID !== null) {
clearTimeout(timeoutID);
timeoutID = null;
}
return;
}

// Return a placeholder that doesn't work
throw new Error("Not implemented");
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
fn.apply(context, args);
lastCall = Date.now();
timeoutID = null;
}, limit - (now - lastCall));
};
// Step 3: Add a cancel() method to reset throttle state
throttled.cancel = function () {
if (timeoutID !== null) {
clearTimeout(timeoutID);
timeoutID = null;
}
lastCall = 0;
};
// Step 4: Return the throttled function
return throttled;
}

module.exports = { debounce, throttle };
26 changes: 19 additions & 7 deletions 03-custom-bind/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ function customBind(fn, context, ...boundArgs) {

// Step 1: Validate that fn is a function
// Throw TypeError if not
if (typeof fn !== "function") {
throw new TypeError("fn is not a function");
}

const hasPrototype = fn.prototype !== undefined;
// 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
const boundFunction = function (...runtimeArgs) {
const args = [...boundArgs, ...runtimeArgs];
if (!hasPrototype) {
return fn(...args);
}
const thisContext = this instanceof boundFunction ? this : context;
return fn.apply(thisContext, args);
};

// Step 3: Handle constructor calls (when used with `new`)
// When called as a constructor:
Expand All @@ -29,11 +41,12 @@ function customBind(fn, context, ...boundArgs) {

// Step 4: Preserve the prototype for constructor usage
// boundFunction.prototype = Object.create(fn.prototype)

if (hasPrototype) {
boundFunction.prototype = Object.create(fn.prototype);
}
// Step 5: Return the bound function

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

/**
Expand All @@ -43,9 +56,8 @@ function customBind(fn, context, ...boundArgs) {
* myFunction.customBind(context, ...args)
*/

// 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 };
56 changes: 39 additions & 17 deletions 04-memoization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,64 @@ function memoize(fn, options = {}) {

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

const { maxSize = Infinity, keyGenerator = null, ttl = null } = 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(',')
const defaultKeyGen = (args) => {
return args.map((arg) => JSON.stringify(arg)).join("|||");
};

const getKey = keyGenerator || defaultKeyGen;
// 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)
const memoized = function (...args) {
const key = getKey(args);
if (cache.has(key)) {
const entry = cache.get(key);
if (ttl !== null && Date.now() - entry.timestamp > ttl) {
cache.delete(key);
} else {
cache.delete(key);
cache.set(key, entry);
return entry.value;
}
}
const value = fn.apply(this, args);

// 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; }
// };
if (cache.size >= maxSize) {
const firstKey = cache.keys().next().value;
if (firstKey !== undefined) {
cache.delete(firstKey);
}
}

// Step 6: Return memoized function
cache.set(key, {
value,
timestamp: Date.now(),
});

// Return placeholder that doesn't work
const memoized = function () {
return undefined;
return value;
};
// Step 5: Add cache control methods
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;
},
};

// Step 6: Return memoized function

// Return placeholder that doesn't work
return memoized;
}

Expand Down
Loading