Are you prepared for questions like 'Can you explain the concept of hoisting in JavaScript?' and similar? We've collected 40 interview questions for you to prepare for your next JavaScript interview.
Did you know? We have over 3,000 mentors available right now!
Hoisting in JavaScript is a behavior in which variable and function declarations are moved to the top of their containing scope during the compile phase, before the code has been executed. It's important to note that only the declarations are hoisted, not initializations. So if you declare and initialize a variable or function at the end of your scope, while you can refer to it earlier in your code without getting a reference error, it will return 'undefined' because the initialization only happens at the point in the code where you wrote it.
Consider an example. If you try to use a variable before declaring it like this:
javascript
console.log(myVar); // undefined
var myVar = 5;
console.log(myVar); // 5
Even though we used 'myVar' before declaring and initializing it, we didn't get a reference error. It returned 'undefined' because while the declaration ('var myVar') was hoisted, the initialization ('myVar = 5') wasn't. That's why when we logged it after initializing, it returned the correct value. In the case of function declarations, both the name and body are hoisted, so you can call a function before its declaration in the code.
In JavaScript, 'this' is a special keyword that refers to the context in which a function is called, also known as the execution context. It doesn’t have a value until the function is called. The value it takes on depends on how the function is invoked, not where the function is defined.
For instance, when used inside a method of an object, 'this' refers to the object itself.
Consider an example with an object as follows:
javascript
let car = {
make: "Tesla",
showMake : function(){
console.log(this.make);
}
}
car.showMake(); // Tesla
In the code above, 'this.make' within the 'showMake' method refers to the 'make' property of the 'car' object because the function is being invoked as a method of the 'car' object.
But, if a function isn't called as a method, like a standalone function or a callback, 'this' doesn't refer to the object in which it's defined, it refers to the global object or is undefined, if in strict mode.
JavaScript includes both primitive and complex data types. The primitive data types include Number, String, Boolean, Undefined, Null, BigInt, and Symbol.
Number covers integers, floats, negative values, and NaN (Not a Number). String is a sequence of Unicode characters surrounded by single or double quotes. A Boolean can have only two values: true or false. Undefined means a declared variable but hasn’t been given a value. Null is an assignment value meaning no value or no object.
BigInt, a relatively new data type, can handle numbers larger than 253-1, which is the limit for the Number type. Symbol, also a newer addition, is a unique and immutable primitive value and can be used as a key for object properties.
On the complex side, we have Object, which can contain any of the primitive data types, as well as Arrays and Functions. Arrays are a type of object used to store multiple values in a single variable. Functions are probably the most important type in JavaScript, allowing you to encapsulate behavior, and they are themselves a type of object in JavaScript.
Sure. The Document Object Model (DOM) is an interface that represents how HTML and XML documents are read by the browser. It forms a tree-like structure, with the 'document' as the root object and HTML tags as branches. JavaScript is used widely to interact with the DOM to dynamically change content, structure, or style of a webpage.
You can select elements in the DOM using various methods, such as 'getElementById', 'getElementsByClassName', 'getElementsByTagName', or the more modern 'querySelector' and 'querySelectorAll' that take CSS-style selectors as their arguments.
Once you've selected elements, you can manipulate their attributes and properties (like the 'className' or 'innerHTML'), create new elements and add them to the DOM (using 'createElement' and 'appendChild'), change CSS styles (using the 'style' property), etc.
For instance:
javascript
let heading = document.getElementById('myHeading');
heading.innerHTML = 'New Heading Text'; // changes the text of the element with ID 'myHeading'
You can also handle user interactions by adding event listeners to elements. An event listener waits for a specific event (like a click, hover, key press, etc.) to happen and then runs a function when that event is detected:
javascript
let myButton = document.querySelector('button');
myButton.addEventListener('click', function () {
// code to run when the button is clicked
});
All these interaction capabilities make JavaScript essential for creating dynamic, interactive web pages.
Closures in JavaScript is a concept where an inner function has access to the outer (enclosing) function's variables—scope chain. This scope chain consists of its own scope (variables defined between its curly brackets), the outer function's variables, and the global variables.
To put it in simpler terms, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
Here is an example of closure:
```javascript function outerFunction(outerVariable) { return function innerFunction(innerVariable) { console.log('outerVariable:', outerVariable); console.log('innerVariable:', innerVariable); } }
const newFunction = outerFunction('outside'); newFunction('inside'); ```
In the code above, innerFunction
has access to outerVariable
even after outerFunction
has finished its execution, demonstrating closure. When we run newFunction('inside')
, it logs both 'outside' and 'inside' because innerFunction
has access to the outerVariable
(due to its closure) and it has its own innerVariable
.
A Promise in JavaScript is an object representing the eventual completion or failure of an asynchronous operation. Essentially, it's a returned object to which you attach callbacks, instead of passing callbacks into a function.
Promises have three states: 1. Pending: The Promise's outcome hasn't yet been determined. 2. Fulfilled: The operation completed successfully. 3. Rejected: The operation failed.
You create a Promise with the 'new Promise' constructor which accepts a single argument—a callback with two parameters, typically named resolve and reject. The 'resolve' function is used to resolve the promise and can take a value which will be passed to the '.then' method. The 'reject' function is used when the promise cannot be fulfilled.
Example:
javascript
let promise = new Promise((resolve, reject) => {
let taskDone = true;
if(taskDone) {
resolve('Task completed');
} else {
reject(Error('Task not completed'));
}
});
You can use '.then' to schedule code to run after a promise fulfills, or if you need to catch an error, you can use '.catch' to handle the rejection.
javascript
promise.then( (successMessage) => {
console.log(successMessage); //logs 'Task completed'
}, (err) => {
console.log(err); //logs 'Task not completed'
});
It's important to handle errors in promises to prevent them from failing silently which makes debugging difficult.
The map()
function is a method built into JavaScript arrays that creates a new array with the results of calling a provided function on every element in the original array. It doesn't modify the original array, instead it returns a new array.
Here's a simple example. Suppose you have an array of numbers and you want to create a new array with each number squared:
javascript
let numbers = [1, 2, 3, 4, 5];
let squared = numbers.map(function(num) {
return num * num;
});
console.log(squared); // [1, 4, 9, 16, 25]
In the code snippet above, numbers.map
calls the provided function on each number in the array and stores the return value into the new 'squared' array.
The map function is especially useful when you want to transform elements in an array. The provided function could do anything from mathematical operations to reformatting strings. It's a clean, functional way to modify all elements in an array without resorting to loops or modifying the original array.
An arrow function is a newer addition to JavaScript and provides a more concise syntax for writing function expressions. It is defined using the arrow '=>' notation.
An example of an arrow function looks like this:
javascript
const square = num => num * num; // This function squares a number
In the code above, 'num' is the parameter and 'num * num' is the returned value. If you have multiple parameters, you need to wrap them in parentheses. If there are no parameters, you can use empty parentheses ().
Regular functions and arrow functions differ in two main ways. First, the syntax, as seen in the above, arrow functions are much more succinct. Second, and probably more importantly, they handle 'this' differently. Regular functions get their own 'this' value when you call them, usually pointing towards the object that calls them. But arrow functions do not have their own 'this' context, they inherit 'this' from the scope they're written in. This makes them more predictable, and means you don’t need to worry about binding 'this'.
```javascript let dog = { name: "Rex", activities: ["walk", "play"], showActivities() { this.activities.forEach((activity) => { console.log(this.name + " likes to " + activity); }); }, };
dog.showActivities(); // Rex likes to walk, Rex likes to play ``` In the code above, because we're using an arrow function in the forEach method, 'this.name' inside the forEach still refers to the 'name' property of the 'dog' object. If we were using a regular function, this would not be the case, because the function would have its own 'this' context.
In JavaScript, Object-Oriented Programming (OOP) is a coding style that uses objects and classes to structure and organize code. Here, the data is structured around objects, which can contain related properties and methods.
Let's go through four key principles of OOP in JavaScript:
Encapsulation: This means grouping related variables and functions (properties and methods) together in an object, so they are encapsulated. This makes it easier to understand how a piece of code works because its behavior is defined by its own methods and properties.
Inheritance: This principle allows a class to inherit properties and methods from another class. In JavaScript, one class (the "child" or subclass) can extend another class (the "parent" or superclass), inheriting all its features.
Polymorphism: This means the ability to call the same method on different objects and have each of them respond in their own way. This is commonly implemented in JavaScript using prototype-based inheritance.
Abstraction: This principle is about reducing complexity by hiding unnecessary details and showing only essential features to users. In JavaScript, this can be achieved by using private properties and methods that are only accessible inside an object.
These OOP principles allow for more modular and reusable code. In JavaScript, before ES6, OOP was implemented using constructor functions and prototype chains, but ES6 introduced classes which makes it more similar to OOP in other languages.
When a language is described as 'single-threaded', like JavaScript, it means that only one operation can be in progress at a time in its execution context. JavaScript has a single call stack where it keeps track of what function is currently being run and its caller functions. It processes one command at a time from the top of the stack.
But it's important to clarify that JavaScript's runtime environment—typically browser or Node.js—might not be single-threaded. They often provide APIs to allow for certain tasks (like HTTP requests or reading/writing files) to be handled in the background on separate threads, through asynchronous operations. That's how JavaScript, which is single-threaded, can still handle many tasks seeming simultaneously.
It's this combination of JavaScript's single-threaded nature with the ability to do non-blocking asynchronous operations that makes JavaScript particularly well-suited for tasks like handling user interactions on a webpage or dealing with multiple requests on a server.
There is no better source of knowledge and motivation than having a personal mentor. Support your interview preparation with a mentor who has been there and done that. Our mentors are top professionals from the best companies in the world.
We’ve already delivered 1-on-1 mentorship to thousands of students, professionals, managers and executives. Even better, they’ve left an average rating of 4.9 out of 5 for our mentors.
"Naz is an amazing person and a wonderful mentor. She is supportive and knowledgeable with extensive practical experience. Having been a manager at Netflix, she also knows a ton about working with teams at scale. Highly recommended."
"Brandon has been supporting me with a software engineering job hunt and has provided amazing value with his industry knowledge, tips unique to my situation and support as I prepared for my interviews and applications."
"Sandrina helped me improve as an engineer. Looking back, I took a huge step, beyond my expectations."
"Andrii is the best mentor I have ever met. He explains things clearly and helps to solve almost any problem. He taught me so many things about the world of Java in so a short period of time!"
"Greg is literally helping me achieve my dreams. I had very little idea of what I was doing – Greg was the missing piece that offered me down to earth guidance in business."
"Anna really helped me a lot. Her mentoring was very structured, she could answer all my questions and inspired me a lot. I can already see that this has made me even more successful with my agency."