JavaScript by Practice

JavaScript by Practice

Here are 30 JavaScript practice questions, progressing from basic to advanced, and aimed at real-life problem solving while covering core JavaScript concepts often asked in interviews:

Basic Level (1-10)

  1. Reverse a String
    Write a function to reverse a given string. (Hint: use string methods and loops).
// Inbuilt Functions
fucntion reverseString(s) {
    return s.split("").reverse().join("");
}
 
// Loop
function reverseString(s) {
    reversed = ""
    for (let i = s.length - 1; i >= 0; i--) {
        reversed += s[i]
    }
    return reversed
}
 
// loop with less iterations and inplace
function reverseString(s) {
    let start = 0;
    let end = s.length - 1;
 
    while (start < end) {
        [s[start], s[end]] = [s[end], s[start]];
        start ++;
        end --;
    }
}
  1. Palindrome Checker
    Create a function that checks if a string is a palindrome (reads the same forwards and backwards).
// loop
funciton isPalindrome(str) {
    for (let i = 0; i <= str.length / 2; i++) {
        if (str[i] != str[str.length - i - 1]) {
            return false;
        }
    }
    return true;
}
 
 
// Two Pointers
funciton isPalindrome(str) {
    let start = 0;
    let end = str.length - 1;
 
    while (start < end) {
        if (str[start] != str[end]) return false;
 
        start ++;
        end --;
    }
    return true
}
 
// Built-in Functions
function isPalindrome(str) {
    str = str.toLowerCase();
    return str === str.split("").reverse().join("");
}
  1. FizzBuzz Problem
    Write a function that prints numbers from 1 to 100. But for multiples of 3, print "Fizz" instead of the number, and for multiples of 5, print "Buzz". For numbers which are multiples of both 3 and 5, print "FizzBuzz".
// Loop
function FizzBuzz(n=100) {
    for (let i = 1; i <= n; i++) {
        if (i % 3 == 0 && i % 5 == 0) {
            console.log("FizzBuzz");
        }
        else if (i % 3 == 0) {
            console.log("Fizz");
        }
 
        else if (i % 5 == 0) {
            console.log("Buzz");
        }
        else {
            console.log(i);
        }
    }
}
 
// Ternary Operator
fucntion FizzBuzz(n=100) {
    for (let i = 1; i <= n; i++) {
        console.log(
            (i % 3 == 0 && i % 5 == 0) ? "FizzBuzz" :
            (i % 3 == 0) ? "Fizz":
            (i % 5 == 0) ? "Buzz":
            i
        );
    }
}
  1. Find Maximum in Array
    Write a function that takes an array of numbers and returns the largest number. Avoid using Math.max().
function findMax(arr) {
    let maxEle = arr[0];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] >= maxEle) {
            maxEle = arr[i];
        }
    }
    return maxEle;
}
 
function findMax(arr) {
    max = arr[0];
    for (const ele of arr) {
        if (ele > max) {
            max = ele;
        }
    }
    return max;
}
  1. Sum of Array
    Create a function that returns the sum of all elements in a given array.
// Loop
function sumArr(arr, n) {
    sum = 0;
    for (const ele of arr) {
        sum += ele;
    }
    return sum;
}
 
// Recursive
function sumArr(arr, n) {
    if (n === 0) return 0;
    return arr[n - 1] + sumArr(arr, n - 1);
}
  1. Remove Duplicates from Array
    Write a function that removes duplicates from an array of integers without using Set.
// Inplace
function removeDuplicates(arr) {
    hashmap = {};
    for (let i = 0; i < arr.length; i++) {
        if (hashmap[arr[i]]) {
            arr.splice(i, 1);
            i--;
        } else {
            hashmap[arr[i]] = true;
        }
    }
    return arr;
}
 
// Inbuilt Functions
function removeDuplicates(arr) {
    return [...new Set(arr)];
}
 
// Two Pointers
function removeDuplicates(arr) {
    let i = 0;
    for (let j = 1; j < arr.length; j++) {
        if (arr[j] != arr[i]) {
            arr[i + 1] = arr[j];
            i++;
        }
    }
    return arr.slice(0, i + 1);
}
  1. Factorial Calculation
    Write a function that calculates the factorial of a number recursively.
funciton factorial(n) {
    if (n === 0) return 1;
 
    return n * factorial(n - 1);
}
  1. Check if Number is Prime
    Write a function that checks whether a given number is prime or not.
// Loop
function checkPrime(n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
 
    if (n % 2 === 0 || n % 3 === 0) return false;
 
    for (let i = 5; i * i <= n; i += 6) {
        if (n % i === 0 || n % (i + 2) === 0) return false;
    }
    return true;
}
 
// Recursive
function checkPrime(n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
 
    if (n % 2 === 0 || n % 3 === 0) return false;
 
    return checkPrime(n, i + 1);
}
  1. Anagram Checker
    Create a function that checks if two given strings are anagrams (i.e., same letters but in different order).
// Loop
function checkAnagram(s1, s2) {
    if (s1.length != s2.length) return false;
 
    hashmap = {};
    for (const char of s1) {
        hashmap[char] = (hashmap[char] || 0) + 1;
    }
 
    for (const char of s2) {
        if (!hashmap[char]) return false;
        hashmap[char]--;
    }
    return true;
}
 
// Sorting and Built-in Functions
function checkAnagram(s1, s2) {
    s1 = s1.split("").sort().join("");
    s2 = s2.split("").sort().join("");
    return s1 === s2;
}
  1. Flatten a Multidimensional Array
    Write a function that flattens a nested array into a single array (e.g., [[1, 2], [3, 4]][1, 2, 3, 4]).
// loop
function nested_to_single_arr(arr) {
    res = [];
    for (const subarr of arr) {
        for (let i = 0; i < subarr.length; i++) {
            res.push(subarr[i]);
        }
    }
    return res;
}

Intermediate Level (11-20)

  1. Debounce Function Implementation
    Implement a debounce function that delays the processing of a function until after a specified time has elapsed since it was last called.
function debounce(main_funciton, delay) {
    let timer;
 
    return function (...args) {
        clearTimeout(timer);
        timer = setTimeout(() => {
            main_funciton(...args);
        }, delay);
    };
}
 
function main_func() {
    console.log("Function is executed, but at what cost!");
}
 
debounce(main_func, 5000)();
  1. Throttling Function Write a function that limits the number of times a function can be executed over time (throttling).
function throttle(func, delay) {
    let lastCall = 0;
    return function (...args) {
        const now = new Date().getTime();
        if (now - lastCall >= delay) {
            lastCall = now;
            func.apply(this, args);
        }
    };
}
 
// Example usage:
const log = throttle(() => console.log("Throttled!"), 1000);
window.addEventListener("scroll", log);
  1. Deep Clone an Object Write a function that performs a deep clone of a given object (i.e., copies all nested objects/arrays).
// Brute Force Solution (Using Recursion)
function deepClone(obj) {
    if (obj === null || typeof obj !== "object") return obj;
    let clone = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key]);
        }
    }
    return clone;
}
console.log(deepClone({ a: 1, b: { c: 2 } })); // Output: { a: 1, b: { c: 2 } }
 
// Using JSON.parse and JSON.stringify
function deepCloneOptimized(obj) {
    return JSON.parse(JSON.stringify(obj));
}
console.log(deepCloneOptimized({ a: 1, b: { c: 2 } })); // Output: { a: 1, b: { c: 2 } }
  1. Sum of Nested Array Write a function that sums all numbers in a deeply nested array (e.g., [1, [2, [3, 4]]]10).
function sumNestedArray(arr) {
    return arr.reduce(
        (acc, curr) =>
            acc + (Array.isArray(curr) ? sumNestedArray(curr) : curr),
        0
    );
}
console.log(sumNestedArray([1, [2, [3, 4]], 5])); // Output: 15
  1. Currying Function Implement a curried function that accepts one argument at a time, until all arguments are provided, and then returns the result.
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function (...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
}
 
// Example usage:
function add(a, b, c) {
    return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // Output: 6
  1. Event Delegation Example Write a code snippet that implements event delegation for handling clicks on dynamically created elements in a list.
document.querySelector("#parent").addEventListener("click", function (event) {
    if (event.target && event.target.nodeName == "BUTTON") {
        console.log("Button clicked:", event.target.innerText);
    }
});
 
// HTML Example:
// <div id="parent">
//   <button>Button 1</button>
//   <button>Button 2</button>
// </div>
  1. Two Sum Problem Write a function that, given an array of numbers and a target number, finds two numbers in the array that sum to the target.
// Brute Force
function twoSum(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] + arr[j] === target) {
                return [i, j];
            }
        }
    }
    return [];
}
console.log(twoSum([2, 7, 11, 15], 9)); // Output: [0, 1]
 
// Using hash map
function twoSumOptimized(arr, target) {
    const map = {};
    for (let i = 0; i < arr.length; i++) {
        const complement = target - arr[i];
        if (map[complement] !== undefined) {
            return [map[complement], i];
        }
        map[arr[i]] = i;
    }
    return [];
}
console.log(twoSumOptimized([2, 7, 11, 15], 9)); // Output: [0, 1]
  1. Binary Search Algorithm Write a binary search function that searches for a value in a sorted array.
// Iterative
function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) return mid;
        if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
console.log(binarySearch([1, 2, 3, 4, 5, 6, 7], 4)); // Output: 3
 
// Recursive
function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) return mid;
        if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
console.log(binarySearch([1, 2, 3, 4, 5, 6, 7], 4)); // Output: 3
  1. Promise.all Polyfill Implement a custom version of Promise.all that accepts an array of promises and returns a promise that resolves when all promises resolve, or rejects if any promise rejects.

  2. Merge Two Sorted Arrays Write a function that merges two sorted arrays into a single sorted array without using array methods like sort().

// Brute Force
function mergeSortedArrays(arr1, arr2) {
    return arr1.concat(arr2).sort((a, b) => a - b);
}
console.log(mergeSortedArrays([1, 3, 5], [2, 4, 6])); // Output: [1, 2, 3, 4, 5, 6]
 
// Two Pointers
function mergeSortedArraysOptimized(arr1, arr2) {
    let merged = [];
    let i = 0,
        j = 0;
 
    while (i < arr1.length && j < arr2.length) {
        if (arr1[i] < arr2[j]) {
            merged.push(arr1[i]);
            i++;
        } else {
            merged.push(arr2[j]);
            j++;
        }
    }
 
    while (i < arr1.length) merged.push(arr1[i++]);
    while (j < arr2.length) merged.push(arr2[j++]);
 
    return merged;
}
console.log(mergeSortedArraysOptimized([1, 3, 5], [2, 4, 6])); // Output: [1, 2, 3, 4, 5, 6]

Advanced Level (21-30)

  1. LRU Cache Implementation Implement an LRU (Least Recently Used) cache using JavaScript objects or classes.

  2. Memoization Create a function that memoizes the results of another function, storing results for previously computed inputs.

  3. Generate Permutations Write a function to generate all possible permutations of an array of numbers.

  4. Debouncing in Real-time Search Input Implement a debounce function to optimize a search input field in a web application (e.g., triggering search only after the user has stopped typing).

  5. Custom Event Emitter Write a simple event emitter class that allows registering event listeners and emitting events.

  6. Implement call, apply, and bind Methods Create your own versions of JavaScript’s call, apply, and bind methods.

  7. Function Composition Write a function that composes multiple functions into one, where the result of each function is passed to the next.

  8. Asynchronous JavaScript Write a function that fetches data from an API using fetch, handles errors, and returns the result as a resolved promise.

  9. Implement a Queue using Two Stacks Use two stacks to implement a queue. Provide enqueue and dequeue operations.

  10. Scheduler using setTimeout Implement a function that schedules tasks to run at specific intervals using setTimeout and allows for cancellation.

Additional Topics Covered:

These questions will give you a comprehensive preparation for JavaScript interviews by covering basic, intermediate, and advanced concepts while focusing on solving real-life coding problems.

Here are some practice questions for the additional JavaScript topics you mentioned, along with code for each (including brute force, optimized solutions, or relevant variations where applicable).


1. Closures

Question 1:

Create a function that stores a counter and allows you to increment or retrieve its value using closures.

Solution (Closures)

function createCounter() {
    let count = 0;
    return {
        increment: function () {
            count++;
        },
        getValue: function () {
            return count;
        },
    };
}
 
const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getValue()); // Output: 2

2. Hoisting

Question 2:

Demonstrate the behavior of function and variable hoisting in JavaScript.

Solution (Hoisting Example)

console.log(x); // Output: undefined (variable hoisting)
var x = 5;
 
hoistedFunction(); // Output: "This function is hoisted!"
 
function hoistedFunction() {
    console.log("This function is hoisted!");
}

Explanation:


3. Prototypes and Inheritance

Question 3:

Create a constructor function for a Person with properties name and age, and then use inheritance to create a Student that inherits from Person.

Solution (Prototypal Inheritance)

function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.greet = function () {
    console.log(`Hello, my name is ${this.name}.`);
};
 
function Student(name, age, major) {
    Person.call(this, name, age); // Inherit properties from Person
    this.major = major;
}
 
Student.prototype = Object.create(Person.prototype); // Inherit methods from Person
Student.prototype.constructor = Student;
 
Student.prototype.study = function () {
    console.log(`${this.name} is studying ${this.major}.`);
};
 
const student = new Student("Alice", 21, "Computer Science");
student.greet(); // Output: "Hello, my name is Alice."
student.study(); // Output: "Alice is studying Computer Science."

4. Asynchronous JavaScript (Promises, async/await)

Question 4:

Write a function that fetches user data from a fake API using both Promises and async/await.

Solution (Using Promises)

function fetchUserData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ name: "John", age: 30 });
        }, 1000);
    });
}
 
fetchUserData()
    .then((data) => console.log(data)) // Output: { name: "John", age: 30 }
    .catch((err) => console.error(err));

Solution (Using async/await)

async function fetchUserDataAsync() {
    try {
        const data = await fetchUserData();
        console.log(data); // Output: { name: "John", age: 30 }
    } catch (err) {
        console.error(err);
    }
}
 
fetchUserDataAsync();

5. JavaScript Design Patterns (Singleton, Factory)

Question 5:

Implement the Singleton design pattern in JavaScript to ensure only one instance of a database connection exists.

Solution (Singleton Pattern)

const Database = (function () {
    let instance;
 
    function createInstance() {
        return { connection: "Database connection established" };
    }
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        },
    };
})();
 
const db1 = Database.getInstance();
const db2 = Database.getInstance();
 
console.log(db1 === db2); // Output: true

Question 6:

Create a Factory function that returns different types of employees based on a role.

Solution (Factory Pattern)

function EmployeeFactory() {}
 
EmployeeFactory.prototype.createEmployee = function (role) {
    switch (role) {
        case "developer":
            return { role: "developer", code: () => console.log("Coding...") };
        case "designer":
            return {
                role: "designer",
                design: () => console.log("Designing..."),
            };
        default:
            return null;
    }
};
 
const factory = new EmployeeFactory();
const dev = factory.createEmployee("developer");
const designer = factory.createEmployee("designer");
 
dev.code(); // Output: "Coding..."
designer.design(); // Output: "Designing..."

6. ES6+ Features (Destructuring, Arrow Functions, Template Literals)

Question 7:

Use destructuring to extract values from an object and array. Implement a function using arrow functions and template literals.

Solution (Destructuring, Arrow Functions, Template Literals)

const person = { name: "Alice", age: 25 };
const { name, age } = person;
 
const numbers = [1, 2, 3];
const [first, second] = numbers;
 
const greet = (name, age) =>
    `Hello, my name is ${name} and I am ${age} years old.`;
 
console.log(greet(name, age)); // Output: "Hello, my name is Alice and I am 25 years old."

7. Error Handling

Question 8:

Write a function that throws an error if a number is negative and use try/catch to handle the error.

Solution (Error Handling with try/catch)

function checkNumber(num) {
    if (num < 0) {
        throw new Error("Negative number not allowed");
    }
    return "Number is valid";
}
 
try {
    console.log(checkNumber(-5)); // This will throw an error
} catch (err) {
    console.error(err.message); // Output: "Negative number not allowed"
}

Question 9:

Create a function that retries an async operation up to 3 times if it fails.

Solution (Retry with Promises and Error Handling)

async function retryOperation(operation, retries) {
    for (let i = 0; i < retries; i++) {
        try {
            return await operation();
        } catch (err) {
            if (i === retries - 1)
                throw new Error("Operation failed after retries");
        }
    }
}
 
async function sampleOperation() {
    // Simulating a failed async operation
    throw new Error("Operation failed");
}
 
retryOperation(sampleOperation, 3)
    .then(() => console.log("Operation succeeded"))
    .catch((err) => console.error(err.message)); // Output: "Operation failed after retries"

These questions and solutions cover closures, hoisting, prototypes, inheritance, asynchronous JavaScript, design patterns, ES6+ features, and error handling. Each solution includes different approaches where applicable, to help prepare for JavaScript interviews.

MIT © Archit Rathod.