JavaScript is an essential skill for anyone pursuing a career in web development, but securing a job in this field can be particularly challenging for newcomers. A critical part of the hiring process is the technical interview, where your JavaScript expertise will be thoroughly evaluated. To support your preparation and build your confidence, we’ve put together a list of the top 50 must-know JavaScript interview questions and answers frequently encountered in interviews.
Debouncing is a smart way to handle events that fire repeatedly within a short time, such as typing in a search box or resizing a window. Instead of executing a function every single time the event is triggered, debouncing ensures the function runs only after the event stops firing for a specified time.
It prevents performance bottlenecks by reducing the number of unnecessary function calls, making your app smoother and more efficient.
The debounce
method delays a function’s execution until after a defined "waiting period" has passed since the last event. Let’s see an example using Lodash:
import { debounce } from 'lodash';const searchInput = document.getElementById('search-input');const debouncedSearch = debounce(() => {// Perform the search operation hereconsole.log('Searching for:', searchInput.value);}, 300);searchInput.addEventListener('input', debouncedSearch);
While debouncing waits until user activity stops, throttling ensures the function runs at fixed intervals, regardless of how often the event occurs. Each technique suits specific use cases, such as search boxes (debouncing) versus scroll events (throttling).
Explore and practice more Debouncing techniques on GreatFrontEnd
Promise.all
Promise.all()
is a powerful method in JavaScript that allows you to handle multiple asynchronous tasks simultaneously. It takes an array of promises and returns a single promise that resolves when all the promises resolve, or rejects if any one of them fails.
This method is perfect when you need to wait for several independent asynchronous tasks to finish before proceeding, like fetching data from multiple APIs.
Here’s how Promise.all()
works with multiple API requests:
const promise1 = fetch('https://api.example.com/data/1');const promise2 = fetch('https://api.example.com/data/2');const promise3 = fetch('https://api.example.com/data/3');Promise.all([promise1, promise2, promise3]).then((responses) => {// Executes only when all promises are resolved.console.log('All responses:', responses);}).catch((error) => {// Catches any error from any promise.console.error('Error:', error);});
Promise.all
Explore more and practice Promise.all
on GreatFrontEnd
Deep equality involves comparing two objects or arrays to determine if they are structurally identical. Unlike shallow equality, which only checks if object references are the same, deep equality examines whether all nested values are equal.
Here's a simple deepEqual
function:
function deepEqual(obj1, obj2) {if (obj1 === obj2) return true;if (obj1 == null ||typeof obj1 !== 'object' ||obj2 == null ||typeof obj2 !== 'object')return false;let keys1 = Object.keys(obj1);let keys2 = Object.keys(obj2);if (keys1.length !== keys2.length) return false;for (let key of keys1) {if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) return false;}return true;}// Example usageconst object1 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};const object2 = {name: 'John',age: 30,address: {city: 'New York',zip: '10001',},};console.log(deepEqual(object1, object2)); // true
This function uses recursion to check nested properties, ensuring all values match in both objects or arrays. It's a critical concept for comparing complex data structures in frontend development.
Practice Deep Equal on GreatFrontEnd
An EventEmitter is a utility that enables objects to listen for and emit events. It implements the observer pattern, allowing you to subscribe to actions or changes and handle them when triggered. This concept is fundamental in both JavaScript and Node.js for managing event-driven programming.
const eventEmitter = new EventEmitter();// Subscribe to an eventeventEmitter.on('customEvent', (data) => {console.log('Event emitted with data:', data);});// Emit the eventeventEmitter.emit('customEvent', { message: 'Hello, world!' });
EventEmitter allows flexible communication between components, making it useful in scenarios like state management, logging, or real-time updates.
Practice Event Emitter on GreatFrontEnd
Array.prototype.reduce()
?Array.prototype.reduce()
is a versatile method for iterating through an array and reducing it to a single value. It processes each element with a callback function, carrying over an accumulator to build the final result. Common use cases include summing numbers, flattening arrays, or even building complex objects.
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce(function (accumulator, currentValue) {return accumulator + currentValue;}, 0);console.log(sum); // Output: 15
reduce
?Practice building Array.protoype.reduce
on GreatFrontEnd
Flattening transforms a nested array into a single-level array, making it more manageable. Since ES2019, JavaScript provides the Array.prototype.flat()
method for this.
// Example: Flattening a nested arrayconst nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = nestedArray.flat(Infinity);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Here, .flat(Infinity)
ensures the entire array is flattened, no matter how deep. For less deeply nested arrays, you can specify the depth.
Before ES2019, custom solutions were common:
// Custom recursive array flattenerfunction flattenArray(arr) {return arr.reduce((acc, val) =>Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val),[],);}const nestedArray = [1, [2, [3, [4, [5]]]]];const flatArray = flattenArray(nestedArray);console.log(flatArray); // Output: [1, 2, 3, 4, 5]
Practice Flattening Arrays on GreatFrontEnd
Merging data is crucial when handling complex structures. JavaScript provides efficient ways to combine objects or arrays.
The spread operator is concise and intuitive for merging objects:
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = { ...obj1, ...obj2 };console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
Object.assign()
Another approach is Object.assign()
:
const obj1 = { a: 1, b: 2 };const obj2 = { b: 3, c: 4 };const mergedObj = Object.assign({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }
const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = [...array1, ...array2];console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
Array.concat()
const array1 = [1, 2, 3];const array2 = [4, 5, 6];const mergedArray = array1.concat(array2);console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
For nested objects, you’ll need custom logic or libraries:
function deepMerge(target, source) {for (const key in source) {if (source[key] instanceof Object && key in target) {Object.assign(source[key], deepMerge(target[key], source[key]));}}Object.assign(target || {}, source);return target;}const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = deepMerge(obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Alternatively, libraries like Lodash simplify deep merging:
const _ = require('lodash');const obj1 = { a: 1, b: { x: 10, y: 20 } };const obj2 = { b: { y: 30, z: 40 }, c: 3 };const mergedObj = _.merge({}, obj1, obj2);console.log(mergedObj); // Output: { a: 1, b: { x: 10, y: 30, z: 40 }, c: 3 }
Practice Merging Data on GreatFrontEnd
getElementsByClassName
getElementsByClassName
fetches elements matching a specific class and returns them as a live HTMLCollection
.
// Fetch and loop through elementsconst elements = document.getElementsByClassName('example');for (let i = 0; i < elements.length; i++) {console.log(elements[i].textContent);}
You can combine class names for more specific selections:
const elements = document.getElementsByClassName('class1 class2');
HTMLCollection
updates automatically if DOM elements are added or removed.
For more complex selectors, use querySelectorAll
:
const elements = document.querySelectorAll('.example');
Practice Using getElementsByClassName
on GreatFrontEnd
Memoization saves computed results to avoid redundant calculations.
function expensiveOperation(n) {console.log('Calculating for', n);return n * 2;}// Memoize functionfunction memoize(func) {const cache = {};return function (n) {if (cache[n] !== undefined) {console.log('From cache for', n);return cache[n];}const result = func(n);cache[n] = result;return result;};}const memoizedExpensiveOperation = memoize(expensiveOperation);console.log(memoizedExpensiveOperation(5)); // Calculating for 5, 10console.log(memoizedExpensiveOperation(5)); // From cache for 5, 10
Libraries like Lodash also provide a memoize
utility.
Practice Memoization on GreatFrontEnd
get
Accessing nested object properties risk errors if any property is undefined. Tools like Lodash's get
or JavaScript's optional chaining (?.
) help mitigate this.
const user = { address: { city: 'New York' } };console.log(_.get(user, 'address.city')); // 'New York'console.log(user.address?.city); // 'New York'
These methods safely retrieve nested properties without crashing the program.
Practice Using get
on GreatFrontEnd
Hoisting refers to how JavaScript moves variable and function declarations to the top of their scope during compilation. While only the declaration is hoisted (not the initialization), understanding hoisting helps in writing cleaner and bug-free code.
var
Variables declared with var
are hoisted and initialized as undefined
. Accessing them before initialization results in undefined
.
console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1
let
, const
, and class
Variables declared with let
, const
, and class
are hoisted but exist in a "temporal dead zone" until their declaration is reached, causing a ReferenceError
if accessed early.
console.log(y); // ReferenceErrorlet y = 'local';
Both the declaration and definition of functions are hoisted, allowing them to be called before their declaration.
foo(); // 'FOOOOO'function foo() {console.log('FOOOOO');}
For function expressions, only the variable is hoisted, not the function itself.
console.log(bar); // undefinedbar(); // TypeError: bar is not a functionvar bar = function () {console.log('BARRRR');};
Imports are hoisted, making them available throughout the module. However, their initialization happens before the module code executes.
foo.doSomething(); // Works fineimport foo from './modules/foo';
Modern JavaScript uses let
and const
to avoid hoisting pitfalls. Declare variables at the top of their scope for better readability and use tools like ESLint to enforce best practices:
By following these practices, you can write robust, maintainable code.
Practice Hoisting on GreatFrontEnd
let
, var
or const
?In JavaScript, let
, var
, and const
are used to declare variables, but they differ in scope, initialization, redeclaration, reassignment, and behavior when accessed before declaration.
Variables declared with var
are function-scoped or global, while let
and const
are block-scoped (confined to the nearest {}
block).
if (true) {var foo = 1;let bar = 2;const baz = 3;}console.log(foo); // 1console.log(bar); // ReferenceErrorconsole.log(baz); // ReferenceError
var
and let
can be declared without initialization, but const
requires an initial value.
var a; // Validlet b; // Validconst c; // SyntaxError: Missing initializer
Variables declared with var
can be redeclared, but let
and const
cannot.
var x = 10;var x = 20; // Allowedlet y = 10;let y = 20; // SyntaxError: Identifier 'y' has already been declared
var
and let
allow reassignment, while const
does not.
let a = 1;a = 2; // Allowedconst b = 1;b = 2; // TypeError: Assignment to constant variable
All variables are hoisted, but var
initializes to undefined
, whereas let
and const
exist in a "temporal dead zone" until the declaration is reached.
console.log(foo); // undefinedvar foo = 'foo';console.log(bar); // ReferenceErrorlet bar = 'bar';
const
for variables that don’t change to ensure immutability.let
when reassignment is needed.var
due to its hoisting and scoping issues.Explore the difference between ==
and ===
in JavaScript on GreatFrontEnd
==
and ===
in JavaScript?The ==
operator checks for equality after performing type conversion, while ===
checks for strict equality without type conversion.
==
)==
allows type coercion, which means JavaScript converts values to the same type before comparison. This can lead to unexpected results.
42 == '42'; // true0 == false; // truenull == undefined; // true
===
)===
checks both value and type, avoiding the pitfalls of type coercion.
42 === '42'; // false0 === false; // falsenull === undefined; // false
===
for most comparisons as it avoids implicit type conversion and makes code more predictable.==
only when comparing null
or undefined
for simplicity.let x = null;console.log(x == null); // trueconsole.log(x == undefined); // true
Object.is()
Object.is()
is similar to ===
but treats -0
and +0
as distinct and considers NaN
equal to itself.
console.log(Object.is(-0, +0)); // falseconsole.log(Object.is(NaN, NaN)); // true
===
for strict comparisons to avoid bugs caused by type coercion.Object.is()
for nuanced comparisons like distinguishing -0
and +0
.Explore the difference between ==
and ===
in JavaScript on GreatFrontEnd
The event loop is the backbone of JavaScript’s asynchronous behavior, enabling single-threaded execution without blocking.
setTimeout
and HTTP requests on separate threads.setTimeout
and UI events.Promise
callbacks, executed before macrotasks.console.log('Start');setTimeout(() => console.log('Timeout 1'), 0);Promise.resolve().then(() => console.log('Promise 1'));setTimeout(() => console.log('Timeout 2'), 0);console.log('End');
Output:
StartEndPromise 1Timeout 1Timeout 2
Explanation:
Start
, End
) run first.Promise 1
) follow.Timeout 1
, Timeout 2
) run last.Explore the event loop in JavaScript on GreatFrontEnd
Event delegation is an efficient way to manage events for multiple elements by attaching a single event listener to their common parent.
event.target
to determine the clicked element.// HTML:// <ul id="item-list">// <li>Item 1</li>// <li>Item 2</li>// </ul>const itemList = document.getElementById('item-list');itemList.addEventListener('click', (event) => {if (event.target.tagName === 'LI') {console.log(`Clicked on ${event.target.textContent}`);}});
Explore event delegation in JavaScript on GreatFrontEnd
this
Works in JavaScriptThe value of this
depends on how a function is called. Let’s explore its different behaviors.
Using new
: When creating objects, this
refers to the newly created object.
function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 'Alice'
Using apply
, call
, or bind
: Explicitly sets this
to a specified object.
function greet() {console.log(this.name);}const person = { name: 'Alice' };greet.call(person); // 'Alice'
Method Call: this
refers to the object the method is called on.
const obj = {name: 'Alice',greet() {console.log(this.name);},};obj.greet(); // 'Alice'
Free Function Call: Defaults to the global object (window
in browsers) or undefined
in strict mode.
function greet() {console.log(this); // global object or undefined}greet();
Arrow Functions: Capture this
from their enclosing scope.
const obj = {name: 'Alice',greet: () => {console.log(this.name); // Inherits `this` from enclosing scope},};obj.greet(); // undefined
this
Arrow functions simplify usage by capturing this
from their lexical scope.
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++;console.log(this.seconds);}, 1000);}const timer = new Timer();
Explore how this
works in JavaScript on GreatFrontEnd
sessionStorage
, and localStorage
Apart?When it comes to client-side storage, cookies, localStorage
, and sessionStorage
serve distinct roles:
Function: Stores small pieces of data sent along with HTTP requests to the server.
Limit: Roughly 4KB per domain.
Lifetime: Can persist or expire after a set time. Session cookies disappear when the browser closes.
Scope: Accessible across pages and subdomains for a single domain.
Security: Features like HttpOnly and Secure flags add extra security.
Example:
// Set a cookie with an expiry datedocument.cookie ='userId=12345; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/';// Read all cookiesconsole.log(document.cookie);// Delete a cookiedocument.cookie = 'userId=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
localStorage
Function: Allows persistent data storage on the client side.
Limit: About 5MB per origin.
Lifetime: Data stays until explicitly removed.
Scope: Shared across all tabs and windows for the same origin.
Security: Accessible by JavaScript within the same origin.
Example:
// Store data in localStoragelocalStorage.setItem('username', 'john_doe');// Retrieve dataconsole.log(localStorage.getItem('username'));// Remove an itemlocalStorage.removeItem('username');// Clear all localStorage datalocalStorage.clear();
sessionStorage
Function: Stores data for the duration of a page session.
Limit: Similar to localStorage
(around 5MB).
Lifetime: Cleared when the tab or browser closes.
Scope: Data is confined to the current tab or window.
Security: Accessible by JavaScript on the same origin.
Example:
// Store data in sessionStoragesessionStorage.setItem('sessionId', 'abcdef');// Retrieve dataconsole.log(sessionStorage.getItem('sessionId'));// Remove an itemsessionStorage.removeItem('sessionId');// Clear all sessionStorage datasessionStorage.clear();
Learn more about cookies, sessionStorage
, and localStorage
on GreatFrontEnd
<script>
, <script async>
, and <script defer>
Differ?<script>
TagWhen using the <script>
tag without attributes, it fetches and executes the script immediately, pausing HTML parsing.
Use Case: Critical scripts needed before page rendering.
Example:
<script src="main.js"></script>
<script async>
TagWith async
, the script loads in parallel to HTML parsing and executes as soon as it’s ready.
<script async src="analytics.js"></script>
<script defer>
TagWhen using defer
, the script loads alongside HTML parsing but only executes after the HTML is fully parsed.
Use Case: Scripts that rely on a complete DOM structure.
Example:
<script defer src="deferred.js"></script>
Discover more about <script>
, <script async>
, and <script defer>
on GreatFrontEnd
null
, undefined
, or Undeclared: What’s the Difference?Variables not defined using var
, let
, or const
are considered undeclared and can cause global scope issues.
undefined
A declared variable that hasn’t been assigned a value is undefined
.
null
Represents the intentional absence of any value. It’s an explicit assignment. Example Code:
let a;console.log(a); // undefinedlet b = null;console.log(b); // nulltry {console.log(c); // ReferenceError: c is not defined} catch (e) {console.log('c is undeclared');}
Read more about null
, undefined
, and undeclared variables on GreatFrontEnd
.call
vs .apply
: What’s the Difference?Both .call
and .apply
let you invoke a function with a specified this
value. The key difference lies in how arguments are passed:
.call
: Accepts arguments as a comma-separated list..apply
: Accepts arguments as an array.Memory Aid:
Example Code:
function sum(a, b) {return a + b;}console.log(sum.call(null, 1, 2)); // 3console.log(sum.apply(null, [1, 2])); // 3
Learn more about .call
and .apply
on GreatFrontEnd
Function.prototype.bind
Work?The bind
method is used to create a new function with a specific this
value and, optionally, preset arguments. This ensures that the function always has the correct this
context, regardless of how or where it’s called.
bind
:this
is correctly set for the function.const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
Explore Function.prototype.bind
on GreatFrontEnd
Using arrow functions for methods in constructors automatically binds the this
context to the constructor, avoiding the need to manually bind it. This eliminates issues caused by this
referring to unexpected contexts.
const Person = function (name) {this.name = name;this.sayName1 = function () {console.log(this.name);};this.sayName2 = () => {console.log(this.name);};};const john = new Person('John');const dave = new Person('Dave');john.sayName1(); // Johnjohn.sayName2(); // Johnjohn.sayName1.call(dave); // Davejohn.sayName2.call(dave); // John
Arrow functions are particularly useful in React class components, ensuring methods maintain the correct context when passed to child components.
Explore the advantage for using the arrow syntax for a method in a constructor on GreatFrontEnd
Prototypal inheritance is a way for objects to share properties and methods through their prototype chain.
null
.new
to create objects.function Animal(name) {this.name = name;}Animal.prototype.sayName = function () {console.log(`My name is ${this.name}`);};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function () {console.log('Woof!');};let fido = new Dog('Fido', 'Labrador');fido.bark(); // "Woof!"fido.sayName(); // "My name is Fido"
Explore how prototypal inheritance works on GreatFrontEnd
function Person(){}
, const person = Person()
, and const person = new Person()
?function Person(){}
: A function declaration, typically used for constructors if written in PascalCase.const person = Person()
: Calls the function normally and assigns the result to person
. No object creation happens unless explicitly returned.const person = new Person()
: Invokes the function as a constructor, creating a new object and setting its prototype to Person.prototype
.Syntax: function foo() {}
Hoisting: Fully hoisted; can be called before its definition.
Example:
foo(); // "Hello!"function foo() {console.log('Hello!');}
Syntax: var foo = function() {}
Hoisting: Only the variable is hoisted, not the function body.
Example:
foo(); // TypeError: foo is not a functionvar foo = function () {console.log('Hello!');};
Here are various approaches to creating objects in JavaScript:
Object Literals The simplest and most common way to create an object is using curly braces {}
with key-value pairs.
const person = {firstName: 'John',lastName: 'Doe',};
Object
Constructor Use the built-in Object
constructor with the new
keyword.
const person = new Object();person.firstName = 'John';person.lastName = 'Doe';
Object.create()
Method Create an object with a specific prototype.
const personPrototype = {greet() {console.log(`Hello, my name is ${this.name}.`);},};const person = Object.create(personPrototype);person.name = 'John';person.greet(); // Hello, my name is John.
ES2015 Classes Define objects using the class
syntax for a blueprint-like structure.
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I’m ${this.name} and I’m ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I’m John and I’m 30 years old.
Constructor Functions Use a function as a template for creating multiple objects.
function Person(name, age) {this.name = name;this.age = age;}const john = new Person('John', 30);console.log(john.name); // John
Explore various ways to create objects in JavaScript on GreatFrontEnd
A higher-order function is a function that either:
Accepts another function as an argument Example:
function greet(name) {return `Hello, ${name}!`;}function greetUser(greeter, name) {console.log(greeter(name));}greetUser(greet, 'Alice'); // Hello, Alice!
Returns another function Example:
function multiplier(factor) {return function (num) {return num * factor;};}const double = multiplier(2);console.log(double(4)); // 8
Explore the definition of a higher-order function on GreatFrontEnd
function Person(name, age) {this.name = name;this.age = age;}Person.prototype.greet = function () {console.log(`Hi, I’m ${this.name} and I’m ${this.age} years old.`);};const john = new Person('John', 30);john.greet(); // Hi, I’m John and I’m 30 years old.
class
keyword for cleaner and more intuitive syntax.class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I’m ${this.name} and I’m ${this.age} years old.`);}}const john = new Person('John', 30);john.greet(); // Hi, I’m John and I’m 30 years old.
static
in ES2015.extends
and super
keywords in ES2015.Explore differences between ES2015 classes and ES5 constructor functions on GreatFrontEnd
Event bubbling is the process where an event triggers on the target element and then propagates upwards through its ancestors in the DOM.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click', () => {console.log('Parent clicked');});child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child element will log both "Child clicked" and "Parent clicked" due to bubbling.
Use stopPropagation()
to prevent the event from propagating upwards.
child.addEventListener('click', (event) => {event.stopPropagation();console.log('Child clicked only');});
Explore event bubbling on GreatFrontEnd
Event capturing, also called "trickling," is the reverse of bubbling. The event propagates from the root element down to the target element.
Capturing is enabled by passing { capture: true }
in addEventListener()
.
const parent = document.getElementById('parent');const child = document.getElementById('child');parent.addEventListener('click',() => {console.log('Parent capturing');},{ capture: true },);child.addEventListener('click', () => {console.log('Child clicked');});
Clicking the child will log "Parent capturing" first, followed by "Child clicked."
Explore event capturing on GreatFrontEnd
mouseenter
and mouseover
Differ?mouseenter
mouseover
Explore the difference between mouseenter
and mouseover
on GreatFrontEnd
Example:
const fs = require('fs');const data = fs.readFileSync('file.txt', 'utf8');console.log(data); // Blocks until the file is fully readconsole.log('Program ends');
Example:
console.log('Start');fetch('https://api.example.com/data').then((response) => response.json()).then((data) => console.log(data)) // Non-blocking.catch((error) => console.error(error));console.log('End');
Explore the difference between synchronous and asynchronous functions on GreatFrontEnd
AJAX (Asynchronous JavaScript and XML) is a technique that allows web pages to fetch and send data asynchronously, enabling dynamic updates without reloading the entire page.
XMLHttpRequest
; fetch()
is the modern alternative.XMLHttpRequest
:let xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {console.log(xhr.responseText);} else {console.error('Request failed');}}};xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);xhr.send();
fetch()
:fetch('https://jsonplaceholder.typicode.com/todos/1').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error('Fetch error:', error));
Explore AJAX in detail on GreatFrontEnd
Explore the advantages and disadvantages of using AJAX on GreatFrontEnd
XMLHttpRequest
and fetch()
XMLHttpRequest
onprogress
.onerror
event.Example:
let xhr = new XMLHttpRequest();xhr.open('GET', 'https://example.com/api', true);xhr.onload = function () {if (xhr.status === 200) {console.log(xhr.responseText);}};xhr.send();
fetch()
.catch()
for better error management.AbortController
for cancellations.Example:
fetch('https://example.com/api').then((response) => response.json()).then((data) => console.log(data)).catch((error) => console.error(error));
fetch()
has cleaner syntax and better Promise integration.XMLHttpRequest
supports progress tracking, which fetch()
does not.Explore the differences between XMLHttpRequest
and fetch()
on GreatFrontEnd
JavaScript features a mix of primitive and non-primitive (reference) data types.
true
or false
.Type Detection: Use the typeof
operator to determine the type of a variable.
Explore the various data types in JavaScript on GreatFrontEnd
JavaScript provides multiple ways to iterate over objects and arrays.
for...in
Loops over all enumerable properties, including inherited ones.
for (const property in obj) {if (Object.hasOwn(obj, property)) {console.log(property);}}
Object.keys()
Retrieves an array of an object’s own enumerable properties.
Object.keys(obj).forEach((key) => console.log(key));
Object.entries()
Returns an array of [key, value]
pairs.
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
Object.getOwnPropertyNames()
Includes both enumerable and non-enumerable properties.
Object.getOwnPropertyNames(obj).forEach((prop) => console.log(prop));
for
LoopClassic approach for iterating through arrays:
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}
Array.prototype.forEach()
Executes a callback for each array item.
arr.forEach((element, index) => console.log(element, index));
for...of
Ideal for looping through iterable objects like arrays.
for (const element of arr) {console.log(element);}
Array.prototype.entries()
Iterates with both index and value.
for (const [index, element] of arr.entries()) {console.log(index, ':', element);}
Explore iteration techniques on GreatFrontEnd
...
)The spread operator is used to expand elements of arrays or objects.
Copying Arrays/Objects:
const array = [1, 2, 3];const newArray = [...array]; // [1, 2, 3]
Merging Arrays/Objects:
const arr1 = [1, 2];const arr2 = [3, 4];const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4]
Passing Function Arguments:
const nums = [1, 2, 3];console.log(Math.max(...nums)); // 3
...
)The rest operator collects multiple elements into an array or object.
Function Parameters:
function sum(...numbers) {return numbers.reduce((a, b) => a + b);}sum(1, 2, 3); // 6
Destructuring:
const [first, ...rest] = [1, 2, 3];console.log(rest); // [2, 3]
Explore spread and rest syntax on GreatFrontEnd
Map
size
property.const map = new Map();map.set('key', 'value');console.log(map.size); // 1
Object.keys()
, Object.values()
, or Object.entries()
.size
property.const obj = { key: 'value' };console.log(Object.keys(obj).length); // 1
Explore the difference between Map and plain objects on GreatFrontEnd
Map
/Set
and WeakMap
/WeakSet
WeakMap
and WeakSet
keys must be objects, while Map
and Set
accept any data type.WeakMap
and WeakSet
allow garbage collection of keys, making them useful for managing memory.Map
and Set
have a size
property.WeakMap
and WeakSet
are not iterable.// Map Exampleconst map = new Map();map.set({}, 'value');console.log(map.size); // 1// WeakMap Exampleconst weakMap = new WeakMap();let obj = {};weakMap.set(obj, 'value');obj = null; // Key is garbage-collected
Explore the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd
Arrow functions simplify function syntax, making them ideal for inline callbacks.
// Traditional function syntaxconst numbers = [1, 2, 3, 4, 5];const doubledNumbers = numbers.map(function (number) {return number * 2;});console.log(doubledNumbers); // [2, 4, 6, 8, 10]// Arrow function syntaxconst doubledWithArrow = numbers.map((number) => number * 2);console.log(doubledWithArrow); // [2, 4, 6, 8, 10]
Explore a use case for the new arrow function syntax on GreatFrontEnd
A callback is a function passed as an argument to another function, executed after the completion of an asynchronous task.
function fetchData(callback) {setTimeout(() => {const data = { name: 'John', age: 30 };callback(data);}, 1000);}fetchData((data) => {console.log(data); // { name: 'John', age: 30 }});
Explore the concept of a callback function in asynchronous operations on GreatFrontEnd
Delays execution of a function until a specified time has elapsed since its last invocation.
function debounce(func, delay) {let timeoutId;return (...args) => {clearTimeout(timeoutId);timeoutId = setTimeout(() => func.apply(this, args), delay);};}
Ensures a function executes at most once within a set time interval.
function throttle(func, limit) {let inThrottle;return (...args) => {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => (inThrottle = false), limit);}};}
Explore the concept of debouncing and throttling on GreatFrontEnd
Destructuring simplifies extracting values from arrays or objects into individual variables.
// Array destructuringconst [a, b] = [1, 2];// Object destructuringconst { name, age } = { name: 'John', age: 30 };
Explore the concept of destructuring assignment on GreatFrontEnd
Hoisting moves function declarations to the top of their scope during the compilation phase. However, function expressions and arrow functions do not get hoisted in the same way.
// Function declarationhoistedFunction(); // Works finefunction hoistedFunction() {console.log('This function is hoisted');}// Function expressionnonHoistedFunction(); // Throws an errorvar nonHoistedFunction = function () {console.log('This function is not hoisted');};
Explore the concept of hoisting on GreatFrontEnd
Classes in ES2015 use extends
for inheritance and super
to access parent constructors and methods.
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}}class Dog extends Animal {constructor(name, breed) {super(name);this.breed = breed;}speak() {console.log(`${this.name} barks.`);}}const dog = new Dog('Rex', 'German Shepherd');dog.speak(); // Rex barks.
Explore the concept of inheritance in ES2015 classes on GreatFrontEnd
Lexical scoping determines variable access based on where functions are defined, not where they’re called.
function outerFunction() {let outerVariable = 'I am outside!';function innerFunction() {console.log(outerVariable); // I am outside!}innerFunction();}outerFunction();
Explore the concept of lexical scoping on GreatFrontEnd
JavaScript has three main types of scope: global, function, and block.
// Global scopevar globalVar = 'I am global';function myFunction() {// Function scopevar functionVar = 'I am in a function';if (true) {// Block scopelet blockVar = 'I am in a block';console.log(blockVar); // Accessible here}// console.log(blockVar); // Error}
Explore the concept of scope in JavaScript on GreatFrontEnd
The spread operator (...
) expands elements of an iterable (like arrays) or properties of objects into individual elements.
// Copying an arrayconst arr1 = [1, 2, 3];const arr2 = [...arr1];// Merging arraysconst mergedArray = [...arr1, [4, 5]];// Copying an objectconst obj1 = { a: 1, b: 2 };const obj2 = { ...obj1 };// Passing as function argumentsconst sum = (x, y, z) => x + y + z;const nums = [1, 2, 3];sum(...nums); // 6
Explore the spread operator on GreatFrontEnd
this
Work in Event Handlers?In JavaScript, this
in event handlers refers to the element that triggered the event. Its context can be explicitly bound using bind()
, arrow functions, or direct assignment.
const button = document.querySelector('button');button.addEventListener('click', function () {console.log(this); // Refers to the button});const obj = {handleClick: function () {console.log(this); // Refers to obj},};button.addEventListener('click', obj.handleClick.bind(obj));
Explore the concept of this
in event handlers on GreatFrontEnd
Congratulations on reaching the end of our comprehensive collection of JavaScript interview questions and answers! We hope this guide has equipped you with the knowledge and confidence needed to excel in your next JavaScript interview. Keep in mind that consistent practice is essential—continue coding and revisiting these concepts until they feel effortless.