Top 100+ Javascript Interview Questions And Answers

In JavaScript, data types can be categorized into two main groups: primitive and non-primitive (also known as reference types) data types. Here's an explanation of each: 1. Primitive Data Types: These are immutable data types that store a single value. a. Boolean: Represents a logical value, either true or false. It is commonly used for conditions and branching in JavaScript.

let isTrue = true;
let a=5;
let isFalse = false;
console.log(isTrue);   // Output: true
console.log(isFalse);  // Output: false
b. Number: Represents numeric values, including integers and floating-point numbers.

let count = 10;
let price = 4.99;
console.log(count);  // Output: 10
console.log(price);  // Output: 4.99
undefined
c. String: Represents a sequence of characters enclosed in single or double quotes. Strings are used to represent textual data.

let message = "Hello, world!";
console.log(message);  // Output: Hello, world!
undefined
d. Null: Represents the intentional absence of any object value. It is often assigned to a variable to indicate that it has no value or that the value is unknown.

let value = null;
console.log(value);  // Output: null
undefined
e. Undefined: Represents an uninitialized or undeclared variable. If a variable is declared but not assigned a value, it will have the value of undefined.

let variable;
console.log(variable);  // Output: undefined
undefined
f. Symbol: Represents a unique identifier. Symbols are typically used as keys in objects to avoid naming conflicts.

let id = Symbol("unique");
console.log(id);  // Output: Symbol(unique)
undefined
2. Non-Primitive (Reference) Data Types: These are mutable data types that store references to memory locations rather than the actual values. a. Object: Represents a collection of key-value pairs and provides a way to group related data and functionality together. Objects can be created using object literals {}, constructor functions, or the class syntax introduced in ECMAScript 2015.

let person = {
  name: "John",
  age: 30,
  isAdmin: false
};
console.log(person);  // Output: { name: 'John', age: 30, isAdmin: false }
undefined
b. Array: Represents an ordered list of values. Arrays can hold values of any type, and their elements are accessed using numeric indices starting from 0.

let numbers = [1, 2, 3, 4, 5];
console.log(numbers);  // Output: [1, 2, 3, 4, 5]
undefined
c. Function: Represents executable code that can be invoked and performs a specific task. Functions are one of the fundamental building blocks in JavaScript and can be defined using function declarations or function expressions.

function greet(name) {
  console.log("Hello, " + name + "!");
}

greet("Alice");  // Output: Hello, Alice!
undefined
Non-primitive data types, such as objects, arrays, and functions, are passed by reference, meaning that when you assign them to a variable or pass them as arguments to functions, you are working with a reference to the original value stored in memory. Primitive data types, on the other hand, are passed by value, meaning that when you assign them to a variable or pass them as arguments, a copy of the value is created.
The main differences between the HTTP `GET` and `POST` methods are their intended purposes and the way they handle data: 1. Purpose: - `GET`: The `GET` method is used to retrieve data from a server. It is meant for reading or fetching a resource without modifying it on the server. - `POST`: The `POST` method is used to send data to the server to create or update a resource. It is meant for submitting data, such as form submissions, to be processed by the server. 2. Data Handling: - `GET`: Data is appended to the URL as query parameters. For example, `https://example.com/api/users?id=123`. This makes the data visible in the URL and is limited in size due to URL length restrictions. It is suitable for passing small amounts of data, but it's not recommended for sensitive or large data. - `POST`: Data is sent in the body of the HTTP request, which is not visible in the URL. This allows for sending larger amounts of data, and there are no URL length restrictions. It is suitable for sensitive or large data, such as JSON payloads. 3. Caching: - `GET`: `GET` requests can be cached by the browser or intermediate proxies since they are considered safe and idempotent. The same `GET` request can be repeated multiple times without any side effects. - `POST`: By default, `POST` requests are not cached because they may have side effects on the server, such as creating or updating resources. However, caching can be explicitly enabled for `POST` requests using appropriate cache headers. 4. Idempotence: - `GET`: `GET` requests are idempotent, meaning that making the same `GET` request multiple times should have the same result. It should not modify any data on the server. - `POST`: `POST` requests are not idempotent since they typically result in the creation or modification of a resource on the server. Making the same `POST` request multiple times may create multiple resources or have different outcomes. 5. Security: - `GET`: Since `GET` requests append data to the URL, the data becomes visible in browser history, server logs, and can be bookmarked. It is not recommended to send sensitive data via `GET` requests as it can be easily exposed. - `POST`: Data sent via `POST` requests is included in the body and is not directly visible in browser history or server logs, offering better security for sensitive information. Example : Here are examples that demonstrate the difference between the `GET` and `POST` methods: 1. `GET` Method Example: Let's say you have a RESTful API that provides information about users. To retrieve the details of a specific user with the ID "123", you would use a `GET` request. Here's an example using JavaScript's `fetch` API:

   fetch('https://example.com/api/users/123', {
     method: 'GET'
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `GET` request is made to the URL `https://example.com/api/users/123`, indicating that you want to retrieve user information for the user with the ID "123". The server will respond with the requested user data. 2. `POST` Method Example: Suppose you have a contact form on a website, and you want to send the form data to a server for processing. In this case, you would use a `POST` request to submit the data. Here's an example using JavaScript's `fetch` API:

   const formData = {
     name: 'John Doe',
     email: 'john@example.com',
     message: 'Hello, World!'
   };

   fetch('https://example.com/api/contact', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(formData)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `POST` request is made to the URL `https://example.com/api/contact` with the form data serialized as JSON in the request body. The server will process the submitted data, such as storing it in a database or sending an email. In summary, the `GET` method is used for retrieving data without modifying it, while the `POST` method is used for sending data to create or update a resource. `GET` requests append data to the URL, have caching advantages, and are idempotent. `POST` requests send data in the request body, are not cached by default, and are not idempotent.
The main differences between the HTTP `POST` and `PUT` methods are their intended purposes and the way they handle data: 1. Purpose: - `POST`: The `POST` method is used to send data to the server to create a new resource. It is often used when submitting forms or sending data that needs to be processed and stored on the server. Each `POST` request typically creates a new resource on the server, and the server assigns a unique identifier to it. - `PUT`: The `PUT` method is used to send data to the server to update an existing resource or create a resource at a specific URL. It is used when you know the exact location of the resource you want to update or create. A `PUT` request can either update an existing resource or create a new one if it doesn't already exist at the specified URL. 2. Idempotence: - `POST`: `POST` requests are not idempotent. Sending the same `POST` request multiple times may result in the creation of multiple resources with different identifiers or cause repeated side effects on the server. - `PUT`: `PUT` requests are idempotent. Making the same `PUT` request multiple times will have the same outcome, ensuring that the resource on the server is updated or created consistently. 3. Data Handling: - `POST`: Data is sent in the body of the HTTP request, typically as form data or serialized JSON/XML. The server processes this data and performs the necessary actions to create a new resource. - `PUT`: Data is sent in the body of the HTTP request, similar to the `POST` method. However, in a `PUT` request, the data represents the complete updated representation of the resource. The server uses this data to replace the existing resource or create a new one. 4. URL Convention: - `POST`: The URL for a `POST` request typically points to a collection endpoint or a general resource endpoint. For example, `https://example.com/api/users` to create a new user. - `PUT`: The URL for a `PUT` request usually points to a specific resource or a unique identifier for that resource. For example, `https://example.com/api/users/123` to update the user with the ID "123". 5. Safe Operations: - `POST`: `POST` requests are not considered safe operations as they can result in the creation of new resources or have side effects on the server. - `PUT`: `PUT` requests are not considered safe operations either, as they can update or create resources on the server. Example: Here are examples that demonstrate the difference between the `POST` and `PUT` methods: 1. `POST` Method Example: Let's say you have an API that handles blog posts. To create a new blog post, you would use a `POST` request. Here's an example using JavaScript's `fetch` API:

   const newPost = {
     title: 'My New Blog Post',
     content: 'This is the content of my new blog post.'
   };

   fetch('https://example.com/api/posts', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(newPost)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `POST` request is made to the URL `https://example.com/api/posts`, indicating that you want to create a new blog post. The server will process the request, create a new blog post with the provided data (`newPost`), and respond with the created post details. 2. `PUT` Method Example: Suppose you have an API that manages user profiles, and you want to update an existing user's information. To achieve that, you would use a `PUT` request. Here's an example using JavaScript's `fetch` API:

   const updatedUser = {
     name: 'Jane Doe',
     email: 'jane@example.com'
   };

   fetch('https://example.com/api/users/123', {
     method: 'PUT',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(updatedUser)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `PUT` request is made to the URL `https://example.com/api/users/123`, indicating that you want to update the user with the ID "123". The server will process the request, replace the existing user's information with the provided data (`updatedUser`), and respond with the updated user details. Conclusion: In summary, the `POST` method is used to send data to create a new resource, while the `PUT` method is used to send data to update an existing resource or create a new one at a specific URL. `POST` requests create new resources, are not idempotent, and send data representing the resource to be created. `PUT` requests update or create resources, are idempotent, and send data representing the complete updated representation of the resource.
Custom events are events that are created by developers to handle specific scenarios or to extend the capabilities of the built-in events. Custom events allow you to define and trigger events that are not natively available in the browser or the DOM. The ability to create custom events gives you more flexibility in designing event-driven systems and enables you to build modular, reusable components that communicate with each other using custom events. Custom events can be useful in various scenarios, such as: 1. Communication between components: Custom events provide a way for different components of an application to communicate and exchange information. Components can listen for custom events and respond accordingly. 2. Application-level events: You can create custom events to represent application-level events, such as "applicationInitialized" or "userLoggedOut." These events can be triggered at specific points in your application and can be used to trigger actions or update the UI. 3. Event-driven architecture: Custom events facilitate an event-driven architecture, where different parts of your application can be decoupled and communicate through events. This promotes loose coupling and improves the modularity and maintainability of your codebase. To work with custom events, you can use the `CustomEvent` constructor and the `dispatchEvent()` method to create and trigger custom events. Additionally, you can use the `addEventListener()` method to listen for and handle custom events. Here's a step-by-step guide on how to create custom events: 1. Create an event using the `CustomEvent` constructor:

   const myEvent = new CustomEvent('myEvent', {
     detail: { key: 'value' },
     bubbles: true, // Specifies whether the event should bubble up through the DOM tree (optional)
     cancelable: true // Specifies whether the event can be canceled with preventDefault() (optional)
   });

In the example above, we create a custom event named `'myEvent'`. The event can carry additional data in the `detail` property, which is an optional object. The `bubbles` and `cancelable` properties determine the behavior of the event during event propagation and allow for event cancellation if desired. 2. Dispatch the custom event on an element:

   const element = document.getElementById('myElement');
   element.dispatchEvent(myEvent);

In this step, we select the desired HTML element on which the event should be dispatched. Here, we use `document.getElementById('myElement')` to obtain the element with the ID `'myElement'`. Then, we call the `dispatchEvent()` method on the element, passing in the custom event `myEvent` as the argument. 3. Listen for the custom event:

   const element = document.getElementById('myElement');
   element.addEventListener('myEvent', event => {
     console.log('Custom event triggered!', event.detail);
   });

Finally, we register an event listener on the element to capture and handle the custom event. In this example, when the `'myEvent'` event is triggered, the provided callback function will execute. You can access the additional data passed in the event's `detail` property using `event.detail`. You have created a custom event, dispatched it on an element, and set up an event listener to respond to the event. You can adapt this approach to meet your specific use cases and define custom behavior for your events in JavaScript.
In JavaScript, both the `for...in` and `for...of` loops are used for iteration, but they serve different purposes and work with different types of data structures. Here's the difference between them: 1. `for...in` loop: The `for...in` loop is used to iterate over the enumerable properties of an object. It iterates over the keys of an object and provides access to the property names. Here's the basic syntax:

for (variable in object) {
  // code to be executed
}

The `variable` represents the property name in each iteration. Here's an example:

const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
  console.log(key); // Outputs: a, b, c
  console.log(obj[key]); // Outputs: 1, 2, 3
}

Note that `for...in` loop can also iterate over the inherited properties of an object. To avoid this, you can use the `hasOwnProperty` method to check if the property is directly defined on the object itself. 2. `for...of` loop: The `for...of` loop is used to iterate over iterable objects like arrays, strings, sets, maps, etc. It provides access to the values of the elements rather than their indices or keys. Here's the basic syntax:

for (variable of iterable) {
  // code to be executed
}

The `variable` represents the value in each iteration. Here's an example:

const arr = [1, 2, 3];

for (let value of arr) {
  console.log(value); // Outputs: 1, 2, 3
}

Unlike the `for...in` loop, the `for...of` loop does not give you access to the keys or indices of the iterable object. It directly provides the values. It's a simpler and more concise way to iterate over arrays and other iterable objects. Conclusion: `for...in` loop is used to iterate over object properties (keys), while the `for...of` loop is used to iterate over iterable objects, providing access to their values.
The output of the above code for first 'this.a' is '10' and second 'this.a' inside function x is ‘undefined’. Reason being that ‘this’ keyword when directly used inside an object’s method points to the object itself but in the above code ‘this’ keyword is present inside x() function of the vir() method , so its not being directly used in object’s method vir() , so it would refer to window object and there is no variable ‘a’ in the window object so output will be ‘undefined’.

let obj ={
a: 10,
vir : function(){
  x();
  console.log(this.a); //output 10
  function x(){
  console.log(this.a) // undefined
  }
 }
}
obj.vir();

Output will be:- 5 5 5 5 5 Reason - some people may think that the output should be 0,1,2,3,4 . But there is a twist here , the arrow function written inside setTimeout does not executes right way , instead it goes in the event queue. So , when the loop iterates from i = 0 till i =4 , all the five console.log(i) statements would go in the event queue , now at the end of iteration the value of i becomes 5 . After this the 5 console.log(i) statements present in the event queue would execute and hence we would see 5 printed 5 times on console.
Output of above will be undefined as inside function doit,variable 'a' will be hoisted at the top inside function scope and it will initialised as undefined.

var a = 90;
doit();
function doit(){
  console.log(a); // undefined
  var a = 10;
}

In JavaScript, `call()`, `bind()`, and `apply()` are methods available on functions and are used to manipulate how functions are invoked and bound to a specific context. Here's an explanation of each of these methods: 1. `call()`: The `call()` method is used to call a function with respect to any object. The `call()` method takes the context object as its first argument, followed by the arguments to be passed to the function. Syntax: `function.call(context, arg1, arg2, ...)` Example:

function greet(name) {
  console.log(`Hello, ${name}! My name is ${this.name}.`);
}

const person = {
  name: 'Alice'
};

greet.call(person, 'Bob');

Output:

Hello, Bob! My name is Alice.

In the example above, `call()` is used to invoke the `greet()` function with the `person` object as the context. The first argument `person` sets `this` inside the function to refer to the `person` object. 2. `bind()`: The `bind()` method creates a new function with a specified context and initial arguments, without invoking it immediately. It returns a new function that, when called, has its `this` value set to the provided context and any additional arguments are prepended to the original function's arguments. Syntax: `function.bind(context, arg1, arg2, ...)` Example:

function greet(name) {
  console.log(`Hello, ${name}! My name is ${this.name}.`);
}

const person = {
  name: 'Alice'
};

const greetPerson = greet.bind(person);

greetPerson('Bob');

Output:

Hello, Bob! My name is Alice.

In the example above, `bind()` is used to create a new function `greetPerson` that has its `this` value bound to the `person` object. The resulting function `greetPerson` can be invoked later with the remaining arguments. 3. `apply()`: The `apply()` method is similar to `call()`, but it takes arguments as an array or an array-like object instead of individual arguments. It is used to invoke a function immediately, specifying the context and an array of arguments to be passed to the function. Syntax: `function.apply(context, [arg1, arg2, ...])` Example:

function greet(name) {
  console.log(`Hello, ${name}! My name is ${this.name}.`);
}

const person = {
  name: 'Alice'
};

greet.apply(person, ['Bob']);


Output:

Hello, Bob! My name is Alice.

In the example above, `apply()` is used to invoke the `greet()` function with the `person` object as the context and an array containing the argument `'Bob'`. To summarize: - `call()` invokes a function immediately with a specified context and individual arguments. - `bind()` creates a new function with a specified context and initial arguments, without invoking it immediately. - `apply()` invokes a function immediately with a specified context and an array of arguments. These methods provide flexibility in managing the execution context (`this`) and arguments when working with JavaScript functions.
Arrow functions in JavaScript provide several advantages over traditional function expressions. Here are some benefits of using arrow functions: 1. Concise Syntax: Arrow functions have a compact and concise syntax, making the code more readable and reducing the amount of boilerplate code. They are particularly useful for writing shorter and more expressive functions. 2. Lexical `this` Binding: Arrow functions do not have their own `this` value. Instead, they lexically bind the `this` value of the enclosing scope. This means that the `this` value inside an arrow function is automatically inherited from the surrounding context. It eliminates the need to use `bind()`, `call()`, or `apply()` to preserve the `this` value or deal with `this`-related issues. 3. No Arguments Object: Arrow functions do not have their own `arguments` object. Instead, they inherit the `arguments` object from the enclosing scope. This can be beneficial in scenarios where you need to access the arguments passed to an enclosing function. 4. Implicit Return: Arrow functions provide implicit return behavior for concise one-line functions. If the function body consists of a single expression, you can omit the curly braces and the `return` keyword. The result of the expression will be automatically returned. 5. Well-suited for Callbacks: Arrow functions are well-suited for callback functions, such as event handlers or asynchronous operations, where the lexical binding of `this` and the concise syntax can make the code more readable and maintainable. Here's an example to illustrate some of these advantages:


const numbers = [1, 2, 3, 4, 5];

// Traditional function expression
const squared1 = numbers.map(function (num) {
  return num * num;
});

// Arrow function
const squared2 = numbers.map(num => num * num);

In the example above, the arrow function `num => num * num` provides a more concise and readable syntax compared to the traditional function expression. It also inherits the `this` value from the surrounding context, which can be useful in certain scenarios. Overall, arrow functions enhance code readability, simplify `this` handling, and provide a more concise syntax for writing functions, making them a popular choice in modern JavaScript development.