JavaScript Frontend Interview Quick Guide (20 Minutes)
This guide covers key JavaScript concepts often asked in frontend interviews. Given the short timeframe (around 20 minutes for these topics), focus on understanding the core idea and being able to explain it clearly with a simple example.
1. Modern JavaScript (ES6+) Features
ES6 (ECMAScript 2015) and later versions introduced significant syntax improvements and features to make JavaScript more powerful, readable, and efficient.
Key Features:
let and const
Block-scoped variable declarations (see Scope/Hoisting section for details).
Arrow Functions
Concise syntax for functions. Lexically binds this.
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// With body
const multiply = (a, b) => {
const result = a * b;
return result;
};Template Literals
String interpolation using backticks (\`) and ${expression}. Allows multi-line strings easily.
const name = 'John';
const age = 30;
// Old way
const message = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
// Template literals
const message = `Hello, my name is ${name} and I am ${age} years old.`;
// Multi-line
const html = `
<div>
<h1>Hello ${name}</h1>
<p>Age: ${age}</p>
</div>
`;Destructuring Assignment
Unpack values from arrays or properties from objects into distinct variables.
// Object destructuring
const user = { name: 'Alice', age: 25, city: 'NYC' };
const { name, age } = user;
console.log(name, age); // Alice 25
// Array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second] = colors;
console.log(first, second); // red green
// With default values
const { country = 'USA' } = user;
console.log(country); // USA
// Nested destructuring
const person = {
name: 'Bob',
address: {
city: 'Boston',
zip: '02101'
}
};
const { address: { city } } = person;
console.log(city); // BostonSpread (...) / Rest (...) Operators
Spread: Expands iterables into individual elements.
Rest: Collects multiple function arguments into a single array parameter.
// Spread operator
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
// Rest parameter
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// Rest in destructuring
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]Classes
Syntactic sugar over JavaScript's prototype-based inheritance.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
static species() {
return 'Homo sapiens';
}
}
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age);
this.jobTitle = jobTitle;
}
work() {
console.log(`${this.name} is working as ${this.jobTitle}`);
}
}
const emp = new Employee('John', 30, 'Developer');
emp.greet(); // Hello, I'm John
emp.work(); // John is working as DeveloperModules (import/export)
Native way to organize code into reusable files/modules.
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export default class Calculator {
multiply(a, b) {
return a * b;
}
}
// app.js
import Calculator, { add, subtract } from './math.js';
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
const calc = new Calculator();
console.log(calc.multiply(5, 3)); // 15Potential Interview Questions:
- Can you explain the difference between var, let, and const?
- What are the benefits of using arrow functions?
- Show me an example of object or array destructuring.
- What's the difference between the Spread and Rest operators?
Try ES6+ Features
2. JS Execution Context
The environment or "box" where JavaScript code is evaluated and executed. Every time code runs, it's within an execution context.
Key Points:
Types of Execution Context
- Global Execution Context (GEC): Base context for code running in the global scope
- Function Execution Context (FEC): Created whenever a function is invoked
Creation Phase
Memory is allocated. The engine sets up:
- Variable Environment (hoists variable/function declarations)
- Scope Chain
- Determines the value of
this
Execution Phase
Code is executed line by line. Values are assigned to variables.
Call Stack (Execution Stack)
Manages execution contexts. When a function is called, its FEC is pushed onto the stack. When it returns, it's popped off. The GEC is at the bottom.
// Example of Execution Context
var x = 10;
function outer() {
var y = 20;
function inner() {
var z = 30;
console.log(x + y + z); // 60
}
inner();
}
outer();
// Execution flow:
// 1. Global Execution Context created
// - x = undefined (creation phase)
// - outer function stored
// 2. Global Execution Phase
// - x = 10
// - outer() called
// 3. outer() Function Execution Context created
// - y = undefined
// - inner function stored
// 4. outer() Execution Phase
// - y = 20
// - inner() called
// 5. inner() Function Execution Context created
// - z = undefined
// 6. inner() Execution Phase
// - z = 30
// - console.log executed
// 7. inner() context popped off
// 8. outer() context popped off
// 9. Back to Global contextPotential Interview Questions:
- What is an execution context?
- Explain the Call Stack and how it relates to function calls.
- What happens during the creation phase of an execution context?
3. Event Loop in JS
JavaScript is single-threaded, but it handles asynchronous operations without blocking the main thread using the Event Loop mechanism.
Key Components:
- Call Stack: Where synchronous code execution happens
- Web APIs / C++ APIs: Handle asynchronous tasks (setTimeout, fetch, DOM events)
- Callback Queue (Task Queue): Holds callbacks for async tasks that have completed
- Microtask Queue: Holds Promise callbacks (.then, .catch, .finally) - higher priority
- Event Loop: Continuously checks if Call Stack is empty, then processes microtasks, then tasks
// Event Loop Example
console.log('1: Start');
setTimeout(() => {
console.log('2: Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('3: Promise');
});
console.log('4: End');
// Output:
// 1: Start
// 4: End
// 3: Promise (Microtask - higher priority)
// 2: Timeout (Callback Queue - lower priority)
// More complex example
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve()
.then(() => console.log('C'))
.then(() => console.log('D'));
console.log('E');
// Output: A, E, C, D, BHow It Works:
1. Execute synchronous code (Call Stack) 2. When async operation completes: - Promise callbacks → Microtask Queue - Other callbacks → Callback Queue 3. When Call Stack is empty: - Process ALL microtasks - Then process ONE task from Callback Queue - Repeat
Potential Interview Questions:
- How does JavaScript handle asynchronous operations if it's single-threaded?
- Explain the roles of the Call Stack, Callback Queue, and Event Loop.
- What's the difference between the Microtask Queue and the Callback Queue?
Try Event Loop Examples
4. Promises in JS
An object representing the eventual completion (or failure) of an asynchronous operation and its resulting value.
Promise States:
- Pending: Initial state, neither fulfilled nor rejected
- Fulfilled: Operation completed successfully (resolves with a value)
- Rejected: Operation failed (rejects with an error/reason)
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
resolve('Operation successful!');
} else {
reject('Operation failed!');
}
}, 1000);
});
// Using a Promise
myPromise
.then(result => {
console.log(result); // Operation successful!
return 'Next value';
})
.then(value => {
console.log(value); // Next value
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('Cleanup or final action');
});Promise Chaining
// Chaining promises
fetch('https://api.example.com/user/1')
.then(response => response.json())
.then(user => {
console.log('User:', user);
return fetch(`https://api.example.com/posts?userId=${user.id}`);
})
.then(response => response.json())
.then(posts => {
console.log('Posts:', posts);
})
.catch(error => {
console.error('Error:', error);
});async/await
Syntactic sugar built on top of Promises, making async code look synchronous.
// Using async/await
async function fetchUserData() {
try {
const userResponse = await fetch('https://api.example.com/user/1');
const user = await userResponse.json();
console.log('User:', user);
const postsResponse = await fetch(`https://api.example.com/posts?userId=${user.id}`);
const posts = await postsResponse.json();
console.log('Posts:', posts);
return { user, posts };
} catch (error) {
console.error('Error:', error);
throw error;
}
}
// async functions always return a Promise
fetchUserData().then(data => console.log('Done:', data));Promise Static Methods
// Promise.all - waits for all promises (fails if any fails)
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise(resolve => setTimeout(() => resolve('foo'), 100));
Promise.all([promise1, promise2, promise3])
.then(values => console.log(values)); // [3, 42, 'foo']
// Promise.race - first to settle wins
Promise.race([
new Promise(resolve => setTimeout(() => resolve('slow'), 500)),
new Promise(resolve => setTimeout(() => resolve('fast'), 100))
]).then(value => console.log(value)); // 'fast'
// Promise.allSettled - waits for all, doesn't fail
Promise.allSettled([
Promise.resolve('success'),
Promise.reject('error')
]).then(results => console.log(results));
// [{ status: 'fulfilled', value: 'success' }, { status: 'rejected', reason: 'error' }]Potential Interview Questions:
- What problem do Promises solve? (Callback hell, better async management)
- What are the different states of a Promise?
- Explain how async/await works and how it relates to Promises.
- What's the difference between Promise.all and Promise.race?
5. Single Threaded JS
JavaScript code execution runs on a single main thread. This means it can only perform one operation at a time in a specific order.
Key Concepts:
One Task at a Time
Instructions are executed sequentially. If a task takes a long time (e.g., complex calculation), it blocks subsequent code from running, potentially freezing the UI in browsers.
Concurrency Model
Asynchronous operations (I/O, timers) are handed off to the environment (Browser APIs, Node.js APIs) which operate potentially on different threads. When these tasks complete, their callbacks are queued for execution back on the main JS thread via the Event Loop.
// Blocking code (BAD)
console.log('Start');
// This blocks for 3 seconds!
const end = Date.now() + 3000;
while (Date.now() < end) {
// Busy waiting - blocks the thread
}
console.log('End after 3 seconds');
// Non-blocking code (GOOD)
console.log('Start');
setTimeout(() => {
console.log('This runs after 3 seconds');
}, 3000);
console.log('End immediately');
// The main thread is free to do other work!Implications
- Must write non-blocking code using async patterns
- Long-running computations should be broken up or moved to Web Workers
- I/O operations should always be asynchronous
- UI responsiveness depends on keeping the main thread free
Potential Interview Questions:
- What does it mean that JavaScript is single-threaded?
- If JS is single-threaded, how can it perform tasks like fetching data without freezing?
- What is 'blocking' in JavaScript?
6. IIFE (Immediately Invoked Function Expressions)
A JavaScript function that is defined and executed as soon as it's created.
Syntax:
// Method 1
(function() {
// code here
})();
// Method 2
(function() {
// code here
}());
// Arrow function IIFE
(() => {
// code here
})();
// With parameters
(function(name) {
console.log('Hello, ' + name);
})('John');Primary Use Cases:
1. Create Private Scope
// Without IIFE - global pollution
var count = 0;
function increment() {
count++;
}
// count is accessible globally
// With IIFE - private scope
(function() {
var count = 0;
function increment() {
count++;
console.log(count);
}
increment(); // 1
increment(); // 2
})();
// console.log(count); // ReferenceError: count is not defined2. Module Pattern
const calculator = (function() {
// Private variables
let result = 0;
// Private function
function validate(num) {
return typeof num === 'number';
}
// Public API
return {
add: function(num) {
if (validate(num)) {
result += num;
}
return this;
},
subtract: function(num) {
if (validate(num)) {
result -= num;
}
return this;
},
getResult: function() {
return result;
}
};
})();
calculator.add(5).add(3).subtract(2);
console.log(calculator.getResult()); // 6
// console.log(calculator.result); // undefined (private!)3. Avoid Variable Collisions
// Library A
(function() {
var $ = 'Library A';
console.log($);
})();
// Library B
(function() {
var $ = 'Library B';
console.log($);
})();
// No conflict!Modern Relevance
Less common now due to ES6 Modules providing a standard, cleaner way to manage scope and dependencies. Still useful occasionally for specific scoping needs or in environments without module support.
Potential Interview Questions:
- What is an IIFE and why would you use one?
- Are IIFEs still necessary with ES6 modules?
- How do IIFEs help with variable scope?
Try IIFE Examples
7. Scope and Hoisting
Scope
Determines the accessibility (visibility) of variables and functions at various parts of your code during runtime.
Types of Scope:
1. Global Scope
Variables declared outside any function or block. Accessible everywhere.
var globalVar = 'I am global';
function test() {
console.log(globalVar); // Accessible
}
test();
console.log(globalVar); // Accessible2. Function Scope
Variables declared inside a function. Accessible only within that function.
function myFunction() {
var functionVar = 'I am function-scoped';
console.log(functionVar); // Works
}
myFunction();
// console.log(functionVar); // ReferenceError3. Block Scope
Variables declared inside a block ({ ... }) using let or const. var is NOT block-scoped.
if (true) {
var varVariable = 'var is NOT block-scoped';
let letVariable = 'let is block-scoped';
const constVariable = 'const is block-scoped';
}
console.log(varVariable); // Works
// console.log(letVariable); // ReferenceError
// console.log(constVariable); // ReferenceError
// Loop example
for (var i = 0; i < 3; i++) {
// i is accessible outside
}
console.log(i); // 3
for (let j = 0; j < 3; j++) {
// j is block-scoped
}
// console.log(j); // ReferenceErrorHoisting
JavaScript's default behavior of moving declarations (but not initializations) to the top of their containing scope before code execution.
var Hoisting
console.log(x); // undefined (not ReferenceError) var x = 5; console.log(x); // 5 // Behind the scenes: // var x; // Declaration hoisted // console.log(x); // undefined // x = 5; // Assignment stays // console.log(x); // 5
let and const - Temporal Dead Zone (TDZ)
// console.log(y); // ReferenceError let y = 10; // console.log(z); // ReferenceError const z = 20; // let and const are hoisted but not initialized // Accessing before declaration = TDZ error
Function Hoisting
// Function Declaration - fully hoisted
sayHello(); // Works!
function sayHello() {
console.log('Hello');
}
// Function Expression - only variable hoisted
// sayGoodbye(); // TypeError: sayGoodbye is not a function
var sayGoodbye = function() {
console.log('Goodbye');
};
sayGoodbye(); // Works now
// Arrow Function - same as function expression
// greet(); // ReferenceError or TypeError
const greet = () => console.log('Hi');
greet(); // Works nowvar vs let vs const Summary
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized with undefined) | Yes (in TDZ) | Yes (in TDZ) |
| Reassignable | Yes | Yes | No |
| Must Initialize | No | No | Yes |
Potential Interview Questions:
- Explain the difference between var, let, and const in terms of scope and hoisting.
- What is hoisting in JavaScript?
- What is the Temporal Dead Zone (TDZ)?
- What's the difference in hoisting between function declarations and function expressions?
8. Callbacks in JS
A function that is passed as an argument to another function, with the intention of being executed ("called back") at a later time, often after an asynchronous operation has completed.
Basic Callback Example:
function greet(name, callback) {
console.log('Hello, ' + name);
callback();
}
function sayGoodbye() {
console.log('Goodbye!');
}
greet('John', sayGoodbye);
// Output:
// Hello, John
// Goodbye!Asynchronous Callbacks
// setTimeout
console.log('Start');
setTimeout(function() {
console.log('This runs after 2 seconds');
}, 2000);
console.log('End');
// Output:
// Start
// End
// This runs after 2 seconds
// Event listeners
document.getElementById('button').addEventListener('click', function() {
console.log('Button clicked!');
});
// Reading files (Node.js)
const fs = require('fs');
fs.readFile('file.txt', 'utf8', function(err, data) {
if (err) {
console.error('Error:', err);
return;
}
console.log('File contents:', data);
});Higher-Order Functions with Callbacks
// Array methods use callbacks
const numbers = [1, 2, 3, 4, 5];
// map
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
// filter
const evens = numbers.filter(function(num) {
return num % 2 === 0;
});
console.log(evens); // [2, 4]
// forEach
numbers.forEach(function(num) {
console.log(num);
});
// reduce
const sum = numbers.reduce(function(total, num) {
return total + num;
}, 0);
console.log(sum); // 15Callback Hell (Pyramid of Doom)
Deeply nested callbacks can become hard to read and maintain.
// BAD - Callback Hell
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
getMoreData(d, function(e) {
console.log('Finally done!', e);
});
});
});
});
});
// BETTER - Using Promises
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => getMoreData(c))
.then(d => getMoreData(d))
.then(e => console.log('Finally done!', e))
.catch(error => console.error(error));
// BEST - Using async/await
async function processData() {
try {
const a = await getData();
const b = await getMoreData(a);
const c = await getMoreData(b);
const d = await getMoreData(c);
const e = await getMoreData(d);
console.log('Finally done!', e);
} catch (error) {
console.error(error);
}
}Error-First Callback Pattern (Node.js)
function readFile(filename, callback) {
// Simulated async operation
setTimeout(() => {
const error = null; // or new Error('File not found')
const data = 'File contents';
// First argument is always error
callback(error, data);
}, 1000);
}
readFile('example.txt', function(err, data) {
if (err) {
console.error('Error:', err);
return;
}
console.log('Data:', data);
});Potential Interview Questions:
- What is a callback function?
- Can you give an example of where callbacks are used?
- What is 'callback hell' and how can it be avoided?
- What is a higher-order function?
Try Callbacks
9. Navigator Object
The Navigator object contains information about the browser and the user's system. It's available as window.navigator or just navigator.
Common Properties and Methods:
// Browser information
console.log(navigator.userAgent); // Browser user agent string
console.log(navigator.appName); // Browser name
console.log(navigator.appVersion); // Browser version
console.log(navigator.platform); // Operating system platform
// Language
console.log(navigator.language); // User's preferred language
console.log(navigator.languages); // Array of preferred languages
// Online status
console.log(navigator.onLine); // true if online, false if offline
// Cookies enabled
console.log(navigator.cookieEnabled); // true if cookies enabled
// Hardware
console.log(navigator.hardwareConcurrency); // Number of CPU cores
// Geolocation
navigator.geolocation.getCurrentPosition(
position => {
console.log('Latitude:', position.coords.latitude);
console.log('Longitude:', position.coords.longitude);
},
error => {
console.error('Error getting location:', error);
}
);
// Clipboard API
navigator.clipboard.writeText('Hello, World!')
.then(() => console.log('Text copied to clipboard'))
.catch(err => console.error('Failed to copy:', err));
navigator.clipboard.readText()
.then(text => console.log('Clipboard contents:', text))
.catch(err => console.error('Failed to read:', err));
// Service Workers
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => console.log('Service Worker registered'))
.catch(error => console.error('Registration failed:', error));
}
// Media Devices
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
// Use the stream (camera/microphone access)
})
.catch(error => console.error('Media access denied:', error));
// Battery Status
navigator.getBattery().then(battery => {
console.log('Battery level:', battery.level * 100 + '%');
console.log('Charging:', battery.charging);
});Feature Detection
Use Navigator to detect browser capabilities before using features:
// Check if features are available
if ('geolocation' in navigator) {
// Geolocation is available
}
if ('serviceWorker' in navigator) {
// Service Workers are supported
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// Camera/microphone access available
}
if (navigator.clipboard) {
// Clipboard API is available
}User Agent Detection (Use with Caution)
const ua = navigator.userAgent.toLowerCase();
if (ua.includes('chrome')) {
console.log('Chrome browser');
} else if (ua.includes('firefox')) {
console.log('Firefox browser');
} else if (ua.includes('safari')) {
console.log('Safari browser');
}
// Better: Feature detection instead of browser detection
if (typeof window.IntersectionObserver !== 'undefined') {
// Use IntersectionObserver
} else {
// Fallback or polyfill
}Potential Interview Questions:
- What is the Navigator object used for?
- How would you detect if a user is online or offline?
- Why is feature detection better than browser detection?
- How can you access the user's geolocation?
Quick Reference Summary
ES6+ Features
- let/const - block scoped
- Arrow functions - concise, lexical this
- Template literals - string interpolation
- Destructuring - unpack values
- Spread/Rest - expand/collect
- Classes - syntactic sugar
- Modules - import/export
Execution Context
- GEC - Global Execution Context
- FEC - Function Execution Context
- Creation Phase - memory allocation
- Execution Phase - code runs
- Call Stack - manages contexts
Event Loop
- Call Stack - sync execution
- Web APIs - async operations
- Microtask Queue - Promises (priority)
- Callback Queue - other callbacks
- Event Loop - orchestrates all
Promises
- States: pending, fulfilled, rejected
- .then() - handle success
- .catch() - handle errors
- .finally() - cleanup
- async/await - syntactic sugar
Scope & Hoisting
- Global - accessible everywhere
- Function - function scoped
- Block - {} scoped (let/const)
- var - hoisted, initialized undefined
- let/const - hoisted, TDZ
- Functions - fully hoisted
Callbacks
- Function passed as argument
- Executed later
- Used in async operations
- Higher-order functions
- Callback hell - avoid with Promises