Introduction
Prepare for frontend interviews with ’52 Frontend Interview Questions – Ft. JavaScript.’ This comprehensive guide features a curated list of JavaScript-related questions to help you ace your next interview and land your dream job in frontend development.
Many developers, whether they’re beginners or more experienced, are finding it tougher to land jobs due to increased competition in the market compared to a few years ago.
The best approach in this situation is to keep learning and improving your skills.
In this post, I’ve put together 52 questions in a “question:answer” format that frontend developers commonly encounter during interviews. These questions are mainly focused on Junior level developers but also touch on topics relevant to Middle level developers.
Note: Even though most questions are aimed at Junior level, by thoroughly studying these topics and preparing for these questions, I was able to secure a job as a Middle Frontend Developer.
1. What data types exist in JavaScript?
- Number – Numbers
- String – Strings
- Boolean – Boolean type, true or false
- Object – JavaScript object
- null – a special value that represents “nothing”, “empty”, or “unknown value”.
- undefined – “value has not been assigned”. This type is assigned if a variable is declared but has no assigned value.
- Symbol – It is a special and unchangeable type of data that acts as a unique identifier for object properties.
- BigInt – used for creating large numbers.
const bigInt = 1234567890123456789012345678901234567890n;
2. What is the difference between “==” and “===”?
The “==” operator checks for abstract equality, while “===” checks for strict equality.
When using “==”, JavaScript may convert the data types to match before comparing, but with “===”, it doesn’t do any type conversion. This means that if two values are not of the same type, “===” will return false.
3. What are the ways to declare a variable?
There are 4 ways to declare a variable:
foo = 123; var foo = 123; let a = 123; const a = 123;
When you declare a variable using “var,” it behaves similarly to the first method. Variables declared this way have global or function scope and don’t have block scope, which can be a drawback.
Using “let” and “const” for declaring variables is considered a best practice. They offer block scope, which means a variable declared inside a block of code (like a function) won’t be visible outside of that block. “const” variables are immutable, but if it’s an object, you can still change its properties, and if it’s an array, you can modify and add elements to it.
4. What is the difference between null and undefined?
Both options represent an empty value. When we create a variable but don’t give it a value, it automatically gets a special marker called “undefined.”
“Null” is another special value that represents “nothing” or “empty.” If we want to clear the value of a variable intentionally, we set it to “null” by assigning it manually, like this: foo = null
.
5. Arrow functions and the differences from regular functions.
- Arrow functions can’t use the
arguments
object. - They have a different syntax compared to regular functions.
- Arrow functions don’t have their own
this
context. Instead, they inherit thethis
value from the surrounding scope. - Arrow functions can’t be used as constructor functions, meaning you can’t create objects with them using the
new
keyword.
6. What is a closure and why are they needed?
A closure is a function and all the external variables it can access. For instance, if there’s a function that contains another function inside it, the inner function will “close over” and keep the variables from its parent function.
function parent() { const a = 5; return function child() { console.log(5); // child closes over the variable 'a'; } }
7. What are template literals?
Template literals are enclosed in backticks (“) and they allow for multiline strings. They also let you embed expressions within them.
const name = 'John'; const text = `User's name is ${name}`; console.log(text) // User's name is John
8. What are Set and Map?
A Map is a collection, a way to organize data using key-value pairs, much like Objects. However, the key difference is that Map allows you to use keys of any type.
A Set is a type of collection similar to an array, but it doesn’t have keys. It’s like a list where each value can only appear once. Sets store only unique values.
9. How to check for the presence of a property in an object?
There are two ways to check if an object has a specific property:
- Using the
hasOwnProperty
function, which is available for every object. - Using the
in
operator. But be careful, as it checks all prototypes in the chain, which may not always give the intended result.
const obj = { year: 2023, name: "John" } console.log(obj.hasOwnProperty("year")) // true console.log("year" in obj) // true console.log("ye" in obj) // false
10. How to access an object property?
There are two ways to access properties of an object:
- Static way, using dot notation:
obj.a
- Dynamic way, using square brackets:
obj['a']
const obj = { year: 2023, name: "John" } console.log(obj['year']) // 2023 console.log(obj.name) // John
11. What are the main methods for working with arrays?
forEach
: A method used to loop through an array, but it doesn’t return anything. It’s a more elegant alternative to using a regularfor
loop.filter
: A method used to create a new array by filtering elements from the original array based on a provided function. It only includes elements for which the provided function returnstrue
.map
: A method used to create a new array by transforming each element of the original array using a provided function.reduce
: A method used to process each element of an array sequentially while maintaining an intermediate result. It’s useful for tasks like summing up all elements of an array or finding the maximum value.
12. What are the ways to create an object?
Using a constructor function:
function User(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } const user = new User('John', 'Johnson'); console.log(user); // { firstName: 'John', lastName: 'Johnson' }
Using object literal notation:
const user = { firstName: 'John', lastName: 'Johnson' };
Using a class:
class User { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } } const user = new User('John', 'Johnson'); console.log(user); // { firstName: 'John', lastName: 'Johnson' }
Using the create function:
const user = Object.create({ firstName: 'John', lastName: 'Johnson' });
13. What is a Promise?
A Promise is like a special object used to handle asynchronous code. It has its own state, which starts as “pending.” If the asynchronous code runs successfully, the Promise transitions to the “fulfilled” state. If an error occurs, it transitions to the “rejected” state.
A Promise takes two callback functions:
onFulfilled
: This function is triggered when the Promise is fulfilled (i.e., when the asynchronous code succeeds).onRejected
: This function is triggered when the Promise is rejected (i.e., when an error occurs during execution).
The usage pattern is as follows:
- When a piece of code needs to do something asynchronously, it creates a Promise and returns it.
- The external code, which is waiting for the asynchronous task to finish, receives the Promise and attaches
onFulfilled
andonRejected
callback functions to it. - Once the asynchronous task is done, the Promise automatically transitions to either the fulfilled or rejected state, triggering the corresponding callback function that was attached to it earlier.
14. What is async/await and how to use it?
async/await
is a special way to work with Promises in JavaScript.- When a function is declared with the
async
keyword, it always returns a Promise. - The
await
keyword makes JavaScript wait until the Promise on its right side is fulfilled before continuing execution. Once the Promise is fulfilled, it returns the result, and the code execution proceeds. However,await
can only be used inside functions declared with theasync
keyword.
15. How to check if an object is an array?
To check if an object is an array, you can use the Array.isArray()
method. It takes an object as input and returns true
if the object is an array, and false
if it is not an array.
const obj1 = { person: 'Stella' } const obj2 = new Array(2) const obj3 = [] console.log(Array.isArray(obj1)) // false console.log(Array.isArray(obj2)) // true console.log(Array.isArray(obj3)) // true
16. What is the purpose of the spread operator?
The spread operator (...
) is used to unpack arrays or objects. It lets you spread or expand elements from an iterable, like arrays and strings..
- In functions where the expected number of arguments for a call is zero or more.
- In array literals or expressions.
- In object literals where the number of key-value pairs should be zero or more.
const date = [2001, 3, 7] const newArray = [...date] // [2001, 3, 7]
17. How to avoid reference dependency when copying an object?
If the object does not contain nested objects, for example:
const obj = { firstName: 'John', lastName: 'Johndoe' }
In this case, you can use spread operator or Object.assign() method:
const copy = {...obj} // or const copy = Object.assign({}, obj)
If the object contains nested objects:
const obj = { data: { id: 1 } }
In this case, you need to perform a deep copy.
A workaround, though slower, is:
const copy = JSON.parse(JSON.stringify(obj))
This method is suitable for objects without prototypes and functions.
Alternatively, you can use the lodash library’s deepClone() function.
18. How to change the context of a function?
- Using the bind() method, which returns a new function with the bound context.
function foo() { return this } const obj = { name: 'John' } const newFoo = foo.bind(obj) console.log(newFoo()) // { name: 'John' }
- Using call() and apply() methods. The main difference is that call() accepts a sequence of arguments, while apply() accepts an array of arguments as the second parameter.
function foo() { return this } const obj = { name: 'John' } foo.call(obj, 'arg1', 'arg2') // { name: 'John' } foo.apply(obj, ['arg1', 'arg2']) // { name: 'John' }
19. What is a ternary operator?
A ternary operator is a shortcut for writing an if-else statement. It’s represented by a question mark ?
and a colon :
. It’s called “ternary” because it takes three arguments:
Condition ? Expression_1 : Expression_2
num >= 10 ? 'more than 10' : 'less than 10' // is equal to if (num >= 10) { return 'more than or equal to 10' } return 'less than 10'
20. What is destructuring?
Destructuring is a syntax that allows us to unpack arrays and objects into multiple variables.
const arr = ['John', 'Johnson'] const [firstName, lastName] = arr console.log(firstName, lastName) // John Johnson OR const obj = { firstName: 'John', lastName: 'Johnson' } const { firstName, lastName } = obj; console.log(firstName, lastName) // John Johnson
21. What is the DOM?
DOM stands for Document Object Model. It represents an HTML document as a tree structure made up of tags.
For example, each tag like <html>
, <head>
, <body>
, etc., is a node in the DOM tree, and each node is represented as an object.
The fundamental components of an HTML document are called tags.
According to the Document Object Model (DOM), each HTML tag is treated as an object. Tags that are inside other tags are like “children” of their parent element. The text inside a tag is also an object. All these objects are accessible using JavaScript, and we can use them to manipulate the page.
22. What is the Event Loop?
The event loop is like a manager in charge of handling tasks in JavaScript. It ensures that everything runs smoothly by managing the order in which tasks are executed. When an operation finishes, like a server request, it’s added to a list. The event loop then checks this list continuously and processes each task one by one. It also handles other tasks like timers and promises, making sure everything happens in the right order. This helps keep JavaScript running smoothly without getting stuck on any one task.
I highly recommend watching the video at the link provided, as the topic is important and deserves a separate article.
23. What is prototypal inheritance?
Every object in JavaScript has something called a prototype. This prototype can have methods and properties added to it. When you create a new object based on this prototype, the new object automatically inherits all the methods and properties of its prototype.
If a property is missing in the object, JavaScript will look for it in the prototype. This allows objects to share common functionality through their prototypes.
Learn more
24. What is the Optional Chaining operator?
The Optional Chaining operator (?.
) helps prevent errors when accessing nested properties in an object.
For example, if we have a user
object and want to access user.address.street
, the Optional Chaining operator can be used like this: user?.address?.street
.
If user.address
is either undefined
or null
, the evaluation stops and undefined
is returned, avoiding an error.
const user = {}; console.log(user.address.street) // Error! console.log(user?.address?.street) // undefined. No Error
25. What is Shadow DOM?
Shadow DOM is like a special area inside an HTML element where you can put content and styles that won’t affect the rest of the webpage. It’s useful for making components or widgets with their own look and behavior without messing up the rest of the page.
Learn more
26. What is recursion? How to use it?
Recursion is when a function solves a problem by calling itself within its own code. It’s like a function that keeps using itself to solve smaller parts of a bigger problem until it finds the answer.
A recursive function consists of:
- Termination condition or base case
- Recursive step – a way to reduce the problem into simpler forms.
function factorial(x) { if (x === 0) { return 1; } return x * factorial(x - 1); }
The base case is a necessary condition; otherwise, it will lead to stack overflow due to an infinite loop of function calls.
Learn more
27. What’s the difference between Function Expression and Function Declaration?
Function Declaration is the traditional way of declaring a function.
function foo() { console.log('Hello World'); }
Function Expression:
let foo = function() { console.log('Hello World'); }
With Function Declaration, a function is created and stored in a variable, just like any other value. It’s essentially treated as a value assigned to the variable “foo”. Function Declarations are processed before the code block is executed, so they’re visible throughout the entire block of code.
Function Expressions, on the other hand, are created only when the code is executed and the flow reaches them. They’re not processed until then.
28. What are constructor functions?
Constructor functions are regular functions that are used to create objects. However, there are two rules for using them:
- The name of the constructor function should start with a capital letter.
- The constructor function should be called using the new operator.
function User(firstName, lastName) { this.firstName = firstName this.lastName = lastName this.role = 'user' } const user = new User('John', 'Johnson') console.log(user.firstName) // John
When a constructor function is created using the new operator, the following happens:
- A new empty object is made, and it’s set as “this.”
- The code inside the constructor function runs, usually adding properties to this object.
- The value of this is returned.
29. How can you get a list of keys and a list of values from an object?
You can use Object.keys() to get a list of keys and Object.values() to get a list of values.
const user = { firstName: 'John', lastName: 'Johnson' } const keys = Object.keys(user) const values = Object.values(user) console.log(keys) // ['firstName', 'lastName'] console.log(values) // ['John', 'Johnson']
30. Provide an example of new functionality in ES6.
The most common ones:
- let and const. Introduction of new keywords let and const for declaring variables with block scope.
- Arrow functions. The concept of arrow functions allows for more concise and clear function definitions.
function add(a, b) { return a + b } // Regular function const add = (a, b) => a + b // Arrow function
- Default parameters. You can define default values for function parameters.
function greet(name = 'Anonymous') { console.log(Hello, ${name}!) } greet(); // "Hello, Anonymous!" greet('John') // "Hello, John!"
- Spread operator (…). The spread operator allows unpacking array or object elements for function arguments or creating new arrays/objects.
const numbers = [1, 2, 3]; console.log(...numbers) // 1 2 3 const array1 = [1, 2, 3]; const array2 = [...array1, 4, 5] // [1, 2, 3, 4, 5]
- Destructuring. Destructuring allows extracting values from arrays or objects and assigning them to variables.
const person = { name: 'John', age: 30, city: 'London' } const { name, age } = person; console.log(name, age) // "John 30" const numbers = [1, 2, 3] const [first, second] = numbers; console.log(first, second); // 1 2
31. How to do class inheritance in ES6?
Class inheritance is done using the “extends” keyword followed by the name of the parent class.
class User { firstName = 'John' lastName = 'Johnson' } class Customer extends User { cart }
32. What are micro and macro tasks in JavaScript?
n JavaScript, there are two types of tasks: microtasks and macrotasks. Both are part of the event loop, which is a cycle where JavaScript executes tasks.
Microtasks:
- These are small tasks that need to be done right away, before the browser updates the screen.
- They’re added to the queue using methods like
Promise.then()
,process.nextTick()
(in Node.js), orMutationObserver
. - Examples include executing promise handlers and making changes to the DOM (the structure of the webpage).
Macrotasks:
- These are larger tasks that are done after the current cycle of the event loop, and before the screen is updated.
- They’re added to the queue using functions like
setTimeout
,setInterval
,requestAnimationFrame
, and also include handling user input and network requests. - Macrotasks run after all the microtasks have finished.
The key difference is their priority: microtasks run before macrotasks. This allows for quicker updates to the interface and helps prevent the main JavaScript thread from being blocked.
Learn more
33. What are generators?
Generators are functions that produce a sequence of values one by one as needed. They’re particularly useful for working with objects and creating data streams.
To declare a generator, you use a special syntax called a generator function.
function* generateSomething() { yield 10; yield 20; yield 30; return 40; }
The next()
method is crucial in a generator. When you call next()
, it begins executing the code until it encounters the closest yield
statement. If there’s no value, it’s represented as undefined
. Once a yield
is reached, the function pauses, and the associated value is returned to the outer code.
let generator = generateSomething(); let first = generator.next();
34. What are the methods of storing data in a browser?
There are several methods of storing data in a browser:
- LocalStorage and SessionStorage – LocalStorage and SessionStorage are storage options in web browsers that store key-value pairs, preserving data even after page refreshes. They only accept string keys and values, requiring objects to be converted using JSON.stringify().
- Cookie – Cookies, on the other hand, are small data strings stored by the web server and added to requests using the Cookie header. Each cookie can hold up to 4kb of data, with most browsers allowing over 20 cookies per site.
- IndexedDB – IndexedDB is a built-in database in browsers, offering more power than localStorage. It supports multiple key types, flexible values, transactions, and key range queries. It’s suitable for offline applications and can be combined with Service Workers for enhanced capabilities.
Learn more
Learn more
Learn more
35. What is the difference between sessionStorage and localStorage?
SessionStorage and localStorage allow storing objects in key-value format in the browser.
The main differences are:
- localStorage can store up to 10 MB, while sessionStorage can store up to 5 MB of data.
- Data in localStorage persists even after the browser is closed, whereas data in sessionStorage is deleted when the browser tab is closed.
- Data stored in localStorage is accessible from any window, while sessionStorage data is only accessible within the same browser window. Learn more
36. What are regular expressions?
Regular expressions are special strings with defined rules and patterns. They are a powerful tool used to detect and work with specific patterns within other strings.
let str = "We will, we will rock you" console.log(str.match(/we/gi)) // ['We', 'we']
37. What are WeakSet and WeakMap and how do they differ from Map and Set?
The first difference between WeakMap and Map is that the keys in WeakMap must be objects, not primitive values.
The second difference is in the memory storage of the data structures. The JavaScript engine keeps values in memory as long as they are reachable, meaning they can be used.
Usually, object properties, array elements, or other data structures are considered reachable and are kept in memory as long as the data structure exists, even if there are no other references to them.
In the case of WeakMap and WeakSet, it works differently. Once an object becomes unreachable, it is removed from the data structure.
38. Why do two objects with the same fields return false when compared?
In JavaScript, objects are compared based on their memory references. Even if two objects have the same properties, they are considered different if they occupy different memory locations. Objects are only considered equal if they refer to the exact same object in memory.
const test1 = { value: 3 } const test2 = { value: 3 } console.log(test1 == test2) // false
39. Why can we call methods on primitive types?
JavaScript treats primitive data types like strings and numbers as if they were objects. This means you can use methods on them, like toLowerCase()
or toUpperCase()
. To enable this, each primitive data type has a corresponding wrapper object: String, Number, Boolean, and Symbol. These wrapper objects provide additional methods for working with primitive values.
40. How to check which class an object was created from?
You can check which class an object was created from using the instanceof operator, taking inheritance into account.
class Person {} const person = new Person() console.log(person instanceof Person) // true
41. Write code that will log the time spent on the site in seconds every 10 seconds.
let time = 0 setInterval(() => { time += 10 console.log(time) }, 10000)
42. What is a pure function?
A pure function is a function that satisfies two conditions:
- The function always gives the same result when given the same inputs.
- It doesn’t change anything outside of itself.
function calculate(num) { return calculate * 0.05; } console.log(calculate(15)) //calculate() function will always return the same result if we pass the same parameter
43. What is a higher-order function?
A higher-order function is a function that takes another function as an argument or returns a function as a result.
const nums1 = [1, 2, 3] const nums2 = nums1.map(function(num) { return num * 2; }) console.log(nums2) // [2, 4, 6]
44. Why do we need Promises if we can work with asynchronous code using callbacks?
If we want to fetch data from a server asynchronously using callback functions, the code might look like this:
func((x) => { anotherFunc(x, (y) => { andAnotherFunc(i, (j) => { // some code }) }) })
This situation is referred to as “callback hell” because each callback is nested inside another, and each inner callback relies on the parent function.
Using Promises, we can rewrite the code above:
func() .then((x) => { return anotherFunc(x) }) .then((y) => { return andAnotherFunc(y) }) .then((i) => { return i })
With Promises, the execution sequence is clear, making the code more readable.
Learn more
45. Write your own implementation of the bind method.
To implement it, we can use closure and the apply() method to bind the function to the context.
function bind(context, func) { return function(...args) { func.apply(context, args) } }
46. Write a calculator function with methods plus, minus, multiply, divide, and get. The function must work through optional chaining.
function calculator() { let result = 0; function plus(val) { result += val; return this; } function minus(val) { result -= val; return this; } function divide(val) { result /= val; return this; } function multiply(val) { result *= val; return this; } function get() { console.log(result); return this; } return { plus, minus, divide, multiply, get }; } let calc = calculator(); calc.plus(2).minus(1).plus(19).divide(2).multiply(3).get(); // 30
47. Write a randomSort function that takes an array of numbers and sorts the array in random order.
You can use the sort() method and Math.random() for this.
function randomSort(array) { return array.sort(() => { return 0.5 - Math.random(); }); } const arr = [2, 1, 3, -2, 9] console.log(randomSort(arr)) // [-2, 2, 1, 3, 9] console.log(randomSort(arr)) // [2, 1, -2, 9, 3] console.log(randomSort(arr)) // [-2, 1, 9, 2, 3] console.log(randomSort(arr)) // [1, -2, 2, 3, 9]
48. Write a deleteGreatestValue function that takes a two-dimensional array of numbers and removes the greatest number from each nested array.
We should iterate through every nested array, get the greatest value of each nested array and delete it.
function deleteGreatestValue(array) { for (let i = 0; i < array.length; i++) { const max = Math.max(...array[i]); const maxIndex = array[i].indexOf(max); array[i].splice(maxIndex, 1); } return array; } const arr = [[1, 4, 4], [2, 6, 3], [9, 2, 7]] console.log(deleteGreatestValue(arr)) // [[1, 4], [2, 3], [2, 7]]
49. Write a sortPeople function that takes an array of strings names and an array of numbers heights, where names[i] == heights[i]. It should sort the names array based on the heights array.
function sortPeople(names, heights) { const array = []; for (let [i, name] of names.entries()) { array.push([name, heights[i]]); } return array.sort((a, b) => b[1] - a[1]).map(([name]) => name); } const names = ['John', 'Maria', 'Alexa', 'Robert'] const heights = [180, 160, 165, 187] console.log(sortPeople(names, heights)) // ['Robert', 'John', 'Alexa', 'Maria']
50. Write a subsets function that takes an array of numbers nums and returns all possible variations of arrays from those numbers.
function subsets(nums) { let result = [[]]; for (let num of nums) { // Iterate through each number in the nums array const currentSize = result.length; // Get the current size of result to use it in the loop. for (let i = 0; i < currentSize; i++) { let subArray = [...result[i], num]; // Create a new subarray by adding the current number to the result[i] element. result.push(subArray); } } return result; // Return all possible variations of arrays from the numbers. }
51. How to reverse a linked list?
Lets create a function reverseLinkedList that takes a linked list as input and returns the reversed version of that list.
Approach:
- It initializes the result variable with null, which will hold the reversed list.
- It initializes the root variable with head, which points to the start of the list.
- It enters a while loop that continues until root becomes null, indicating the end of the list.
- Inside the loop, it checks if result already has elements. If it does, it creates a new list node with the current value root.val and a pointer to the next node result. It then updates result with this new node.
- If result doesn’t have any elements yet, it creates a new list node with the current value root.val and null as the pointer to the next node. It then updates result with this new node.
- After updating result, it moves to the next element in the list by assigning root.next to root.
- Once the while loop finishes, it returns the reversed list stored in result.
In summary, the function reverses the linked list by iterating through each node from the head to the tail, creating a new list node for each value and updating the pointers accordingly.
/** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ function reverseLinkedList(node) { let result = null; // Initialize the result variable with null, it will hold the reversed list. let root = head; // Initialize the root variable with head, pointing to the start of the list. // While root is not null (until we reach the end of the list) while (root) { if (result) { // If result already has elements result = new ListNode(root.val, result); // Create a new list node with the current value root.val and a pointer to the next node result. Update result. } else { // If result doesn't have any elements yet... result = new ListNode(root.val, null); // Create a new list node with the current value root.val and null as the pointer to the next node. Update result. } root = root.next; // Move to the next element in the list. } return result; // Return the reversed list. }
52. How to sort a linked list?
Lets create a function sortList that takes a linked list as input and returns the sorted version of that list.
Approach:
- Check if the given linked list is empty or not.
- Traverse the linked list and store the node values into an array.
- Sort the array using the built-in sort() method.
- Create a new linked list using the sorted array.
- Return the head of the created linked list.
/** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ function sortList (head) { if (!head) { return null; } let root = head; let arr = []; while(root){ arr.push(root.val); root = root.next; } arr.sort((a, b) => a - b); let node = new ListNode(arr[0]); head = node; let temp = head; for(let i = 1; i < arr.length; i++){ let node = new ListNode(arr[i]); temp.next = node; temp = temp.next; } return head; };
Conclusion
Preparing for these questions, studying the topics covered, and reviewing relevant resources can improve your chances of successfully passing the interview. This post is part of a series of posts on interview questions.
I look forward to your reactions and comments.
Good luck in your interview!