Master JavaScript through hands-on coding challenges. Write real code, get instant feedback, and build practical web development skills.
Master advanced JavaScript concepts: closures for data privacy, higher-order functions for abstraction, and recursion for elegant solutions.
function outer() {
const message = "Hello from outer!";
function inner() {
// inner() has access to 'message' from outer()
console.log(message);
}
inner(); // "Hello from outer!"
}
outer();function createGreeter(greeting) {
// 'greeting' is captured in the closure
return function(name) {
return greeting + ", " + name + "!";
};
}
const sayHello = createGreeter("Hello");
const sayHi = createGreeter("Hi");
console.log(sayHello("Alice")); // "Hello, Alice!"
console.log(sayHi("Bob")); // "Hi, Bob!"function createCounter() {
let count = 0; // Private variable
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// count is not directly accessible!function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
const triple = multiplier(3);
const tenTimes = multiplier(10);
console.log(double(5)); // 10
console.log(triple(5)); // 15
console.log(tenTimes(5)); // 50function memoize(fn) {
const cache = {}; // Closure keeps cache private
return function(arg) {
if (cache[arg] !== undefined) {
console.log("From cache:", arg);
return cache[arg];
}
console.log("Computing:", arg);
const result = fn(arg);
cache[arg] = result;
return result;
};
}
const expensiveSquare = memoize(x => x * x);
console.log(expensiveSquare(5)); // Computing: 5 → 25
console.log(expensiveSquare(5)); // From cache: 5 → 25function delayedMessages() {
for (let i = 1; i <= 3; i++) {
// 'let' creates a new binding for each iteration
setTimeout(function() {
console.log("Message " + i);
}, i * 1000);
}
}
// Using 'let' (not 'var') ensures correct values
// Alternative with IIFE (Immediately Invoked Function Expression)
for (var i = 1; i <= 3; i++) {
(function(num) {
setTimeout(() => console.log("Num: " + num), num * 100);
})(i);
}let or IIFEvar in loops with closures (captures final value)Greeting: Hello, World!
Counter: 1, 2, 3
Double: 10, Triple: 15
Memoized result from cache
return function() { ... }function make(x) { return (y) => x + y; }let in loops with setTimeout to capture correct valuesReview feedback below
function doOperation(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
console.log(doOperation(5, 3, add)); // 8
console.log(doOperation(5, 3, multiply)); // 15
// With anonymous function
console.log(doOperation(10, 2, (x, y) => x / y)); // 5function createValidator(minLength) {
return function(str) {
return str.length >= minLength;
};
}
const isValidPassword = createValidator(8);
const isValidUsername = createValidator(3);
console.log(isValidPassword("abc")); // false
console.log(isValidPassword("secure123")); // true
console.log(isValidUsername("Jo")); // false
console.log(isValidUsername("John")); // truefunction myMap(array, transformFn) {
const result = [];
for (const item of array) {
result.push(transformFn(item));
}
return result;
}
const numbers = [1, 2, 3, 4, 5];
const doubled = myMap(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
const names = ["alice", "bob"];
const upper = myMap(names, s => s.toUpperCase());
console.log(upper); // ["ALICE", "BOB"]function myFilter(array, predicateFn) {
const result = [];
for (const item of array) {
if (predicateFn(item)) {
result.push(item);
}
}
return result;
}
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evens = myFilter(nums, n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
const words = ["apple", "banana", "cherry", "date"];
const longWords = myFilter(words, w => w.length > 5);
console.log(longWords); // ["banana", "cherry"]function myReduce(array, reducerFn, initialValue) {
let accumulator = initialValue;
for (const item of array) {
accumulator = reducerFn(accumulator, item);
}
return accumulator;
}
const numbers = [1, 2, 3, 4, 5];
const sum = myReduce(numbers, (acc, n) => acc + n, 0);
console.log("Sum:", sum); // 15
const product = myReduce(numbers, (acc, n) => acc * n, 1);
console.log("Product:", product); // 120
const max = myReduce(numbers, (acc, n) => n > acc ? n : acc, -Infinity);
console.log("Max:", max); // 5function compose(...fns) {
return function(x) {
return fns.reduceRight((acc, fn) => fn(acc), x);
};
}
// Or pipe (left to right)
function pipe(...fns) {
return function(x) {
return fns.reduce((acc, fn) => fn(acc), x);
};
}
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const composed = compose(square, double, addOne);
console.log(composed(3)); // square(double(addOne(3))) = square(8) = 64
const piped = pipe(addOne, double, square);
console.log(piped(3)); // square(double(addOne(3))) = 64map, filter, reduce, forEach, find, some, everyOperation result: 8
Validator: true/false
myMap: [2, 4, 6, 8, 10]
myFilter: [2, 4, 6, 8, 10]
myReduce sum: 15
function doX(callback) { callback(); }function make() { return () => {}; }transformFn(item) to resultpredicateFn(item) is trueacc = reducerFn(acc, item)fns.reduceRight((acc, fn) => fn(acc), x)Review feedback below
function factorial(n) {
// Base case: factorial of 0 or 1 is 1
if (n <= 1) {
return 1;
}
// Recursive case: n! = n * (n-1)!
return n * factorial(n - 1);
}
console.log(factorial(0)); // 1
console.log(factorial(1)); // 1
console.log(factorial(5)); // 120 (5 * 4 * 3 * 2 * 1)
console.log(factorial(10)); // 3628800function fibonacci(n) {
// Base cases
if (n <= 0) return 0;
if (n === 1) return 1;
// Recursive case: fib(n) = fib(n-1) + fib(n-2)
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(0)); // 0
console.log(fibonacci(1)); // 1
console.log(fibonacci(6)); // 8 (0,1,1,2,3,5,8)
console.log(fibonacci(10)); // 55
// More efficient with memoization
function fibMemo(n, memo = {}) {
if (n in memo) return memo[n];
if (n <= 1) return n;
memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
return memo[n];
}function sumArray(arr) {
// Base case: empty array
if (arr.length === 0) {
return 0;
}
// Recursive case: first element + sum of rest
return arr[0] + sumArray(arr.slice(1));
}
console.log(sumArray([1, 2, 3, 4, 5])); // 15
console.log(sumArray([10, 20, 30])); // 60
console.log(sumArray([])); // 0function reverseString(str) {
// Base case: empty or single char
if (str.length <= 1) {
return str;
}
// Recursive: last char + reverse of rest
return str[str.length - 1] + reverseString(str.slice(0, -1));
}
console.log(reverseString("hello")); // "olleh"
console.log(reverseString("JavaScript")); // "tpircSavaJ"
console.log(reverseString("a")); // "a"function flatten(arr) {
let result = [];
for (const item of arr) {
if (Array.isArray(item)) {
// Recursive case: flatten nested array
result = result.concat(flatten(item));
} else {
// Base case: add non-array item
result.push(item);
}
}
return result;
}
console.log(flatten([1, [2, 3], [4, [5, 6]]]));
// [1, 2, 3, 4, 5, 6]
console.log(flatten([[1, 2], [[3]], [4, [5, [6]]]]));
// [1, 2, 3, 4, 5, 6]function deepClone(obj) {
// Base case: primitives
if (obj === null || typeof obj !== "object") {
return obj;
}
// Handle arrays
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
// Handle objects
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const clone = deepClone(original);
clone.b.c = 99;
console.log(original.b.c); // 2 (unchanged!)factorial(5) = 120
fibonacci(10) = 55
sumArray([1,2,3,4,5]) = 15
reverseString("hello") = "olleh"
flatten result: [1, 2, 3, 4, 5, 6]
if (n <= 1) return 1;n * factorial(n - 1)fib(n-1) + fib(n-2)arr[0] + sumArray(arr.slice(1))lastChar + reverse(rest)Array.isArray(item)Review feedback below