Skip to main content

9 posts tagged with "nishant-mendiratta"

View All Tags

Β· 12 min read
Nishant Mendiratta

Machine coding rounds are where theory meets reality. You are not asked to explain hooks in abstract terms β€” you are asked to build a working UI in 60–90 minutes, make reasonable trade-offs under pressure, and communicate your thinking out loud.

This post is a curated guide based on a personal collection of React challenges I have practiced over the years - from Todo list starter problems to company-style components like transfer lists, autocomplete, shopping carts, and async UI testing. Use it as a roadmap: understand the problem statement, know what signals interviewers look for, and practice until the patterns feel natural.


What is a React machine coding round?​

In most frontend interviews (especially at product companies and startups), a machine coding round evaluates:

  1. Can you translate requirements into component structure?
  2. Can you manage state correctly without over-engineering?
  3. Do you write readable, maintainable React β€” not just "it works"?
  4. Do you handle edge cases (empty states, disabled buttons, invalid moves, loading/error)?
  5. Can you test or at least design for testability?

Unlike LeetCode, there is rarely one optimal algorithm. Interviewers care about product thinking + React fundamentals + execution speed.

What strong candidates do differently
  • Clarify requirements in the first 5 minutes (single vs multi-select, sorting rules, API shape).
  • Start with a working MVP, then iterate.
  • Keep state minimal and colocated until duplication forces extraction.
  • Use semantic HTML and stable keys.
  • Name components and handlers clearly (handleAddEntry, not doStuff).

The skill ladder (how to use this list)​

LevelFocusTypical time
Warm-upuseState, events, rendering lists15–25 min
Core UIForms, controlled inputs, sorting/filtering30–45 min
State designContext, reducer, lifting state45–60 min
Product componentsTransfer list, autocomplete, cart logic60–90 min
Quality barAsync fetch, error/loading, RTL/Jest tests60–90 min

Practice in this order. If you cannot build a counter confidently, a shopping cart will feel overwhelming.


Challenge catalog​

Below is a challenge-by-challenge breakdown: problem statement, what interviewers expect, and concepts tested.


1. Simple Counter​

Problem statement: Build a counter with an increment button. Display the current count and update it on click.

What interviewers expect:

  • Correct useState usage with functional updates when needed: setCount(prev => prev + 1).
  • Stable element ids/classes if tests rely on them (#mainButton, count span).
  • No stale closure bugs when logic grows (e.g., adding decrement/reset later).

Signals tested: Basic React rendering, event handling, state updates.

Stretch goals: Decrement, reset, min/max bounds, keyboard accessibility.


2. Button Toggle (ON/OFF)​

Problem statement: Render a button that toggles label/state between ON and OFF on each click.

What interviewers expect:

  • Boolean state (useState(false)).
  • Derived UI from state (label reflects state, not duplicated state).
  • Clean handler: setToggle(prev => !prev).

Signals tested: Minimal state, controlled UI, readability.


3. Static List Rendering​

Problem statement: Given an array like [{ name, age }, ...], render a list of items.

What interviewers expect:

  • map() with a stable key (prefer id; index only if static).
  • Small presentational components (DataItem, DataList).
  • Separation of data and view.

Signals tested: Lists/keys, component composition.


4. Context API - Language Toggle​

Problem statement: Provide shared language state via Context. Display current language and a button to cycle/toggle between options (e.g., JavaScript ↔ Python).

What interviewers expect:

  • createContext + Provider wrapping subtree.
  • Consumer reads context with useContext.
  • Avoid prop drilling; keep provider value memoized if object identity matters.

Signals tested: Context pattern, shared state across components.

Common mistakes: Creating context value inline every render causing unnecessary re-renders; putting everything in context too early.


5. Phone Book (Form + Table + Sort)​

Problem statement: Build a form to add users (first name, last name, phone). On submit, append to a table sorted by last name.

What interviewers expect:

  • Controlled inputs (value + onChange).
  • Form submit handling (preventDefault).
  • Immutable state updates (setEntries(prev => [...prev, newEntry])).
  • Sorting logic (case-insensitive last name compare).
  • Reset form after successful submit.

Signals tested: Forms, derived sorted data, useReducer (optional but strong signal).

Bonus: Validation (required fields, phone format), duplicate detection, edit/delete.


6. Todo List (Basic)​

Problem statement: Render todos with checkbox to mark complete/incomplete. Toggle completion state on click.

What interviewers expect:

  • Immutable updates in reducer or state setter.
  • Checkbox checked bound to todo state.
  • Clear visual distinction for completed items.

Signals tested: useReducer, list updates, controlled inputs.


7. Todo App with Context + Reducer​

Problem statement: Extend todos with add + mark-complete actions shared across form/list components using Context.

What interviewers expect:

  • Reducer actions (ADD, COMPLETE) with pure state transitions.
  • Context exposes actions, not raw dispatch, when possible (addItem, markAsCompleted).
  • Provider wraps app subtree; consumers stay thin.

Signals tested: Scalable state architecture without Redux.


8. Toggle State with Context + Reducer​

Problem statement: Manage toggleable app state (e.g., language/theme) using useReducer and Context together.

What interviewers expect:

  • Reducer stays pure; side effects stay outside.
  • Context API design: { state, toggleLanguage } instead of leaking implementation details.
  • Predictable action types (TOGGLE).

Signals tested: Intermediate state management patterns commonly used in real codebases.


9. Tic Tac Toe​

Problem statement: Build a 3Γ—3 grid. Two players alternate (X/O). Detect win/draw, prevent invalid moves, optionally support reset.

What interviewers expect:

  • Single source of truth for board state (array or map keyed by cell).
  • Win detection for rows/columns/diagonals.
  • Disable cells after game ends or if already filled.
  • Turn indicator and winner display.

Signals tested: Derived state, effects (useEffect) used carefully, edge-case handling.

Stretch: NΓ—N board generalization (see extended grid challenge).

Interview tip: Start with board representation before UI. Most bugs come from duplicated state (currentPlayer vs board-derived turn).


10. Star Rating Component​

Problem statement: Render 5 stars. Hover highlights up to hovered star; click sets rating; show current rating.

What interviewers expect:

  • Hover vs selected state separated (hoverIndex vs rating).
  • Mouse enter/leave handlers that restore selected rating on leave.
  • Accessible controls (radio group pattern or buttons with aria-label).

Signals tested: UI interaction state, event handling, component decomposition (Star child).


11. Directory Tree (Recursive UI)​

Problem statement: Given nested folder/file data, render expandable tree. Click folder to expand/collapse children.

What interviewers expect:

  • Recursive rendering (RenderTree, ExpandedTree).
  • Local expand/collapse state per folder node.
  • Correct handling of children: null vs empty array.

Signals tested: Recursion in React, conditional rendering, tree data modeling.


12. Transfer List (High-frequency interview question)​

Problem statement: Build two lists with controls to move items left/right. Support selecting one or many items via checkbox and transfer selected items between lists.

What interviewers expect:

  • Separate list state (leftItems, rightItems).
  • Selection state (checkbox per item or Set of selected ids).
  • Disabled action buttons when nothing selected / list empty.
  • Preserve order or define sort behavior explicitly.

Signals tested: Complex state coordination, UX details, immutable array operations.

What great candidates mention aloud:

  • Should moved items keep selection?
  • Should we support reorder within a list?
  • Should "move all" exist?

This challenge appears frequently because it mirrors real admin UIs (permissions, bucket assignment, playlist management).


13. Transfer List - Advanced Variant​

Problem statement (extended): Add reorder (Move Up / Move Down) and multi-select transfer between two lists.

What interviewers expect:

  • Set-based selection for multi-select.
  • Reorder only selected contiguous groups or single items - clarify behavior.
  • Button disabled logic when selection is at boundary (top/bottom).
  • No mutation of state arrays in place.

Signals tested: Higher complexity state transitions, the kind asked at mid-senior frontend loops.


14. Autocomplete - Offline​

Problem statement: Input filters a local suggestions list as user types (countries, tags, etc.).

What interviewers expect:

  • Controlled input.
  • Case-insensitive filtering (includes / startsWith / regex - justify choice).
  • Debounced filtering for performance on large lists.
  • Empty state when no matches.

Signals tested: Controlled components, debouncing, list filtering.


15. Autocomplete - Online (API-backed)​

Problem statement: Fetch suggestions from API (e.g., GitHub users) based on search term.

What interviewers expect:

  • Debounced API calls (avoid request storm on every keystroke).
  • Loading and error states.
  • Cancel stale requests or ignore outdated responses (AbortController or mounted flag).
  • Render API response shape safely (items?.map).

Signals tested: Async React patterns, network UX, race condition awareness.


16. Dynamic Forms (Add More Fields)​

Problem statement: Render one or more form blocks. Clicking "Add More" appends another form dynamically.

What interviewers expect:

  • Dynamic list of form entities in state (forms: [{ id }]).
  • Stable keys per form instance.
  • Context/reducer or state lift to add/remove forms.
  • Each form maintains its own local input state.

Signals tested: Dynamic UI generation, context/reducer design, component boundaries.

Common pitfall: Reusing index as key after insert/delete causes input state to jump between forms.


17. Shopping Cart + Coupon Strategies​

Problem statement: Add products to cart, apply coupons (percentage/fixed), compute net price.

What interviewers expect:

  • Extensible discount logic (strategy pattern or pure functions).
  • Derived totals (calculateTotalPrice, calculateNetPrice) instead of storing redundant values.
  • Context or reducer for cart state.
  • Clear separation: product model vs discount rules vs UI.

Signals tested: Domain modeling in UI, design patterns, derived state.

What interviewers love hearing: "Total is derived; we don't store net price as source of truth."


18. Redux Toolkit Counter (Optional round variant)​

Problem statement: Implement counter (or small feature) using Redux Toolkit slice.

What interviewers expect:

  • Slice with reducers/actions.
  • Store provider at app root.
  • Component dispatches actions, selects state via hooks.

Signals tested: Global state familiarity - more common in enterprise teams than startups.

Use this if the JD mentions Redux/Zustand; otherwise Context + reducer is usually enough.


19. Async Posts List + Tests (RTL/Jest)​

Problem statement: Fetch posts on mount, show loading, render list, support filter action, handle API failure gracefully.

What interviewers expect:

  • useEffect with cleanup (mounted flag or abort).
  • Loading/error/success UI branches.
  • Tests with React Testing Library:
    • loading state visible initially
    • list renders after fetch resolves
    • filter reduces items
    • rejected promise shows error UI
  • Mock fetch in tests (jest.fn, mockRejectedValueOnce).

Signals tested: Async UI lifecycle, testability, production-minded error handling.

This is increasingly common: "make it work" + "prove it works."


Cross-cutting expectations in every round​

No matter which challenge you get, interviewers usually score these:

Component design​

  • Smart vs presentational split when complexity grows.
  • Avoid "god components" beyond ~150 lines.
  • Reusable primitives (Button, List, Cell) when repeated.

State management choices​

  • Local state first.
  • Lift state when siblings need it.
  • Context/reducer when prop drilling hurts.
  • Redux only when requirements explicitly need global/shared cross-route state.

React correctness​

  • Never mutate state directly.
  • Keys on lists.
  • Effects with correct dependency arrays.
  • Avoid unnecessary effects (derive values in render when possible).

UX details that impress​

  • Disabled buttons when action invalid.
  • Empty states ("No items in List 2").
  • Keyboard support for interactive widgets.
  • Clear feedback on win/loss/error/loading.

Communication​

  • Think aloud: "I'll represent board as a map keyed by cell id."
  • Call out trade-offs: "Debouncing at 300ms balances responsiveness and API cost."
  • State assumptions explicitly when requirements are ambiguous.

A practical 90-minute game plan​

Use this template in real interviews:

  1. 0–10 min: Clarify requirements + write acceptance checks.
  2. 10–35 min: Build MVP (render + core interaction).
  3. 35–60 min: Edge cases + polish (disabled states, empty/error).
  4. 60–75 min: Refactor names/structure, extract components.
  5. 75–90 min: Tests or self-review checklist.

Acceptance checklist example (Transfer List):

  • Can select multiple items
  • Can move selected right/left
  • Selection clears after move (if required)
  • Buttons disabled appropriately
  • No item duplication/loss

If you are early in your frontend journey, practice in this sequence:

  1. Counter β†’ Toggle β†’ List
  2. Phone Book β†’ Todo (basic)
  3. Context Toggle β†’ Todo with Context/Reducer
  4. Star Rating β†’ Directory Tree
  5. Tic Tac Toe
  6. Autocomplete (offline β†’ online)
  7. Transfer List (basic β†’ advanced reorder variant)
  8. Dynamic Forms
  9. Shopping Cart
  10. Async list + RTL tests

Each step adds one new concept without jumping complexity too fast.


Final advice for young engineers​

Machine coding rounds can feel intimidating, but they are also the most fair part of many interview loops: your screen shows what you can actually build.

You do not need fancy abstractions. You need:

  • calm requirement clarification,
  • steady MVP delivery,
  • correct React state habits,
  • and clear communication.

Pick one challenge from this list, timebox 60 minutes, build it, then rewrite it cleaner the next day. Repeat weekly - that compounding practice is what turns anxiety into confidence.


References & further practice​

If you want hands-on walkthroughs of these challenges inside JavaScript360, check the React Challenges section.

Happy building - and remember: interviewers are not looking for perfect code. They are looking for clear thinking, working software, and good React instincts under time pressure.

Β· 5 min read
Nishant Mendiratta

Here's a brief overview of important JavaScript concepts, along with sample code snippets for each:

1. Hoisting​

Hoisting refers to JavaScript's behavior of moving declarations (variables and functions) to the top of their scope before code execution.

console.log(a);  // undefined due to hoisting
var a = 5;

2. Single Threaded​

JavaScript runs in a single thread, meaning it can only execute one task at a time.

3. Synchronous​

Synchronous code is executed line by line, with each operation blocking the next one until it completes.

console.log("First");
console.log("Second");

4. Function Invocation​

This is the process of calling or executing a function.

function greet() {
console.log("Hello");
}
greet(); // Function invocation

5. Variable Environment​

The context in which variables are stored and accessed, typically inside a scope (global, function, or block).

6. Scope​

Scope determines the visibility and lifetime of variables and functions. In JavaScript, there are function scope, block scope, and global scope.

if (true) {
let x = 5; // Block scope
}
console.log(x); // Error: x is not defined

7. Asynchronous​

Asynchronous code allows other operations to continue while waiting for a task to complete.

setTimeout(() => {
console.log("Asynchronous");
}, 1000);

8. How This Happens Inside JS Engine?​

JavaScript's event loop handles asynchronous operations. The call stack executes synchronous code, while the event loop waits for tasks in the callback queue.

9. Dynamic Typing​

In JavaScript, variable types are determined at runtime, allowing flexibility in assigning different types to the same variable.

let data = 42;
data = "Now a string"; // No error

10. Operators​

Operators in JavaScript include arithmetic, comparison, logical, etc.

let sum = 10 + 5;  // Arithmetic
let isEqual = 10 === 10; // Comparison

11. Operator Precedence​

This defines the order in which operations are executed.

let result = 2 + 3 * 4;  // 14, because * has higher precedence than +

12. Associativity​

Associativity determines the direction in which operators are evaluated.

let result = 10 - 5 - 2;  // Left-associative, evaluated as (10 - 5) - 2

13. Coercion​

JavaScript automatically converts types when required.

let result = '5' - 2;  // '5' is coerced into a number, result is 3

14. Namespace​

A way to organize code by grouping related variables and functions.

let MyApp = {
name: "My Application",
version: 1.0
};

15. First-Class Function (FCF)​

Functions in JavaScript can be treated as variables and passed around as arguments or returned from other functions.

function sayHello() {
return "Hello";
}
let greet = sayHello; // First-class function

16. Expression​

An expression is any valid unit of code that resolves to a value.

let sum = 5 + 10;  // The expression `5 + 10` evaluates to 15

17. Executing Function Created on the Fly​

Anonymous functions are created and executed instantly.

(function() {
console.log("Executed on the fly");
})();

18. Mutate​

To modify an object or variable's properties.

let obj = { name: "Alice" };
obj.name = "Bob"; // Mutating the object

19. Arguments​

arguments is an array-like object in functions that contains all the arguments passed.

function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3)); // 6

20. Whitespace​

Whitespace (spaces, tabs, newlines) is ignored in JavaScript code for the most part.

21. Closure​

A closure is a function that remembers and accesses variables from its outer scope, even after that scope has closed.

function outer() {
let x = 10;
return function inner() {
console.log(x);
};
}
const closureFunc = outer();
closureFunc(); // Outputs 10

22. Factory​

A function that returns objects.

function createPerson(name, age) {
return { name, age };
}
const person = createPerson("John", 30);

23. Callback Function​

A function passed as an argument to another function and executed later.

function fetchData(callback) {
setTimeout(() => {
callback("Data fetched");
}, 1000);
}

fetchData((message) => console.log(message));

24. Function Currying​

Currying transforms a function that takes multiple arguments into a series of functions, each taking one argument.

function add(a) {
return function(b) {
return a + b;
};
}
let add5 = add(5);
console.log(add5(3)); // 8

25. Inheritance​

Inheritance allows one object to acquire properties and methods of another.

class Animal {
speak() {
console.log("Animal speaks");
}
}

class Dog extends Animal {
bark() {
console.log("Dog barks");
}
}

26. Function Constructors​

A way to create objects in JavaScript using constructor functions.

function Person(name, age) {
this.name = name;
this.age = age;
}
const john = new Person("John", 30);

27. Polyfill​

A polyfill is code that provides functionality for older browsers that don't natively support newer features.

if (!Array.prototype.includes) {
Array.prototype.includes = function(element) {
return this.indexOf(element) !== -1;
};
}

28. Syntactic Sugar​

Syntactic sugar refers to language features that don't add new functionality but make the code easier to read and write.

// Arrow function is syntactic sugar for writing function expressions
const add = (a, b) => a + b;
info

If you are looking for a resource where you can learn Javascript. Summarized version of Understanding the Weird Parts is highly recommended.

Β· One min read
Nishant Mendiratta

Promises ( polyfill of Promise all, race, allSettled )​

Implement Async parallel, Async series which executes a set of async tasks parallel and in series​

Implement Retry API with n retry​

Build a Promise from Scratch​

Currying with example​

String.prototype.repeat​

Implement the compose() and pipe() polyfill .​

Design patterns like singleton, factory patterns, etc​

Implement the publisher-subscriber pattern in javascript​

Implement the map, filter, reduce, and for Each polyfill​

Implement the Function .bind, call, apply method on the Function Prototype​

Prototype and prototype inheritance​

How rendering works in the browser​

Event delegation and event propagation in js​

Implement Memoization​

Progressive Web application​

Clone an Object​

Debouncing and Throttling​

Implement clearAllTimeout()​

Event Target β€” addEventListener, removeEventListener, dispatchEvent​

Intersection Observer​

Summary

Resources​

Differences Between These Keywords

Β· 3 min read
Nishant Mendiratta

Currying Vs Partial application.​

In simple words: Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:

function f(x,y,z) {
z(x(y));
}

When curried, becomes:

function f(x) {
lambda(y) {
lambda(z) {
z(x(y));
}
}
}

In order to get the full application of f(x,y,z), you need to do this:

f(x)(y)(z);

Many functional languages let you write f x y z.

If you only call f x y or f(x)(y) then you get a partially-applied functionβ€”the return value is a closure of lambda(z){ z(x(y)) } with passed-in the values of x and y to f(x,y).

Example 1​

One way to use partial application is to define functions as partial applications of generalized functions, like fold:

function fold(combineFunction, accumulator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/*
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list)
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10
*/

Example 2​

The easiest way to see how they differ is to consider aΒ real example. Let's assume that we have a functionΒ AddΒ which takes 2 numbers as input and returns a number as output, e.g.Β Add(7, 5)Β returnsΒ 12. In this case:

Partial applyingΒ the functionΒ AddΒ with a valueΒ 7Β will give us a new function as output. That function itself takes 1 number as input and outputs a number. As such:

Partial(Add, 7); // returns a function f2 as output
// f2 takes 1 number as input and returns a number as output

So we can do this:

f2 = Partial(Add, 7);
f2(5); // returns 12;
// f2(7)(5) is just a syntactic shortcut

CurryingΒ the functionΒ AddΒ will give us a new function as output. That function itself takes 1 number as input and outputsΒ yetΒ another new function. That third function then takes 1 number as input and returns a number as output. As such:

Curry(Add); // returns a function f2 as output

// f2 takes 1 number as input and returns a function f3 as output
// i.e. f2(number) = f3

// f3 takes 1 number as input and returns a number as output
// i.e. f3(number) = number

So we can do this:

f2 = Curry(Add);
f3 = f2(7);
f3(5); // returns 12

In other words, "currying" and "partial application" are two totally different functions.Β Currying takes exactly 1 input, whereas partial application takes 2 (or more) inputs.

Even though they both return a function as output, the returned functions are of totally different forms as demonstrated above.

Resources​

SO

Β· 5 min read
Nishant Mendiratta

var vs let vs. const. What's the Difference?​

To analyze the differences between these keywords, I'll be using three factors:

  • Scope of variables
  • Redeclaration and reassignment
  • Hoisting

Var​

  • Variables declared with var can have a global or local scope.
  • Global scope is for variables declared outside functions, while local scope is for variables declared inside functions.
  • Variables declared with var can be redeclared and reassigned.
  • Variables declared with var are hoisted to the top of their global or local scope, which makes them accessible before the line they are declared. But the variable is hoisted with a default value of undefined.
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space

Let​

  • Variables declared with let can have a global, local, or block scope.
  • Block scope is for variables declared in a block. A block in JavaScript involves opening and closing curly braces.
  • Just like var, variables declared with let can be reassigned to other values, but they cannot be redeclared.
  • Redeclaring a variable with let will throw an error. You see we get a syntax error: Identifier 'number' has already been declared.
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space

Const​

  • Variables declared with const are similar to let in regards to scope. Such variables can have a global, local, or block scope.
  • In this regard, const is different from var and let. const is used for declaring constant variables – which are variables with values that cannot be changed.
  • So such variables cannot be redeclared, and neither can they be reassigned to other values. Attempting such would throw an error.
    • SyntaxError: Identifier 'number' has already been declared
    • TypeError: Assignment to constant variable
  • Variables declared with const, just like let, are hoisted to the top of their global, local, or block scope – but without a default initialization.
  • Accessing a variable declared with const before the line of declaration will throw a cannot access variable before initialization error.
    • ReferenceError: Cannot access 'number' before initialization
var a = 100;
let b = 200;
const c = 300;
{
var a = 1; // a is shadowing 'a' in line 1 (both 'a' are pointing to same memory location) // GLOBAL memory
let b = 2; // block memory
const c = 3; // block memory

console.log(a); // 1
console.log(b); // 2 // shadowing because of block memory
console.log(c); // 3 //
}

console.log(a); // 1 shadowed and modified line 1, both 'a' are pointing to same memory location)
console.log(b); // 200 // Not shadowing because of block memory, diff memory space
console.log(c); // 300 // Not shadowing because of block memory, diff memory space
Summary

Here's a table summary showing the differences between these keywords:

KEYWORDSCOPEREDECLARATION & REASSIGNMENTHOISTING
varGlobal, Localyes & yesyes, with default value
letGlobal, Local, Blockno & yesyes, without default value
constGlobal, Local, Blockno & noyes, without default value

These factors I've explained, play a role in determining how you declare variables in JavaScript.

If you never want a variable to change,Β constΒ is the keyword to use.

If you want to reassign values:

  • and you want the hoisting behavior,Β varΒ is the keyword to use
  • if you don't want it,Β letΒ is the keyword for you

The hoisting behavior can cause unexpected bugs in your application. That's why developers are generally advised to avoidΒ varΒ and stick toΒ letΒ andΒ cost.

Resources​

Differences Between These Keywords

Β· 5 min read
Nishant Mendiratta

A fun little quiz. The quizzes get more and more difficult, and they present a nice challenge if you fear your skills are getting rusty. Give it an honest try first, then come back here if you get seriously, honestly stuck. Try to do it yourself!

Problem 1: doubleInteger​

function doubleInteger(i) {
// i will be an integer. Double it and return it.

return i * 2;
}

Explanation​

Nothing tricky going on here. The prompt implies this should be done in two steps, but a one-liner works. We know i will always be an integer, so we don’t need any sanitization checks, just a straight up return.

Problem 2: isNumberEven​

function isNumberEven(i) {

// i will be an integer. Return true if it's even,
// and false if it isn't.

return i % 2 === 0;
}

Explanation​

The solution relies on an understanding of the modulus function. Basically, think of modulus as β€œremainder” function for division problems. For instance, 4 % 2 will give a modulus of 1 because 4 goes into 2 twice, evenly, with zero remainder. On the other hand, 5 % 2 will give a modulus of 1 because 5 goes into 2 twice, with a remainder of 1. This turns out to be a handy way to differentiate even (4) and odd (5) numbers.

Problem 3: getFileExtension​

function getFileExtension(i) {

// i will be a string, but it may not have a file extension.
// return the file extension (with no period) if it has one,
// otherwise false

var e = i.search(/\..+/)
if (e !== -1) {
return i.substr(e + 1);
}
else {
return false;
}
}

// Although above passes the test,
// this is a much more reliable solution.
function getFileExtension(i) {
var result = i.split('.');
return result.length > 1 ? result.pop() : false;
}

Explanation​

Now we’re getting a bit tougher! Strings have a handy function that splits them into an array: split(). For instance, "foo.bar.baz".split(".") will output ["foo", "bar", "baz"]. Notice that the periods have been removed, just as the prompt asked for!

In this case we need to first check if there was no file extension. This means that there were no periods to split on, resulting in a one-member array ("foo".split(".") becomes ["foo"]). In this case we return false, as the prompt asks for.

Now, in the else case, we know that the array must be larger than 1, meaning that it has a file extension. Since the file extension is always at the end, we know that it must be the last member in the array.

We can find the index of the last member by taking into account array length (result.length) and the fact that the array is zero-indexed, meaning that we need to subtract 1 from this length. Now we know the extension exists and is at the end of the array, so all we need to do is return it:

Problem 4: longestString​

function longestString(i) {

// i will be an array.
// return the longest string in the array

var longest = ''
if (Array.isArray(i)) {
for (var n = 0, len = i.length; n < len; ++n) {
if ((typeof i[n] === 'string') &&
(i[n].length > longest.length)) {
longest = i[n];
}
}
}
return longest;
}

// Alternate solution with some
// functional programming thrown in.
function longestString(i) {
var longest = '';
if (Array.isArray(i)) {
var a = i.filter(function(t) {
return typeof t === 'string';
}).forEach(function(t) {
if (t.length > longest.length) {
longest = t;
}
});

return longest;
}
}

Explanation​

Just keep track of the longest string in a variable, then loop through all the members of the array. If the current member is longer, set it as the new longest string. At the end, return whatever that variable contains.

Problem 5: arraySum​

function arraySum(i) {

// i will be an array, containing integers, strings
// and/or arrays like itself. Sum all the integers you
// find, anywhere in the nest of arrays.

var sum = 0;
if (Array.isArray(i)) {
for (var n = 0, len = i.length; n < len; ++n) {
if (Array.isArray(i[n])) {
sum += arraySum(i[n]);
}
else if (typeof i[n] === 'number') {
sum += i[n];
}
}
}
return sum;
}

Explanation​

The best way to handle this one is through recursion, which is tricky but powerful.

First, let’s start with arraySum(1). The output is of course 1, but note that we didn’t actually input an array. The code took the first codepath in the if statement (because typeof 1 === "number" is true)

Let’s see what happens when we input a basic array:

arraySum([1]); // 1

Again the answer is 1, but this time the codepath was different. This time Array.isArray([1]) was true, and the function was called recursively for each of the members in the array. That is to say, each member of the array (in this case the single member) was sent to arraySum as a simple integer. So the recursive call was the same as the original example: arraySum(1), and the output was the same.

In the case of more numbers, the outputs are all added to the sum and then returned back at the end of the function.

Note that this works quite well for even deeply nested arrays, since arraySum() has logic for arrays that will always return the sum of all of its deeply nested integers:

Resources​

Β· 2 min read
Nishant Mendiratta

Question:​

Write custom function to simulate implementation of double click.

Solution:​

Use this solution only when you want to bind a single click AND a double click to an element. Otherwise I recommend using the native click and dblclick listeners.

These are the differences:

  • Vanillajs, No dependencies
  • Don't wait on the setTimeout to handle the click or doubleclick handler
  • When double clicking it first fires the click handler, then the doubleclick handler

Click:

  • Fires when a mousedown and mouseup event occur on the same element.
  • Fires when the user activates the element that has the keyboard focus (usually by pressing Enter or the space bar).

dblclick

  • Fires when two mouse click events occur on the same element within a reasonable time. In our solution we have taken 400 ms.

Code:​

var singleClick = function () {
console.log('Single');
}
var doubleClick = function () {
console.log('Double');
}

function makeDoubleClick(singleClickCallBack, doubleClickCallBack) {
let clicks = 0, timeout;
return function (){
clicks++;
if (clicks === 1) {
singleClickCallBack.apply(this, arguments);
timeout = setTimeout(() => clicks = 0, 400);
} else {
clearTimeout(timeout);
doubleClickCallBack.apply(this,arguments);
clicks = 0;
}

}
}

document.addEventListener('click', makeDoubleClick(singleClick,doubleClick));

Latest Solution:​

Instead of utilizing more ad-hoc states and setTimeout, turns out there is a native property called detail that you can access from the event object! Modern browsers and even IE-9 supports it :) Source

element.onclick = event => {
if (event.detail === 1) {
// it was a single click
} else if (event.detail === 2) {
// it was a double click
}
};

Resources:​

Β· 5 min read
Nishant Mendiratta

Event Loop Phases in Node.js​

The event loop has several different phases to it and each one of these phases maintains a queue of callbacks that are to be executed.

Callbacks are destined for different phases based on how they are used by the application.

Phase 1: Poll​

  • Starting point of Node.js application
  • Most of the application code executes in this phase.
  • The poll phase executes I/O-related callbacks.

Phase 2: Check​

  • In this phase, callbacks that are triggered via setImmediate() are executed.

Phase 3: Close​

  • This phase executes callbacks triggered via EventEmitter close events.
  • For example, when a net.Server TCP server closes, it emits a close events that runs in this phase.

Phase 4: Timers​

  • In this phase, callbacks triggered via setTimeout() and setInterval() are executed.

Phase 5: Pending​

  • Special system events are run in this phase, like when a net.Socket TCP soccer throws an ECONNREFUSED error.

Microtask queues​

Apart from these, there are two special microtask queues that can have callbacks added to them while a phase is running.

  • The first microtask queue handles callbacks registered using process.nextTick().
  • The second microtask queues handles promises that reject or resolve.

Execution Priority and order​

  • Callback in the microtask queues take priority over callbacks in the phase's normal queue.
  • Callbacks in the next tick microtask queue run before callbacks in the promise microtask queue.
  • When the application starts running, the event loop is also started and the phases are handled one at a time. Node.js adds callbacks to different queues as appropriate while the application runs.
  • When the event loop gets to a phase, it will run all the callbacks in the phase's queue. Once all the callbacks in a given phase are executed, the event loop then moves on to the next phase.

Let's see one code example:

const fs = require('fs');

setImmediate(() => console.log(1));

Promise.resolve().then(() => console.log(2));

process.nextTick(() => console.log(3));

fs.readFile(__filename, () => {
console.log(4);
setTimeout(() => console.log(5));
setImmediate(() => console.log(6));
process.nextTick(() => console.log(7));
});

console.log(8);

Output will be 8 3 2 1 4 5 6 5

Let's see what is happening behind the scene:

Code execution starts off executing line by line in the poll phase.

Step 1: The fs module is required. Step 2: The setImmediate() call is run and its callback is added to the check queue. Step 3: the promise resolves, adding callback to the promise microtask queue. Step 4: process.nextTick() runs next, adding its callback to the next tick microtask queue. Step 5: The fs.readFile() tells Node.js to start reading the file, placing its callback in the poll queue once it is ready. Step 6: Finally console.log(8) is called and 8 is printed to the screen.

That's it for the current stack.

  • Now, the two microtask queues are consulted. The next tick microtask queue is always checked first, and callback with output 3 is called. Since, there is only one callback in the next tick microtask queue, the promise microtask queue is checked next and callback with output 2 is executed. That finished the two micro-task queues and the current poll phase is completed.
  • Now, the event loop enters the check phase. This phase has callback 1 in it, which is then executed. Both the microtask queues are empty at this point, so the check phase ends.
  • The close phase is checked next but is empty, so the loop continues. The same happens with the timers phase and the pending phase, and the event loop continues back around to the poll phase.

Once it is back in the poll phase, the application doesn't have much else going on, so it basically waits until the file has finished being read. Once that happens, the fs.readFile() callback is run.

  • The number 4 is immediately printed since it's the first line in the callback.
  • next, the setTimeout() call is made and callback 5 is added to the timers queue.
  • The setImmediate() call happens next, adding callback 6 to the check queue.
  • Finally, the process.nextTick() call is made, adding callback 7 to the next ticket microtask queue.

The poll phase is now finished and the microtask queues are again consulted.

  • Callback 7 runs from the next tick queue,
  • The promise queue is consulted and found empty, and the poll phase ends.
  • Again the event loop enters to the check phase where callback 6 is encountered. The number is printed and microtask queues are determined to be empty and the phase ends.
  • The close phase is checked again and found empty.
  • Finally, the timers phase is consulted and callback 5 is executed and prints 5 on the console.
  • Once that's done, the applications doesn't have any more work to do and it exits.

As we know, Node.js runtime environment is single-threaded. Running too much code in a single stack will stall the event loop and prevent other callbacks from firing.

tip

To prevent this event loop starving situation, you can break your CPU-heavy operations up across multiple stacks.

For example, if you are processing 1000 data records, you can consider breaking down into 10 batches of 100 records, using setImmediate() at the end of each batch to continue processing the next batch.

Another option is forking a new child process and offload processing to it. But never break up such work using process.nextTick(). Doing so will lead to a microtask queue that never empties and your application will be trapped in the same phase forever. The runtime won't throw any error instead it will remain a zombie process that eats through CPU.

Reference: Distributed Systems with Node.js (Book)

Β· 2 min read
Nishant Mendiratta

Can you write code for this function: sum(a)(b)(c)....( n)(). This should return the sum of all the numbers a+b+c+..+n.​


// Functions in javascripts are First Class Functions
// Functions are like objects
// sum(1)(2)(3).. (n)

// Fist consider it like a function sum(1)(2)() which is called and gives output 3
let sum = function (a) {
// Sum again gives a function which can be executed by passing an argument inside it let's call it b
return function (b) {
return a+b
}
}

// Now consider it like a function sum(1)(2)(3)() which is called and gives output 6
let sum = function (a) {
return function (b) {
return function (c) {
return a+b+c;
}
}
}


// We can see a clear pattern, will solve it recursively
const sum = function (a) {
return function (b) {
if (b) {
return sum(a+b); // it takes an argument and return a function which again can take an argument.
}
return a; // it will keep on adding 1+2+3+4..
}
};


console.log(sum(1)(2)()); //3
console.log(sum(1)(2)(3)(4)()); //10

Github​

Follow me on Github.


Bookmark​

Unlike life, keyboards do have shortcuts, press COMMAND+D to make this an easily accessible resource by bookmarking it.


Original Post​