Level 1 – Foundational Concepts (Warm-Up & Core JavaScript) Flashcards

Focus: closures, this, scope, custom method implementations (12 cards)

1
Q

Memoization

Description:
You’re given a function callback that performs expensive computations. Your task is to implement a memoization utility — a higher-order function called memoize that wraps around the callback and caches its results based on the arguments passed.
The memoized function should:
* Return cached results when called with the same arguments again.
* Work correctly for functions with any number of arguments.
* Ensure that no redundant computation occurs for already seen arguments.
💡 You can assume the callback is a pure function (same input => same output, no side effects).

Code:

// 🧱 Stubbed Implementation
function memoize(fn) {
  // TODO: Return a function that caches results of `fn`
  return function (...args) {
    // Stub: Always recompute
    return fn(...args);
  };
}

✅ Test Suite

(function enforceMemoizeConstraints() {
  const source = memoize.toString();
  const forbidden = ["eval", "Function", ".toString", "window", "globalThis"];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in memoize(): "${term}" is not allowed.`);
    }
  });
})();

function runMemoizeTests() {
  let computeCount = 0;
  const slowAdd = (a, b) => {
    computeCount++;
    return a + b;
  };

  const memoizedAdd = memoize(slowAdd);

  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  computeCount = 0;
  assert("First call computes", memoizedAdd(2, 3), 5);
  assert("Second call uses cache", memoizedAdd(2, 3), 5);
  assert("Third call with new args computes", memoizedAdd(4, 5), 9);
  assert("Fourth call reuses cache", memoizedAdd(2, 3), 5);
  assert("Total compute count should be 2", computeCount, 2);
}

runMemoizeTests();
A

✅ Version 1: JSON.stringify-based memoization

function memoize(fn) {
  let cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

✔ Pros:
* Simple to understand and implement.
* Works well with primitive arguments (number, string, etc.).

❌ Cons:
* JSON.stringify(args) can be inefficient for large or complex arguments.
* May produce collisions for different inputs with equivalent stringified forms.
* Functions, objects, and cyclic structures don’t serialize well or at all.
* Sensitive to argument order and shape ({a:1, b:2} vs {b:2, a:1}).

✅ Version 2: Nested Map memoization (trie-like)

function memoize(fn) {
  const cache = new Map();

  return function (...args) {
    let current = cache;

    for (let arg of args) {
      if (!current.has(arg)) {
        current.set(arg, new Map());
      }
      current = current.get(arg);
    }

    if (current.has("result")) {
      return current.get("result");
    }

    const result = fn(...args);
    current.set("result", result);
    return result;
  };
}

✔ Pros:
* Handles non-primitive arguments, including objects and functions (as long as they retain identity).
* No serialization, so it’s safer and more performant for complex inputs.
* Avoids JSON.stringify overhead and potential collisions.

❌ Cons:
* Slightly more complex implementation.
* Arguments must be strictly equal (===) to match—no deep equality.
* Can cause memory leaks if used carelessly (e.g. with many unique object keys).

🏁 Summary:
| Handles primitives well | ✅ | ✅ |
| Handles objects/functions | ❌ | ✅ |
| Collision risk | ⚠️ Yes (stringify collisions) | ❌ |
| Memory usage | ⚠️ Can be large (key strings) | ⚠️ Can grow deeply |
| Performance (general) | ⚠️ Slower for complex args | ✅ Fast, avoids stringify |
| Implementation complexity | ✅ Simple | ⚠️ More complex |

🧠 Best Practice:
* Use Version 2 when you need robust, identity-based memoization (like in React or performance-critical apps).
* Use Version 1 for simple, quick memoization where input types are controlled and predictable.

| ————————- | —————————– | ———————— |

Feature | Version 1 (JSON.stringify) | Version 2 (Nested Map) |

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Implement Once function

Description:
Implement a utility function once(fn, context) that returns a new function which ensures that the input function fn is executed only once, regardless of how many times the returned function is called.
* The first call should invoke fn with the correct this context and arguments.
* The result of the first call should be returned for all subsequent calls.
* After the first execution, fn must never be invoked again.
Use closures to store state and avoid external dependencies.

Code

// once.js

/**
 * Returns a function that only calls `fn` once.
 * @param {Function} fn
 * @returns {Function}
 */
function once(fn) {
  // TODO: Implement the logic to ensure `fn` runs only once
  return function (...args) {
    // placeholder return
    return fn(...args);
  };
}

✅ Test Suite

// test-once.js

function runOnceTests() {
  let callCount = 0;
  const addOnce = once((a, b) => {
    callCount++;
    return a + b;
  });

  const assert = (desc, actual, expected) => {
    const passed = actual === expected;
    console.log(`${passed ? "✅" : "❌"} ${desc}`);
    if (!passed) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  callCount = 0;
  assert("First call computes result", addOnce(3, 4), 7);
  assert("Second call returns cached result", addOnce(99, 99), 7);
  assert("Call count is 1", callCount, 1);

  const onceNoArgs = once(() => 42);
  assert("First call returns 42", onceNoArgs(), 42);
  assert("Second call returns cached 42", onceNoArgs(), 42);

  const onceVoid = once(() => console.log("Executed"));
  onceVoid(); // Should log
  onceVoid(); // Should not log
}

runOnceTests();
A
function once(fn) {
  let called = false;
  let result;

  return function (...args) {
    if (!called) {
      result = fn.apply(this, args);
      called = true;
    }
    return result;
  };
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Bind Polyfill

Description:
Implement your own version of the built-in Function.prototype.bind() method and call it myBind.
Your myBind method should:
* Return a new function with this explicitly bound to the given context.
* Allow arguments passed during binding as well as at the time of the function call (partial application).
* Ensure the original function is not executed during binding, only when the returned function is called.
You may assume ES5+ support (use of apply, arguments, etc.).
⚠️ Do NOT use the native .bind() method. Your code will be tested for this.

Code:

/**
 * Custom implementation of Function.prototype.bind
 * @param {*} context - The value to be passed as `this`
 * @param  {...any} args - Arguments to prepend when invoking the target function
 * @returns {Function}
 */
Function.prototype.myBind = function (context, ...args) {
  const originalFn = this;

  // TODO: Return a function that calls originalFn with the correct `this`
  // and prepends bound arguments before any new ones
  return function (...callArgs) {
    // TODO: Call originalFn with combined arguments and correct context
    return; // placeholder
  };
};

✅ Test Suite

// --- Test Suite ---
(function enforceMyBindConstraints() {
  const source = Function.prototype.myBind.toString();
  const forbidden = ["eval", "Function", ".toString", "bind(", "call(", "window", "globalThis"];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myBind(): "${term}" is not allowed.`);
    }
  });
})();

function runMyBindTests() {
  let callCount = 0;

  function say(greeting, name) {
    callCount++;
    return `${greeting}, ${name}!`;
  }

  const sayHelloTo = say.myBind(null, "Hello");

  const assert = (desc, actual, expected) => {
    const passed = actual === expected;
    console.log(`${passed ? "✅" : "❌"} ${desc}`);
    if (!passed) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  callCount = 0;
  assert("Pre-bound argument works", sayHelloTo("Alice"), "Hello, Alice!");
  assert("Call count increments", callCount, 1);

  const obj = {
    value: 42,
    getValue(prefix) {
      return `${prefix}: ${this.value}`;
    }
  };

  const bound = obj.getValue.myBind({ value: 100 }, "Result");
  assert("Bound this context and arguments work", bound(), "Result: 100");

  // Extra: bound with no args
  function add(a, b) {
    return a + b;
  }

  const add10 = add.myBind(null, 10);
  assert("Supports partial application", add10(5), 15);
}

runMyBindTests();
A
Function.prototype.myBind = function (context, ...args) {
  const originalFn = this;

  return function (...callArgs) {
    return originalFn.apply(context, [...args, ...callArgs]);
  };
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Generic Curry

Description:
Implement a function genericCurry(fn) that takes a function fn of arity n and returns a curried version of that function. The curried function should:
* Accept arguments one at a time or in groups
* When enough arguments have been provided (i.e., args.length >= fn.length), invoke the original function and return the result.
* Support chaining of partial applications.

Example:

function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = genericCurry(add);

curriedAdd(1)(2)(3);       // 6
curriedAdd(1, 2)(3);       // 6
curriedAdd(1)(2, 3);       // 6
curriedAdd(1, 2, 3);       // 6

Code:

// curry.js

function genericCurry(fn) {
  // TODO: Implement a currified version of the given fixed-arity function `fn`
  return function currified(...args) {
    // TODO: If enough arguments have been collected, call fn
    // Otherwise return a function that collects more
    return;
  };
}

✅ Test Suite

// enforce-genericCurry.js

(function enforceGenericCurryConstraints() {
  const source = genericCurry.toString();
  const forbidden = ["eval", "Function", ".toString", "window", "globalThis"];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in genericCurry(): "${term}" is not allowed.`);
    }
  });
})();

// test-genericCurry.js

function runGenericCurryTests() {
  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  function add(a, b, c) {
    return a + b + c;
  }

  const curriedAdd = genericCurry(add);

  assert("Separate calls", curriedAdd(1)(2)(3), 6);
  assert("First call multiple args", curriedAdd(1, 2)(3), 6);
  assert("Second call multiple args", curriedAdd(1)(2, 3), 6);
  assert("Single call with all args", curriedAdd(1, 2, 3), 6);

  let called = false;
  function logArgs(a, b) {
    called = true;
    return [a, b];
  }

  const curriedLog = genericCurry(logArgs);
  curriedLog("hello")("world");
  assert("Callback executed after enough args", called, true);
}

runGenericCurryTests();
A
function genericCurry(fn) {
  return function currified(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...nextArgs) {
        return currified.apply(this, [...args, ...nextArgs]);
      };
    }
  };
}

The above works, but it creates many intermediate function objects and can be less elegant.

🧠 “Wait — instead of manually returning a new function and tracking state, I can use .bind to partially apply arguments and reuse currified itself.”
~~~

function genericCurry(fn) {
return function currified(…args) {
// If enough arguments have been collected, call fn
if (args.length >= fn.length) { // extra args are trimmed
return fn.apply(this, args)
}
// Otherwise return a function that collects more
// Replaced manual recursion with bind
return currified.bind(this, …args);
};
}

✅ 1. Variadic Curry (accepts any number of arguments)
This version doesn't rely on fn.length and lets you keep calling until you explicitly invoke the function (e.g., via an empty call () or using .value()).

function variadicCurry(fn) {
function curried(…args) {
const collected = […args];

function collector(...next) {
  if (next.length === 0) {
    return fn(...collected);
  }
  collected.push(...next);
  return collector;
}

collector.valueOf = () => fn(...collected);
collector.toString = () => fn(...collected);
return collector;   }

return curried;
}

// Example
const sum = (…nums) => nums.reduce((a, b) => a + b, 0);
const curriedSum = variadicCurry(sum);

console.log(curriedSum(1)(2)(3, 4)()); // 10
console.log(curriedSum(5, 10, 15).valueOf()); // 30

🧠 This version is best when you want flexible, indefinite argument collection until a clear stopping point (like calling ()).

✅ 2. Fixed Arity-Aware Curry (with extra args safety)
This version respects fn.length but also handles:
* Functions with optional/default parameters.
* Calls with extra arguments.
* Safe over-collection.

function safeCurry(fn) {
return function curried(…args) {
if (args.length >= fn.length) {
return fn.apply(this, args.slice(0, fn.length)); // Trim extra args
}
return function (…next) {
return curried.apply(this, […args, …next]);
};
};
}

// Example
function multiply(a, b = 1, c = 1) {
return a * b * c;
}

const curriedMultiply = safeCurry(multiply);

console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2)(3)); // 6
console.log(curriedMultiply(2, 3, 4, 5)); // 24 (extra arg ignored)
~~~
🔐 This version makes sure you don’t accidentally pass more than fn.length args, and still works when defaults are used.

🧩 Summary:
Given: I need a currified function
Nothing yet implemented
Step 1 - Check if we have enough args
Added fn.length check
Step 2 - Need to collect more args if not enough
function to collect more args
Step 3 - There’s a cleaner way using bind
Replaced manual recursion with bindAdded
Step 4 - Simplify the control flow
Condensed logic with ternary operator

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Array.map Polyfill

Description:
Create a custom implementation of the built-in Array.prototype.map() method and name it myMap.
Your implementation should:
* Return a new array.
* Call the provided callback on each element of the array.
* Provide the element, index, and original array as arguments to the callback.
* Not mutate the original array.

🔒 Constraints
You must not use:
* Array.prototype.map
* Array.prototype.reduce
* Array.prototype.filter
* eval, Function, .toString()
* Global objects (window, globalThis)

💡 Examples:

const nums = [1, 2, 3];
const doubled = nums.myMap(n => n * 2);  // → [2, 4, 6]

const indexed = nums.myMap((n, i) => n + i);  // → [1, 3, 5]

const originalRef = [];
const result = [10, 20].myMap((n, i, arr) => {
  originalRef.push(arr);
  return n + 1;
});  // → [11, 21]

console.log(originalRef[0] === originalRef[1]);  // → true (same original array)

Code:

// myMap.js

Array.prototype.myMap = function(callback) {
  // TODO: Create a new array to hold the mapped values

  // TODO: Loop through the array and apply the callback

  // TODO: Return the new array
};

✅ Test Suite

// enforce-myMap.js
(function enforceMyMapConstraints() {
  const source = Array.prototype.myMap.toString();
  const forbidden = [
    ".map", ".reduce", ".filter", "eval", "Function",
    ".toString", "window", "globalThis"
  ];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myMap(): "${term}" is not allowed.`);
    }
  });
})();

// test-myMap.js
function runMyMapTests() {
  const assert = (desc, actual, expected) => {
    const pass = JSON.stringify(actual) === JSON.stringify(expected);
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${JSON.stringify(expected)}, but got: ${JSON.stringify(actual)}`);
    }
  };

  assert("Doubles elements", [1, 2, 3].myMap(x => x * 2), [2, 4, 6]);
  assert("Maps with index", [10, 20].myMap((x, i) => x + i), [10, 21]);
  assert("Empty array", [].myMap(x => x * 10), []);

  let capturedOriginal;
  [100, 200].myMap((val, i, arr) => {
    if (!capturedOriginal) capturedOriginal = arr;
    return val;
  });

  assert("Original array passed correctly", capturedOriginal, [100, 200]);
}

runMyMapTests();
A

Basic Version

Array.prototype.myMap = function(callback) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    result.push(callback(this[i], i, this));
  }
  return result;
};

Native Version

Array.prototype.myMap = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }

  const result = new Array(this.length);

  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      result[i] = callback.call(thisArg, this[i], i, this);
    }
  }

  return result;
};

Matches built-in behavior:
* Skips holes
* Supports thisArg
* Preallocates result array
* Throws error if callback is not a function

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Array.filter Polyfill

Description:
Your task is to implement a custom version of the built-in Array.prototype.filter() method called myFilter.
Your implementation should:
* Return a new array.
* Include only those elements for which the provided callback returns a truthy value.
* Pass the element, index, and original array as arguments to the callback.
* Not mutate the original array.

🔒 Constraints:
You must not use any of the following:
* .filter
* .map, .reduce, .forEach
* eval, Function, .toString
* Global objects like window or globalThis

💡 Examples

[1, 2, 3, 4].myFilter(x => x % 2 === 0);       // → [2, 4]

["apple", "", "banana"].myFilter(Boolean);    // → ["apple", "banana"]

[0, 1, 2].myFilter((val, i) => i < 2);         // → [0, 1]

Code:

// myFilter.js

Array.prototype.myFilter = function(callback) {
  // TODO: Create a new array to hold filtered results

  // TODO: Loop through each element in this array

  // TODO: If callback returns truthy, add the element to the result array

  // TODO: Return the result array
};

✅ Test Suite

// enforce-myFilter.js

(function enforceMyFilterConstraints() {
  const source = Array.prototype.myFilter.toString();
  const forbidden = [
    ".filter", ".map", ".reduce", ".forEach",
    "eval", "Function", ".toString",
    "window", "globalThis"
  ];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myFilter(): "${term}" is not allowed.`);
    }
  });
})();

// test-myFilter.js

function runMyFilterTests() {
  const assert = (desc, actual, expected) => {
    const pass = JSON.stringify(actual) === JSON.stringify(expected);
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${JSON.stringify(expected)}, but got: ${JSON.stringify(actual)}`);
    }
  };

  assert(
    "Filters even numbers",
    [1, 2, 3, 4].myFilter(x => x % 2 === 0),
    [2, 4]
  );

  assert(
    "Filters truthy strings",
    ["apple", "", "banana", null].myFilter(Boolean),
    ["apple", "banana"]
  );

  assert(
    "Filters based on index",
    [10, 20, 30, 40].myFilter((_, i) => i < 2),
    [10, 20]
  );

  assert(
    "Empty array returns empty array",
    [].myFilter(x => true),
    []
  );

  const input = [1, 2, 3];
  input.myFilter(() => true);
  assert("Does not mutate original array", input, [1, 2, 3]);
}

runMyFilterTests();
A

Basic Version:

Array.prototype.myFilter = function(callback) {
  const result = [];
  for (let index = 0; index < this.length; index++) {
    if (callback(this[index], index, this)) {
      result.push(this[index]);
    }
  }
  return result;
};

Native Version

Array.prototype.myFilter = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }

  const result = [];

  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      if (callback.call(thisArg, this[i], i, this)) {
        result.push(this[i]);
      }
    }
  }

  return result;
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Array.reduce Polyfill

Description:
Implement Array.prototype.myReduce
Create a custom implementation of the built-in Array.prototype.reduce() method called myReduce.
Your implementation should:
* Call a provided callback function for each element in the array, in order.
* Accumulate the result and return the final accumulated value.
* Accept an optional initialValue.
* If no initialValue is provided, use the first element of the array as the initial accumulator value, and start iterating from index 1.

🔒 Constraints:
You must not use the following:
* .reduce
* eval, Function, .toString
* Global objects like window or globalThis

💡 Examples:

[1, 2, 3, 4].myReduce((acc, val) => acc + val, 0);       // → 10

[5, 10, 20].myReduce((acc, val) => acc + val);           // → 35

["a", "b", "c"].myReduce((acc, val) => acc + val, "");   // → "abc"

[].myReduce((acc, val) => acc + val, 10);                // → 10

// [].myReduce((acc, val) => acc + val);                 // ❌ Should throw error

Code:

// myReduce.js

Array.prototype.myReduce = function(callback, initialValue) {
  // TODO: Initialize accumulator using initialValue (or first element if not provided)

  // TODO: Loop through the array and update the accumulator by calling the callback

  // TODO: Return the final accumulator value
};

✅ Test Suite

// enforce-myReduce.js

(function enforceMyReduceConstraints() {
  const source = Array.prototype.myReduce.toString();
  const forbidden = [
    ".reduce", "eval", "Function",
    ".toString", "window", "globalThis"
  ];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myReduce(): "${term}" is not allowed.`);
    }
  });
})();

// test-myReduce.js

function runMyReduceTests() {
  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  assert(
    "Sum with initial value",
    [1, 2, 3, 4].myReduce((acc, val) => acc + val, 0),
    10
  );

  assert(
    "Sum without initial value",
    [5, 10, 20].myReduce((acc, val) => acc + val),
    35
  );

  assert(
    "String concatenation",
    ["a", "b", "c"].myReduce((acc, val) => acc + val, ""),
    "abc"
  );

  assert(
    "Empty array with initial value",
    [].myReduce((acc, val) => acc + val, 100),
    100
  );

  try {
    [].myReduce((acc, val) => acc + val);
    console.log("❌ Should throw error on empty array without initial value");
  } catch (e) {
    console.log("✅ Throws error on empty array without initial value");
  }
}

runMyReduceTests();
A
Array.prototype.myReduce = function(callback, initialValue) {
  if (this.length === 0 && initialValue === undefined) {
    throw new TypeError("Reduce of empty array with no initial value");
  }

  let accumulator = initialValue;
  let startIndex = 0;

  if (accumulator === undefined) {
    accumulator = this[0];
    startIndex = 1;
  }

  for (let i = startIndex; i < this.length; i++) {
    accumulator = callback(accumulator, this[i], i, this);
  }

  return accumulator;
};

Spec-compliant implementation
~~~

Array.prototype.myReduce = function(callback, initialValue) {
if (typeof callback !== ‘function’) {
throw new TypeError(callback + ‘ is not a function’);
}

const arr = this;
const len = arr.length;

let accumulator;
let startIndex = 0;

if (arguments.length >= 2) {
accumulator = initialValue;
} else {
// Find the first defined (non-hole) value to use as initial accumulator
while (startIndex < len && !(startIndex in arr)) {
startIndex++;
}
if (startIndex >= len) {
throw new TypeError(‘Reduce of empty array with no initial value’);
}
accumulator = arr[startIndex++];
}

for (let i = startIndex; i < len; i++) {
if (i in arr) {
accumulator = callback(accumulator, arr[i], i, arr);
}
}

return accumulator;
};
~~~

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Infinite Currying Sum

Description:
Your task is to implement a function sumSpecial such that it supports infinite currying of numbers, and computes the total when the final empty call () is made.

Each function call should return another function that accepts a number, until a final call with no arguments ends the chain and returns the accumulated sum.

💡 Examples

sumSpecial(1)(2)(3)(4)();        // → 10
sumSpecial(10)(-5)(5)(100)();    // → 110
sumSpecial(0)();                 // → 0

🔒 Constraints:
Your implementation must NOT use any of the following:
* eval
* Function constructor
* .toString
* Global objects like window, globalThis, self
These are strictly forbidden and will be checked by enforcement code.

Code:

// sumSpecial.js

function sumSpecial(initialValue) {
  // TODO: Return a curried function that accumulates values
}

✅ Test Suite

// enforce-sumSpecial.js

(function enforceSumSpecialConstraints() {
  const source = sumSpecial.toString();
  const forbidden = [
    "eval",
    "Function",
    ".toString",
    "window",
    "globalThis",
    "self"
  ];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in sumSpecial(): "${term}" is not allowed.`);
    }
  });
})();

// test-sumSpecial.js

function runSumSpecialTests() {
  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  assert("sumSpecial(1)(2)(3)() returns 6", sumSpecial(1)(2)(3)(), 6);
  assert("sumSpecial(5)(5)(5)(5)() returns 20", sumSpecial(5)(5)(5)(5)(), 20);
  assert("sumSpecial(10)() returns 10", sumSpecial(10)(), 10);
  assert("sumSpecial(0)() returns 0", sumSpecial(0)(), 0);
  assert("sumSpecial(100)(-50)(50)() returns 100", sumSpecial(100)(-50)(50)(), 100);
}

runSumSpecialTests();
A
function sumSpecial(initialValue) {
  let total = initialValue;

  function inner(next) {
    if (typeof next === "undefined") {
      return total;
    }
    total += next;
    return inner;
  }

  return inner;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Implement Function Composition

Write a function compose that accepts any number of functions as arguments and returns a new function. When invoked, this returned function executes the provided functions from right to left, passing the return value of each function as the input to the previous one.

This is commonly used in functional programming to "compose" multiple operations into a single pipeline.

🔧 Constraints:
You must not use:
* Native Array.prototype.reduceRight
* eval, Function, or similar dynamic code execution
* Any global variables

📌 Example:

const add = x => x + 1;
const square = x => x * x;

const composed = compose(add, square); // (x) => add(square(x))

console.log(composed(2)); // 5 → (2^2 = 4, 4 + 1 = 5)

Code:

// 🚧 Stubbed Implementation
function compose(...fns) {
  // TODO: Compose functions from right to left
  return function(x) {
    // placeholder
    return x;
  };
}

✅ Test Suite

// 🚫 Enforcement Check - No use of eval, Function, reduceRight, global, etc.
(function enforce() {
  const source = compose.toString();
  const forbidden = ['eval', 'Function', 'reduceRight', 'window', 'globalThis', 'this.'];
  forbidden.forEach(term => {
    if (source.includes(term)) {
      throw new Error(`Forbidden usage detected: ${term}`);
    }
  });
})();

// ✅ Test Suite
function runTests() {
  const add = x => x + 1;
  const square = x => x * x;
  const double = x => x * 2;

  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, Got: ${actual}`);
    }
  };

  try {
    assert("compose with two functions", compose(add, square)(2), 5);       // square(2)=4, add(4)=5
    assert("compose with three functions", compose(add, square, double)(2), 17); // double(2)=4 → square=16 → add=17
    assert("compose with one function", compose(add)(3), 4);
    assert("compose with no functions", compose()(42), 42);
  } catch (err) {
    console.error("❌ Error during tests:", err.message);
  }
}

// Run the test suite
runTests();
A
function compose(...fns) {
    return function(initialValue) {
        let result = initialValue;
        for (let i = fns.length - 1; i >= 0; i--) {
            result = fns[i](result);
        }
        return result;
    };
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Implement Pipe Function for function composition

The pipe function takes any number of unary functions as arguments and returns a new function that takes a single input and applies the functions from left to right to produce a final result.
For example:

const add1 = x => x + 1;
const double = x => x * 2;
const result = pipe(add1, double)(2); // → double(add1(2)) → 6

➕ You may assume all input functions are unary (accept one argument).

🔒 Constraints:
- Do NOT use eval, Function, reduce, or global variables.
- Do NOT use .toString() or string manipulation of the function source.
Implement the stubbed pipe function and run the provided test suite.

Code:

// 🧱 Stubbed Implementation
function pipe(...fns) {
  // TODO: Implement left-to-right function composition
  return function(initialValue) {
    return initialValue; // placeholder
  };
}

✅ Test Suite

// 🔒 Enforcement - Prevent forbidden usage in user implementation
(function enforcePipeConstraints() {
  const forbiddenPatterns = ['eval', 'Function', 'reduce', 'toString', 'globalThis', 'window', 'this.'];
  const fnSrc = pipe.toString();
  forbiddenPatterns.forEach(term => {
    if (fnSrc.includes(term)) {
      throw new Error(`❌ Forbidden usage detected in pipe(): "${term}" is not allowed.`);
    }
  });
})();

// 🧪 Test Suite
function runPipeTests() {
  const add = x => x + 1;
  const square = x => x * x;
  const double = x => x * 2;

  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, but got: ${actual}`);
    }
  };

  try {
    assert("pipe with one function", pipe(add)(3), 4);
    assert("pipe with two functions", pipe(add, double)(2), 6); // (2 + 1) * 2 = 6
    assert("pipe with three functions", pipe(add, double, square)(2), 36); // (((2 + 1) * 2)^2)
    assert("pipe with no functions", pipe()(42), 42);
    assert("pipe with identity function", pipe(x => x)(10), 10);
  } catch (err) {
    console.error("❌ Error:", err.message);
  }
}

// ✅ Run Tests
runPipeTests();
A
function pipe(...fns) {
  return function(initialValue) {
    let result = initialValue;
    for (let fn of fns) {
      result = fn(result);
    }
    return result;
  };
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Implement Function.prototype.call

Description
Implement Function.prototype.myCall such that it invokes a function with a specified this value and individual arguments.
This mimics:
fn.call(thisArg, arg1, arg2, ...)

🔒 Constraints:
* Do not use Function.prototype.call, apply, or bind.
* Do not use eval, Function(), or .toString().
* You may assume that thisArg can be null, undefined, or a primitive.
* Use only ES5+ JavaScript (no proxies or symbols).

Code

Function.prototype.myCall = function(thisArg, ...args) {
  // TODO: your implementation
};

🧪 Test Suite

// 🔒 Enforcement: Disallow forbidden patterns
(function enforceMyCallConstraints() {
  const src = Function.prototype.myCall?.toString?.() || '';
  const forbidden = ["call", "apply", "bind", "eval", "Function", ".toString"];
  forbidden.forEach(term => {
    if (src.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myCall: "${term}"`);
    }
  });
})();

function runMyCallTests() {
  let passed = 0;

  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, Got: ${actual}`);
    } else {
      passed++;
    }
  };

  // Test 1 – Basic usage
  function greet(greeting, punctuation) {
    return `${greeting}, ${this.name}${punctuation}`;
  }
  const person = { name: "Alice" };
  assert(
    "Calls function with context and arguments",
    greet.myCall(person, "Hello", "!"),
    "Hello, Alice!"
  );

  // Test 2 – Null context (should default to global or undefined in strict mode)
  function testThis() {
    return typeof this;
  }
  assert(
    "Handles null thisArg",
    testThis.myCall(null),
    "object" // In non-strict mode, globalThis is used
  );

  // Test 3 – Primitive thisArg (should be boxed)
  function getType() {
    return Object.prototype.toString.call(this);
  }
  assert(
    "Boxes primitive thisArg",
    getType.myCall("hello"),
    "[object String]"
  );

  // Test 4 – Argument passing
  function add(a, b) {
    return a + b;
  }
  assert(
    "Passes multiple arguments",
    add.myCall(null, 3, 4),
    7
  );

  // Test 5 – Edge case: no arguments
  function identity() {
    return this.value || 42;
  }
  const fallback = { value: 100 };
  assert(
    "Works with no extra args",
    identity.myCall(fallback),
    100
  );
}

runMyCallTests();
A
Function.prototype.myCall = function(thisArg, ...args) {
  const fn = this;

  // Handle null/undefined by assigning to global object
  thisArg = thisArg == null ? globalThis : Object(thisArg);

  const tempFnKey = Symbol("tempFn");
  thisArg[tempFnKey] = fn;

  const result = thisArg[tempFnKey](...args);

  delete thisArg[tempFnKey];

  return result;
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Implement Function.prototype.apply

Description
Implement Function.prototype.myApply such that it invokes a function with a specified this value and arguments provided as an array or array-like object.
This mimics:
fn.apply(thisArg, [arg1, arg2, ...])

🔒 Constraints:
* Do not use Function.prototype.call, apply, or bind.
* Do not use eval, Function(), or .toString().
* Assume input will be valid (second parameter is always null or an array-like).
* Use only ES5+ JavaScript features.

Code

Function.prototype.myApply = function(thisArg, argsArray) {
  // TODO: your implementation
};

🧪 Test Suite

// 🔒 Enforcement: Disallow forbidden patterns
(function enforceMyApplyConstraints() {
  const src = Function.prototype.myApply?.toString?.() || '';
  const forbidden = ["call", "apply", "bind", "eval", "Function", ".toString"];
  forbidden.forEach(term => {
    if (src.includes(term)) {
      throw new Error(`❌ Forbidden pattern used in myApply: "${term}"`);
    }
  });
})();

function runMyApplyTests() {
  let passed = 0;

  const assert = (desc, actual, expected) => {
    const pass = actual === expected;
    console.log(`${pass ? "✅" : "❌"} ${desc}`);
    if (!pass) {
      console.log(`   Expected: ${expected}, Got: ${actual}`);
    } else {
      passed++;
    }
  };

  // Test 1 – Apply with arguments array
  function greet(greeting, punctuation) {
    return `${greeting}, ${this.name}${punctuation}`;
  }
  const person = { name: "Alice" };
  assert(
    "Applies function with context and args array",
    greet.myApply(person, ["Hello", "!"]),
    "Hello, Alice!"
  );

  // Test 2 – Apply with null context
  function typeOfThis() {
    return typeof this;
  }
  assert(
    "Handles null thisArg",
    typeOfThis.myApply(null),
    "object" // globalThis in non-strict mode
  );

  // Test 3 – Primitive thisArg (should be boxed)
  function getType() {
    return Object.prototype.toString.call(this);
  }
  assert(
    "Boxes primitive thisArg",
    getType.myApply("hello"),
    "[object String]"
  );

  // Test 4 – Math.max with apply
  assert(
    "Applies with native functions",
    Math.max.myApply(null, [3, 10, 7]),
    10
  );

  // Test 5 – No arguments array
  function identity() {
    return this.value || 42;
  }
  const obj = { value: 100 };
  assert(
    "Works with no args array",
    identity.myApply(obj),
    100
  );
}

runMyApplyTests();
A
Function.prototype.myApply = function(thisArg, argsArray) {
  const fn = this;
  thisArg = thisArg == null ? globalThis : Object(thisArg);

  const tempFnKey = Symbol("tempFn");
  thisArg[tempFnKey] = fn;

  const result = Array.isArray(argsArray)
    ? thisArg[tempFnKey](...argsArray)
    : thisArg[tempFnKey]();

  delete thisArg[tempFnKey];
  return result;
};
How well did you know this?
1
Not at all
2
3
4
5
Perfectly