New 100 JavaScript Interview Question

Introduction
JavaScript is one of the most popular programming languages used for developing dynamic and interactive web applications. As a student studying programming or preparing for a job interview, it is essential to have a solid understanding of JavaScript concepts and be well-prepared for potential interview questions.
In this guide, we will provide you with a collection of common JavaScript interview questions that students often encounter during technical interviews. These questions are designed to assess your knowledge of JavaScript fundamentals, including syntax, data types, functions, and object-oriented programming concepts.
Throughout this guide, we will cover a variety of topics, such as:
- JavaScript Basics: Questions related to the basic syntax, data types, variables, and operators in JavaScript.
- Functions: Questions about creating and using functions, understanding function scopes, and passing arguments.
- Objects and Prototypes: Questions exploring object-oriented programming concepts, including object creation, inheritance, and prototypes.
- DOM Manipulation: Questions about interacting with the Document Object Model (DOM) and manipulating web page elements using JavaScript.
- Asynchronous Programming: Questions related to handling asynchronous operations, including callbacks, promises, and async/await.
By studying these interview questions and practicing your answers, you’ll be well-prepared to tackle JavaScript-related questions in your upcoming interviews. Remember to not only memorize the answers but also understand the underlying concepts so that you can apply them effectively in different scenarios.
Basic Questions
1. What is JavaScript?
JavaScript is a popular programming language used for creating dynamic and interactive web pages. It runs on the client-side and allows developers to add functionality, validate forms, manipulate HTML elements, and perform other tasks within a web browser.
2. Differentiate between JavaScript and Java.
JavaScript | Java |
---|---|
Interpreted language | Compiled language |
Primarily used for web development | Used for various applications, including desktop and mobile development |
Dynamically typed | Statically typed |
Supports prototype-based inheritance | Supports class-based inheritance |
Designed to run in a web browser | Can be run on any device with a Java Virtual Machine (JVM) |
3. What are the different types of data types in JavaScript?
JavaScript has several built-in data types, including:
- String: Represents textual data.
- Number: Represents numeric values.
- Boolean: Represents either true or false.
- Object: Represents a collection of key-value pairs.
- Array: Represents an ordered list of values.
- Null: Represents the intentional absence of any object value.
- Undefined: Represents an uninitialized variable or missing property.
4. What is the use of NaN in JavaScript?
NaN stands for “Not a Number” and is a special value in JavaScript. It is returned when a mathematical operation fails or when a function attempting to parse a number fails. The use of NaN includes:
- Indicating the result of an invalid or undefined mathematical operation.
- Performing type-checking to identify non-numeric values.
- Detecting errors while parsing numeric inputs.
5. What are undefined and null in JavaScript?
In JavaScript, undefined
and null
are special values used to represent the absence or unavailability of a meaningful value.
undefined
: It is the default value assigned to variables that have been declared but not assigned a value. It indicates that the variable doesn’t have a defined value.
let x;
console.log(x); // Output: undefined
null
: It is a value assigned to a variable to indicate that it intentionally has no value or absence of an object.
let y = null;
console.log(y); // Output: null
6. What is the ‘this’ keyword in JavaScript?
The this
keyword in JavaScript refers to the object on which a function is being invoked. It provides a way to access properties and methods within an object’s scope.
const person = {
name: 'John',
greet: function() {
console.log(`Hello, ${this.name}!`);
}
};
person.greet(); // Output: Hello, John!
In the above example, this.name
accesses the name
property of the person
object within the greet
function.
7. What is a closure in JavaScript?
A closure is a combination of a function and the lexical environment within which it was declared. It allows a function to access variables from its outer scope, even after the outer function has finished executing.
function outer() {
const message = 'Hello';
function inner() {
console.log(message);
}
return inner;
}
const closure = outer();
closure(); // Output: Hello
In the above example, the inner
function forms a closure with the variable message
defined in the outer scope of outer()
. The returned inner
function can still access and use the message
variable even after the outer
function has completed execution.
8. Explain the concept of hoisting in JavaScript.
Hoisting is a JavaScript behavior where variable and function declarations are moved to the top of their containing scope during the compilation phase, regardless of where they are actually declared in the code.
console.log(x); // Output: undefined
var x = 5;
In the above example, even though the variable x
is accessed before its declaration, it doesn’t result in an error. This is because the variable declaration is hoisted to the top, and its initial value is undefined
until the assignment is encountered.
9. What is the difference between ‘==’ and ‘===’ operators?
The ==
and ===
are comparison operators in JavaScript.
==
(loose equality): It compares values after performing type coercion, meaning it converts the operands to a common type before comparison.
console.log(5 == '5'); // Output: true
console.log(true == 1); // Output: true
===
(strict equality): It compares both the values and types of the operands without performing type coercion.
console.log(5 === '5'); // Output: false
console.log(true === 1); // Output: false
In the above examples, ==
performs type coercion while ===
does not. Therefore, 5 == '5'
and true == 1
are evaluated as true, but 5 === '5'
and true === 1
are evaluated as false.
10. What are JavaScript Promises?
JavaScript Promises are objects used to handle asynchronous operations. They represent the eventual completion (or failure) of an asynchronous operation and allow us to chain multiple asynchronous operations together.
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data fetched successfully!');
}, 2000);
});
};
fetchData()
.then((data) => {
console.log(data); // Output: Data fetched successfully!
})
.catch((error) => {
console.log(error);
});
In the above example, the fetchData
function returns a Promise that resolves after a 2-second delay. We can use .then()
to handle the resolved value and .catch()
to handle any errors that occur during the asynchronous operation.
11. What is a callback function in JavaScript?
A callback function in JavaScript is a function that is passed as an argument to another function and is executed inside that function. It allows for asynchronous programming and enables functions to be executed after a specific event or operation.
function fetchData(callback) {
setTimeout(() => {
const data = 'Data fetched successfully!';
callback(data);
}, 2000);
}
function displayData(data) {
console.log(data); // Output: Data fetched successfully!
}
fetchData(displayData);
In the above example, the fetchData
function takes a callback function as an argument. After a 2-second delay, it invokes the callback function and passes the fetched data to it. The displayData
function is passed as a callback and logs the data to the console.
12. Explain how to create an object in JavaScript.
In JavaScript, objects can be created using object literals or constructor functions. Here’s an example of both approaches:
- Object Literal:
const person = {
name: 'John',
age: 25,
greet: function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
};
person.greet(); // Output: Hello, my name is John and I am 25 years old.
- Constructor Function:
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
}
const person = new Person('John', 25);
person.greet(); // Output: Hello, my name is John and I am 25 years old.
13. What are the different ways to define a JavaScript function?
There are multiple ways to define functions in JavaScript:
- Function Declaration:
function sayHello() {
console.log('Hello!');
}
sayHello(); // Output: Hello!
- Function Expression:
const sayHello = function() {
console.log('Hello!');
};
sayHello(); // Output: Hello!
- Arrow Function (ES6):
const sayHello = () => {
console.log('Hello!');
};
sayHello(); // Output: Hello!
14. What is the purpose of the array methods like map, filter, and reduce?
Array methods like map
, filter
, and reduce
are used to perform common operations on arrays:
map
: Creates a new array by applying a provided function to each element of the original array.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // Output: [2, 4, 6, 8, 10]
filter
: Creates a new array with all elements that pass a provided test.
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4]
reduce
: Applies a function to an accumulator and each element in the array, reducing it to a single value.
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // Output: 15
15. How can you write a for loop in JavaScript?
A for loop is used to iterate over a block of code a specific number of times. Here’s an example:
for (let i = 0; i < 5; i++) {
console.log(i); // Output: 0, 1, 2, 3, 4
}
In the above example, the loop initializes a variable i
to 0, executes the loop body as long as i
is less than 5, increments i
by 1 after each iteration.
16. Explain event bubbling and event capturing in JavaScript.
Event bubbling and event capturing are two different phases of event propagation in the DOM (Document Object Model).
- Event Bubbling: In this phase, when an event is triggered on a nested element, it “bubbles” up through its parent elements, invoking their respective event handlers.
document.getElementById('nested').addEventListener('click', () => {
console.log('Nested element clicked');
});
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent element clicked');
});
document.getElementById('grandparent').addEventListener('click', () => {
console.log('Grandparent element clicked');
});
If you click the nested element, the event will bubble up, and you will see the following output:
Nested element clicked
Parent element clicked
Grandparent element clicked
- Event Capturing: In this phase, the event is captured from the top-level element and propagates down to the target element.
document.getElementById('grandparent').addEventListener('click', () => {
console.log('Grandparent element clicked');
}, true);
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent element clicked');
}, true);
document.getElementById('nested').addEventListener('click', () => {
console.log('Nested element clicked');
}, true);
With event capturing, if you click the nested element, the event will capture from the top-level element, and you will see the following output:
Grandparent element clicked
Parent element clicked
Nested element clicked
17. What is JSON and how can you fetch data from a JSON file?
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is used to represent structured data as a collection of key-value pairs.
To fetch data from a JSON file, you can use the fetch()
function in JavaScript:
fetch('data.json')
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
});
In the above example, the fetch()
function is used to make an HTTP request to the JSON file. The response.json()
method is used to extract the JSON data from the response. The data can then be accessed and used in the second .then()
block.
18. How do you handle exceptions in JavaScript?
Exceptions in JavaScript can be handled using try-catch blocks. The try
block contains the code that may throw an exception, and the catch
block is used to handle the exception if it occurs.
try {
// Code that may throw an exception
throw new Error('Something went wrong');
} catch (error) {
// Code to handle the exception
console.log(error.message); // Output: Something went wrong
}
In the above example, the throw
statement is used to manually throw an exception. The catch
block catches the exception and executes the code inside it, logging the error message to the console.
19. What is the difference between localStorage and sessionStorage in the web storage API?
localStorage | sessionStorage |
---|---|
Persists data beyond the current session | Persists data only within the current session |
Data is shared across all tabs and windows of the same origin | Data is specific to the tab or window that created it |
Data remains stored until explicitly cleared or removed | Data is cleared when the session ends or the tab/window is closed |
Accessed using the localStorage object | Accessed using the sessionStorage object |
20. How can you make a redirect page with JavaScript?
To redirect to a different page using JavaScript, you can use the window.location
object and set its href
property to the desired URL.
// Redirect to a different page
window.location.href = 'https://example.com';
In the above example, window.location.href
is set to the URL you want to redirect to. Once the code is executed, the browser will navigate to the specified URL.
21. What are the different scopes of a variable in JavaScript?
In JavaScript, variables have three different scopes:
- Global Scope: Variables declared outside any function or block have a global scope and can be accessed from anywhere within the program.
- Function Scope: Variables declared inside a function have a function scope and are only accessible within that function, including any nested functions.
- Block Scope: Variables declared with
let
orconst
inside a block (e.g., within anif
statement or a loop) have block scope and are only accessible within that block.
22. Explain ‘use strict’ in JavaScript.
'use strict'
is a directive that enables strict mode in JavaScript. It helps catch common mistakes and enforces stricter rules for writing JavaScript code.
'use strict';
// Code written in strict mode
In the above example, placing the 'use strict'
directive at the beginning of a script or function enables strict mode for that code block.
23. What are ES6 classes and how are they different from constructors?
ES6 classes are a syntactical sugar over the existing prototype-based inheritance in JavaScript. They provide a more familiar class-based syntax for creating objects and managing inheritance.
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
const person = new Person('John');
person.greet(); // Output: Hello, my name is John
In the above example, the Person
class is defined using the class
keyword. It has a constructor that sets the name
property, and a greet
method. Objects can be created using the new
keyword followed by the class name, just like constructor functions.
24. What are arrow functions in JavaScript? How are they different from traditional functions?
Arrow functions, introduced in ES6, are a concise syntax for writing JavaScript functions. They have a more compact syntax and lexically bind the this
value.
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
In the above example, the traditional function add
and the arrow function add
both perform the same addition operation. The arrow function omits the function
keyword and uses a fat arrow (=>
) between the parameter list and the function body. Arrow functions also have implicit return if the function body is a single expression.
25. What is destructuring assignment in ES6?
Destructuring assignment is a feature in ES6 that allows you to extract values from arrays or properties from objects and assign them to variables in a concise way.
// Array Destructuring
const numbers = [1, 2, 3];
const [a, b, c] = numbers;
console.log(a, b, c); // Output: 1 2 3
// Object Destructuring
const person = { name: 'John', age: 25 };
const { name, age } = person;
console.log(name, age); // Output: John 25
In the above examples, array destructuring is used to assign values from an array to variables, and object destructuring is used to extract property values into variables. It provides a concise way to access and use the values stored in arrays or objects.
27. What are JavaScript generators?
JavaScript generators are special functions that can be paused and resumed, allowing for the generation of a sequence of values over time. They are defined using the
function* syntax and utilize the yield
keyword.
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
const generator = generateSequence();
console.log(generator.next().value); // Output: 1
console.log(generator.next().value); // Output: 2
console.log(generator.next().value); // Output: 3
In the above example, the generateSequence
generator function uses the yield
keyword to generate a sequence of values. Each call to generator.next().value
retrieves the next value from the generator.
28. What are async functions in JavaScript?
Async functions are a syntactical feature in JavaScript that allows writing asynchronous code using a synchronous-looking syntax. They are defined using the async
keyword and always return a Promise.
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data fetched successfully!');
}, 2000);
});
}
async function getData() {
const data = await fetchData();
console.log(data); // Output: Data fetched successfully!
}
getData();
In the above example, the getData
function is defined as an async function. Within the function, the await
keyword is used to pause the execution until the Promise returned by fetchData()
resolves. The value of the resolved Promise is then assigned to the data
variable.
29. What are the differences between var, let, and const in JavaScript?
var | let | const |
---|---|---|
Function-scoped | Block-scoped | Block-scoped |
Hoisted to the top of their scope | Not hoisted | Not hoisted |
Can be redeclared and reassigned | Cannot be redeclared, but can be reassigned | Cannot be redeclared or reassigned |
Can be accessed before they are declared (resulting in undefined ) | Throws a ReferenceError if accessed before declaration | Throws a ReferenceError if accessed before declaration |
Commonly used in pre-ES6 JavaScript | Preferred choice for variable declaration in ES6+ |
30. What are JavaScript modules?
JavaScript modules are reusable pieces of code that encapsulate related functionality. They allow us to separate code into separate files and import/export variables, functions, or classes between different modules.
moduleA.js
export const name = 'John';
export function greet() {
console.log(`Hello, ${name}!`);
}
moduleB.js
import { name, greet } from './moduleA.js';
console.log(name); // Output: John
greet(); // Output: Hello, John!
In the above example, moduleA.js
exports the name
variable and greet
function using the export
keyword. These exports can then be imported into moduleB.js
using the import
keyword. The exported variables and functions can be used within the importing module.
31. Explain how DOM manipulation is done in JavaScript.
DOM (Document Object Model) manipulation is the process of interacting with HTML elements using JavaScript. It allows us to dynamically modify the structure, content, and styles of a web page.
Here’s a basic example of DOM manipulation:
// HTML
<!-- <div id="myElement">Hello, World!</div> -->
// JavaScript
const element = document.getElementById('myElement');
element.textContent = 'Hello, JavaScript!';
element.style.color = 'red';
In the above example, the JavaScript code selects the HTML element with the ID myElement
using getElementById()
. Then, it modifies the text content of the element using the textContent
property and changes the color of the text using the style
object.
Intermediate Questions
1. What are the different types of Prototypal Inheritance?
There are several types of prototypal inheritance in JavaScript:
- Prototype Chain Inheritance: Objects inherit properties and methods directly from their prototype through the
[[Prototype]]
property. This forms a chain where each object’s prototype is linked to its parent object’s prototype, creating a prototype chain. - Constructor Inheritance: Constructor functions can be used to create objects that inherit properties and methods from a prototype. The
new
keyword is used to create instances of the constructor function, which inherit from the constructor’s prototype. - Object.create() Inheritance: The
Object.create()
method creates a new object with the specified prototype object. This allows for direct inheritance, where the newly created object inherits from the specified prototype. - Class Inheritance (ES6): With the introduction of ES6, JavaScript has a class syntax that provides a more familiar way to define and inherit from classes. Classes can be used to create objects that inherit properties and methods from a class using the
extends
keyword. - Mixin Inheritance: Mixins are objects or functions that can be used to add specific behavior to an object by merging or copying properties and methods. By combining multiple mixins, objects can inherit from multiple sources.
2. Explain the concept of Temporal Dead Zone in ES6.
In ES6, the Temporal Dead Zone (TDZ) is a behavior that occurs during the variable declaration phase. It is a period between the start of a block scope and the point at which a variable is declared, where accessing the variable results in a ReferenceError
.
The TDZ concept is related to the use of block-scoped variables declared with let
and const
. Unlike variables declared with var
, which are hoisted to the top of their scope, variables declared with let
and const
remain inaccessible until they are declared.
During the TDZ, if you try to access a block-scoped variable before its declaration, JavaScript throws a ReferenceError
. This behavior prevents accessing variables before they are properly initialized and promotes better code quality.
Here’s an example to illustrate the Temporal Dead Zone:
console.log(x); // Output: ReferenceError: x is not defined
let x = 10;
console.log(x); // Output: 10
In this example, accessing the variable x
before its declaration results in a ReferenceError
due to the TDZ. Only after the variable is declared with let x = 10
can it be accessed without an error.
3. What are Symbols in ES6?
In ES6, Symbols are a new primitive data type introduced to JavaScript. They are unique, immutable values that can be used as property keys for object properties. Symbols are created using the Symbol()
function.
The main characteristics of Symbols are:
- Uniqueness: Each Symbol value is unique and cannot be duplicated. Even if two Symbols have the same description, they are different values.
- Immutable: Symbols are immutable and cannot be changed or modified.
- Privacy: Symbols can be used as property keys to define non-enumerable properties on objects. This makes Symbols useful for creating private or internal properties that are not meant to be accessed or modified by external code.
4. What are Higher Order Functions in JavaScript?
Higher-order functions in JavaScript are functions that can accept other functions as arguments and/or return functions as their results. They treat functions as first-class citizens, allowing them to be manipulated and used as data.
Here’s an example of a higher-order function that takes a function as an argument:
function greet(name, callback) {
const message = `Hello, ${name}!`;
callback(message);
}
function displayMessage(message) {
console.log(message);
}
greet("John", displayMessage); // Output: Hello, John!
In the above example, the greet
function is a higher-order function that takes two arguments: name
and callback
. The callback
argument is a function that will be invoked within greet
. In this case, the displayMessage
function is passed as the callback
argument, and it logs the message to the console.
Higher-order functions enable more flexible and modular code by allowing functions to be composed and reused in various contexts. They are often used in functional programming paradigms and can be found in many JavaScript built-in methods like map
, filter
, and reduce
.
5. Explain Event Delegation in JavaScript.
Event delegation is a technique in JavaScript where instead of attaching an event listener to each individual element, a single event listener is added to a parent element. This listener then handles events that occur on its descendant elements.
The main advantages of event delegation are:
- Improved Performance: By attaching a single event listener to a parent element, you reduce the number of event listeners in the DOM. This can significantly improve performance, especially when dealing with a large number of elements or dynamically added elements.
- Dynamic Event Handling: Event delegation allows for handling events on elements that are added or removed dynamically from the DOM. Since the parent element remains constant, new elements automatically inherit the event handling without needing to add or remove event listeners explicitly.
- Simplified Code: Event delegation simplifies the code by reducing the need to attach event listeners to multiple elements. It promotes a more concise and efficient code structure.
Here’s an example to illustrate event delegation:
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
const myList = document.getElementById('myList');
myList.addEventListener('click', function(event) {
if (event.target.nodeName === 'LI') {
console.log('Clicked on:', event.target.textContent);
}
});
In this example, instead of attaching a click event listener to each <li>
element, a single event listener is added to the parent <ul>
element. When a click event occurs, the event bubbles up from the clicked <li>
to the <ul>
element, and the event listener is triggered. By checking the event.target
property, the specific <li>
element that was clicked can be identified and appropriate actions can be taken.
6. What is currying in JavaScript and when might you use this technique?
Currying is a technique in JavaScript where a function with multiple arguments is transformed into a sequence of functions, each taking a single argument. The curried function returns a new function for each argument until all arguments are provided, and then the final result is returned.
Here’s an example to illustrate currying:
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(4)); // Output: 8
In the above example, the multiply
function takes the first argument a
and returns an inner function that takes the second argument b
. The inner function, when invoked, multiplies a
with b
and returns the result.
Currying is often useful in scenarios where you have a function that can be partially applied with certain arguments upfront, and you want to reuse that partially applied function with different arguments later. It allows for creating specialized versions of a function that can be used in different contexts without repeating the same arguments.
Some benefits and use cases of currying include:
- Partial application: Currying allows you to create partially applied functions by fixing some arguments, which can be useful in scenarios where you want to reuse a function with certain arguments while leaving others open for later.
- Reusability: Curried functions can be reused and composed to build more complex functions.
- Modularity: Currying promotes code modularity by breaking down complex functions into smaller, reusable units.
7. What are JavaScript Mixins?
JavaScript mixins are a way to extend the functionality of an object by combining the properties and methods from multiple sources. It is a technique that allows code reuse and composition without the need for traditional class inheritance.
Mixins are typically implemented using object composition or function composition. They can be used to add new behavior to an object or enhance existing behavior by merging in the properties and methods from other objects or functions.
Here’s an example to illustrate the concept of mixins:
// Mixin object
const sayHelloMixin = {
sayHello() {
console.log("Hello!");
}
};
// Target object
const person = {};
// Applying the mixin
Object.assign(person, sayHelloMixin);
person.sayHello(); // Output: Hello!
In the above example, the sayHelloMixin
object defines a sayHello
method. We then use Object.assign
to merge the properties and methods from the sayHelloMixin
object into the person
object, effectively adding the sayHello
method to the person
object.
8. Explain the difference between ‘null’ and ‘undefined’.
null | undefined |
---|---|
Represents an intentional absence of any object value | Represents an unintentional absence of a defined value |
It is an assignment value that can be explicitly assigned to a variable | It is a primitive value automatically assigned to variables that have been declared but not assigned a value |
When explicitly assigned, it indicates that a variable has no value or an empty value | Indicates a variable or property has been declared but has not been assigned any value yet |
typeof null returns “object” | typeof undefined returns “undefined” |
Example: let name = null; | Example: let age; |
9. How does ‘Function.prototype.bind’ work in JavaScript?
The bind()
method in JavaScript is used to create a new function that, when invoked, has a bound this
value and potentially pre-set arguments. It allows you to control the execution context of a function and provides a way to create function references with specific this
values.
When bind()
is called on a function, it returns a new function with the same body and scope as the original function, but with a permanently bound this
value.
Here’s an example to illustrate the usage of bind()
:
const person = {
name: 'John',
greet: function() {
console.log('Hello, ' + this.name + '!');
}
};
const boundGreet = person.greet.bind(person);
boundGreet(); // Output: Hello, John!
In this example, the bind()
method is used to create a new function boundGreet
that is permanently bound to the person
object. When boundGreet()
is invoked, the this
value within the function is set to person
, regardless of how the function is called.
10. What are JavaScript decorators?
JavaScript decorators are a language feature introduced in ECMAScript (ES) standards, specifically ES2016 and later versions. Decorators are a way to modify the behavior of classes, methods, or properties by wrapping them with higher-order functions called decorators.
Decorators are defined using the @
symbol followed by a function or expression. They are placed immediately before the class, method, or property they intend to modify. When the code is executed, the decorator is invoked and can alter the target object or add additional functionality.
Here’s an example to illustrate the usage of decorators:
function log(target, name, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
console.log(`Calling ${name} with arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${name} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
console.log(calc.add(2, 3)); // Output: Calling add with arguments: 2, 3 // Method add returned: 5 // 5
In the above example, the log
decorator is defined as a higher-order function. It receives the target object, the method name, and a descriptor object. It wraps the original method by modifying the descriptor
object’s value
property. In this case, the decorator adds logging statements before and after the method is invoked.
Decorators can be used for various purposes, such as:
- Logging and debugging
- Adding validation or error checking
- Applying access control or permissions
- Modifying the behavior of methods or properties
- Implementing aspects like caching or memoization
It’s important to note that decorators are a proposal in JavaScript and may require a transpiler like Babel to use them in current environments.
11. Explain try-catch-finally
statement in JavaScript.
The try-catch-finally
statement in JavaScript is used to handle exceptions and control the flow of execution in error-prone code blocks. It allows you to attempt a block of code that may throw an exception and provides a way to handle and recover from any thrown exceptions.
The basic syntax of try-catch-finally
is as follows:
try {
// Code that might throw an exception
} catch (error) {
// Code to handle the exception
} finally {
// Code that always executes, regardless of whether an exception was thrown
}
Here’s how the try-catch-finally
statement works:
- The code within the
try
block is executed. If an exception is thrown during the execution of this block, the normal flow of execution is immediately transferred to the correspondingcatch
block. - If an exception is thrown, the
catch
block is executed. Thecatch
block takes anerror
parameter that represents the thrown exception. You can use this block to handle or process the exception, log error messages, or take any necessary recovery actions. - The
finally
block is optional. It contains code that is executed regardless of whether an exception was thrown or caught. This block is commonly used to perform cleanup operations, such as closing resources or releasing memory, that need to be executed regardless of the exception handling.
The try-catch-finally
statement allows you to handle exceptions and gracefully recover from errors, preventing your program from terminating unexpectedly. It provides a structured way to handle exceptional cases and ensures that critical cleanup operations are performed, even in the presence of exceptions.
12. Explain the differences between ‘throw’ and ‘throw new Error’.
In JavaScript, both throw
and throw new Error
are used to throw exceptions, but they have slight differences:
throw
: Thethrow
statement is used to throw any value as an exception. It can be used with any type of value, such as strings, numbers, objects, or custom error types. However, when usingthrow
without specifying an error type, the error message may not be as descriptive or standardized.throw new Error
: Thethrow new Error
statement is specifically used to throw instances of theError
object or its subclasses. It creates a new instance of theError
object and throws it as an exception. This approach provides more standardized error handling and allows for more detailed error messages, stack traces, and additional error properties.
Here’s an example to illustrate the difference:
throw 'Something went wrong'; // Throws a string as an exception
throw new Error('Something went wrong'); // Throws an Error object with a descriptive error message
Using throw new Error
allows you to create more meaningful and standardized error objects with additional properties and stack traces. It is generally recommended to use throw new Error
or custom error types for better error handling and debugging capabilities.
13. How can you clone an object in JavaScript?
In JavaScript, there are several ways to clone an object, each with its own characteristics and considerations. Here are a few common approaches:
- Using the spread operator:
const originalObject = { key: 'value' };
const clonedObject = { ...originalObject };
- Using
Object.assign()
:
const originalObject = { key: 'value' };
const clonedObject = Object.assign({}, originalObject);
- Using
JSON.parse()
andJSON.stringify()
(works for objects without functions or non-serializable values):
const originalObject = { key: 'value' };
const clonedObject = JSON.parse(JSON.stringify(originalObject));
- Using the
lodash.cloneDeep()
method from the Lodash library (for deep cloning):
const originalObject = { key: 'value' };
const clonedObject = _.cloneDeep(originalObject);
It’s important to note that the above approaches create a shallow clone, meaning that the properties are copied by value, but if the object contains nested objects, only references to them will be copied. If you need a deep clone (creating copies of nested objects as well), you can use the JSON method or a dedicated library like Lodash’s cloneDeep()
.
14. What is a JavaScript Generator and how is it different from a regular function?
A JavaScript Generator is a special type of function that can be paused and resumed during its execution. It allows you to control the flow of execution and generate a sequence of values over time.
Here are the key characteristics of Generators:
- Pausing and Resuming: Generators can be paused using the
yield
keyword, which allows them to return a value temporarily and then resume execution from where they left off. This is different from regular functions, which execute from start to finish in one go. - Iterability: Generators are iterables, meaning they can be used in
for...of
loops and other constructs that expect iterables. Eachyield
statement produces a value that can be consumed one at a time. - State Maintenance: Generators maintain their internal state across multiple invocations, allowing them to remember the execution context and resume where they left off.
- Asynchronous Programming: Generators are often used in asynchronous programming to simplify control flow and make async code look more like synchronous code.
Here’s an example to demonstrate a Generator:
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const generator = numberGenerator();
console.log(generator.next().value); // Output: 1
console.log(generator.next().value); // Output: 2
console.log(generator.next().value); // Output: 3
In this example, the numberGenerator()
function is a Generator that yields a sequence of numbers. Each call to generator.next()
returns an object with a value
property representing the yielded value. By repeatedly calling generator.next()
, we can consume the values generated by the Generator.
15. What is the importance of ‘JavaScript Module Pattern’ and when do you use it?
The JavaScript Module Pattern is a design pattern that allows for encapsulation, private data, and the creation of self-contained modules. It provides a way to organize code, prevent naming conflicts, and expose only specific functionality to the outside world.
The key features and benefits of the Module Pattern are:
- Encapsulation: The Module Pattern encapsulates related data and functions into a single module, preventing them from polluting the global scope and avoiding naming collisions with other modules or scripts.
- Privacy and Data Protection: The Module Pattern allows for the creation of private data and methods within a module that are inaccessible from the outside. This helps maintain data integrity and provides better control over module internals.
- Reusability: Modules created using the Module Pattern are highly reusable and can be easily included in different parts of an application without causing conflicts or dependencies.
- Namespacing: Modules provide a way to create namespaces, preventing function and variable clashes between different parts of an application. This improves code organization and helps developers better understand the dependencies and relationships between modules.
16. What is an IIFE ( Immediately Invoked Function Expression) and why might you use it?
An Immediately Invoked Function Expression (IIFE) is a JavaScript function that is declared and executed immediately after it is defined. It is a self-invoking function that does not need to be called explicitly.
The primary reasons to use an IIFE are:
- Encapsulation: IIFEs provide a way to create a private scope for variables and functions within the function, preventing them from polluting the global scope. This helps avoid naming conflicts and provides data privacy.
- Isolation: By executing the function immediately, you can isolate variables and functions within the IIFE, ensuring they do not interfere with other parts of the code.
- Initialization: IIFEs can be used to initialize values, set up configurations, or perform any one-time actions during script execution.
Here’s an example of an IIFE:
(function() {
// IIFE code
var message = 'Hello, IIFE!';
console.log(message);
})();
In this example, the function is defined using an anonymous function expression (function() { ... })
, and the parentheses (function() { ... })()
immediately invoke the function. The code inside the IIFE is executed immediately, creating a private scope and printing the message to the console.
17. What is currying in JavaScript and when might you use this technique?
Currying is a technique in functional programming where a function that takes multiple arguments is transformed into a sequence of functions, each taking a single argument. This allows for partial application of arguments, creating new functions that are specialized versions of the original function.
Here’s an example to illustrate currying in JavaScript:
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(4)); // Output: 8
In the above example, the multiply
function takes the first argument a
and returns a new function that takes the second argument b
. When multiplyByTwo
is invoked with 4
, it multiplies 4
by the original 2
and returns the result 8
.
Currying provides flexibility and reusability in function composition. It allows you to create specialized versions of functions by fixing some arguments upfront and leaving others open for later. This can be useful in scenarios where you have a function with common parameters that you want to reuse across different contexts with varying specific arguments.
Benefits and use cases of currying include:
- Partial application: Currying enables you to create partially applied functions by fixing certain arguments, allowing for code reuse and specialization.
- Reusability: Curried functions can be reused and composed to build more complex functions.
- Modularity: Currying promotes code modularity by breaking down complex functions into smaller, reusable units.
18. What are JavaScript Mixins?
JavaScript mixins are a way to extend the functionality of an object by combining the properties and methods from multiple sources. They allow code reuse and composition without the need for traditional class inheritance.
Mixins can be implemented using object composition or function composition. They provide a mechanism to add new behavior to an object or enhance existing behavior by merging in the properties and methods from other objects or functions.
Here’s an example to illustrate the concept of mixins using object composition:
// Mixin objects
const canEat = {
eat() {
console.log("Eating...");
}
};
const canSleep = {
sleep() {
console.log("Sleeping...");
}
};
// Target object
const cat = {};
// Applying mixins
Object.assign(cat, canEat, canSleep);
// Using the mixed-in methods
cat.eat(); // Output: Eating...
cat.sleep(); // Output: Sleeping...
In the above example, we define two mixin objects: canEat
and canSleep
, each containing specific methods. Then, we create a target object called cat
. By using Object.assign
, we merge the properties and methods from the mixin objects into the cat
object, effectively adding the behaviors of eating and sleeping.
19. Explain the difference between ‘null’ and ‘undefined’.
In JavaScript, null
and undefined
are both values that indicate the absence of a value, but they have different meanings:
null
: It is a value that represents the intentional absence of any object value. It is typically assigned to a variable to indicate that it has no value or that an object property does not exist.undefined
: It is a value that indicates the absence of an assigned value. It is the default value of variables that have been declared but have not been assigned a value, function parameters that have not been provided, or object properties that have not been defined.
20. How does ‘Function.prototype.bind’ work in JavaScript?
The bind()
method in JavaScript is used to create a new function that, when invoked, has a bound this
value and potentially pre-set arguments. It allows you to control the execution context of a function and provides a way to create function references with specific this
values.
When bind()
is called on a function, it returns a new function with the same body and scope as the original function, but with a permanently bound this
value.
Here’s an example to illustrate the usage of bind()
:
const person = {
name: 'John',
greet: function() {
console.log('Hello, ' + this.name + '!');
}
};
const boundGreet = person.greet.bind(person);
boundGreet(); // Output: Hello, John!
In this example, the bind()
method is used to create a new function boundGreet
that is permanently bound to the person
object. When boundGreet()
is invoked, the this
value within the function is set to person
, regardless of how the function is called.
21. What are JavaScript decorators?
JavaScript decorators are a language feature introduced in ECMAScript (ES) standards, specifically ES2015 and later versions. Decorators provide a way to modify the behavior of classes, methods, or properties by wrapping them with higher-order functions called decorators.
Decorators are defined using the @
symbol followed by a function or expression. They are placed immediately before the class, method, or property they intend to modify. When the code is executed, the decorator is invoked and can alter the target object or add additional functionality.
Here’s an example to demonstrate the usage of decorators:
function log(target, name, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${name} with arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${name} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
console.log(calc.add(2, 3)); // Output: Calling add with arguments: 2, 3 // Method add returned: 5 // 5
In the above example, the log
decorator is defined as a higher-order function. It receives the target object, the method name, and a descriptor object. It wraps the original method by modifying the descriptor
object’s value
property. In this case, the decorator adds logging statements before and after the method is invoked.
Decorators can be used for various purposes, such as:
- Logging and debugging
- Adding validation or error checking
- Applying access control or permissions
- Modifying the behavior of methods or properties
- Implementing aspects like caching or memoization
22. Explain how ‘try-catch-finally’ works in JavaScript.
The try-catch-finally
statement in JavaScript is used to handle exceptions and control the flow of execution in error-prone code blocks. It allows you to attempt a block of code that may throw an exception and provides a way to handle and recover from any thrown exceptions.
The basic syntax of try-catch-finally
is as follows:
try {
// Code that might throw an exception
} catch (error) {
// Code to handle the exception
} finally {
// Code that always executes, regardless of whether an exception was thrown
}
Here’s how the try-catch-finally
statement works:
- The code within the
try
block is executed. If an exception is thrown during the execution of this block, the normal flow of execution is immediately transferred to the correspondingcatch
block. - If an exception is thrown, the
catch
block is executed. Thecatch
block takes anerror
parameter that represents the thrown exception. You can use this block to handle or process the exception, log error messages, or take any necessary recovery actions. - The
finally
block is optional. It contains code that is executed regardless of whether an exception was thrown or caught. This block is commonly used to perform cleanup operations, such as closing resources or releasing memory, that need to be executed regardless of the exception handling.
23. Explain the differences between ‘throw’ and ‘throw new Error’.
In JavaScript, both throw
and throw new Error
are used to throw exceptions, but they have slight differences:
throw
: Thethrow
statement is used to throw any value as an exception. It can be used with any type of value, such as strings, numbers, objects, or custom error types. However, when usingthrow
without specifying an error type, the error message may not be as descriptive or standardized.throw new Error
: Thethrow new Error
statement is specifically used to throw instances of theError
object or its subclasses. It creates a new instance of theError
object and throws it as an exception. This approach provides more standardized error handling and allows for more detailed error messages, stack traces, and additional error properties.
Here’s an example to illustrate the difference:
throw 'Something went wrong'; // Throws a string as an exception
throw new Error('Something went wrong'); // Throws an Error object with a descriptive error message
Using throw new Error
allows you to create more meaningful and standardized error objects with additional properties and stack traces. It is generally recommended to use throw new Error
or custom error types for better error handling and debugging capabilities.
24. How can you clone an object in JavaScript?
There are several ways to clone an object in JavaScript, depending on the requirements of the cloning process:
- Shallow Cloning: To create a shallow clone of an object, you can use techniques like the spread operator (
{...obj}
),Object.assign()
, or theArray.from()
method. - Deep Cloning: To create a deep clone that also includes nested objects, you can use techniques like
JSON.parse(JSON.stringify(obj))
, third-party libraries like Lodash’s_.cloneDeep()
, or implement a custom recursive cloning function.
25. What is the importance of ‘JavaScript Module Pattern’ and when do you use it?
The JavaScript Module Pattern is a design pattern that allows for encapsulation, private data, and the creation of self-contained modules. It provides a way to organize code, prevent naming conflicts, and expose only specific functionality to the outside world.
The key features and benefits of the Module Pattern are:
- Encapsulation: The Module Pattern encapsulates related data and functions into a single module, preventing them from polluting the global scope and avoiding naming collisions with other modules or scripts.
- Privacy and Data Protection: The Module Pattern allows for the creation of private data and methods within a module that are inaccessible from the outside. This helps maintain data integrity and provides better control over module internals.
- Reusability: Modules created using the Module Pattern are highly reusable and can be easily included in different parts of an application without causing conflicts or dependencies.
- Namespacing: Modules provide a way to create namespaces, preventing function and variable clashes between different parts of an application. This improves code organization and helps developers better understand the dependencies and relationships between modules.
26. What is an IIFE ( Immediately Invoked Function Expression) and why might you use it?
An Immediately Invoked Function Expression (IIFE) is a JavaScript function that is declared and executed immediately after it is defined. It is a self-invoking function that does not need to be called explicitly.
The primary reasons to use an IIFE are:
- Encapsulation: IIFEs provide a way to create a private scope for variables and functions within the function, preventing them from polluting the global scope. This helps avoid naming conflicts and provides data privacy.
- Isolation: By executing the function immediately, you can isolate variables and functions within the IIFE, ensuring they do not interfere with other parts of the code.
- Initialization: IIFEs can be used to initialize values, set up configurations, or perform any one-time actions during script execution.
Here’s an example of an IIFE:
(function() {
// IIFE code
var message = 'Hello, IIFE!';
console.log(message);
})();
In this example, the function is defined using an anonymous function expression (function() { ... })
, and the parentheses (function() { ... })()
immediately invoke the function. The code inside the IIFE is executed immediately, creating a private scope and printing the message to the console.
27. How do you use ‘apply’, ‘call’, and ‘bind’ methods in JavaScript?
The ‘apply’, ‘call’, and ‘bind’ methods are used to invoke functions with a specific context (the value of this
) and can pass arguments to the function. Here’s how they work:
- apply: The
apply
method allows you to call a function with a specifiedthis
value and an array (or an array-like object) of arguments.
function greet(name) {
console.log(`Hello, ${name}!`);
}
greet.apply(null, ['John']); // Output: Hello, John!
- call: The
call
method is similar toapply
, but instead of passing an array of arguments, you pass them individually as arguments to the function.
function greet(name) {
console.log(`Hello, ${name}!`);
}
greet.call(null, 'John'); // Output: Hello, John!
- bind: The
bind
method returns a new function with a bound context (this
value) and any specified arguments. It allows you to create a function that, when called, will have a specificthis
value and potentially predefined arguments.
function greet(name) {
console.log(`Hello, ${name}!`);
}
const greetJohn = greet.bind(null, 'John');
greetJohn(); // Output: Hello, John!
In these examples, null
is passed as the first argument because the context (this
value) is not relevant in this case.
28. Explain the concept of ‘memoization’ in JavaScript.
‘Memoization’ is a technique in JavaScript used to optimize function performance by caching the results of function calls and reusing them for subsequent identical inputs. It is a form of caching that avoids unnecessary computation by storing function results for specific input arguments.
Here’s a simplified example of memoization:
function memoizedFunction() {
const cache = {};
return function(n) {
if (n in cache) {
return cache[n];
} else {
const result = /* Perform the computation */;
cache[n] = result;
return result;
}
};
}
const memoizedFn = memoizedFunction();
console.log(memoizedFn(5)); // Output: Cached result or computed value for input 5
console.log(memoizedFn(5)); // Output: Cached result from the previous call
In this example, the memoizedFunction
returns a new function that performs a computation and caches the result in the cache
object. If the same input argument is encountered again, the cached result is returned instead of recomputing it. This improves the performance of the function by avoiding redundant computations.
29. What are the different ways to handle asynchronous operations in JavaScript?
There are several ways to handle asynchronous operations in JavaScript:
- Callbacks: Callbacks are a traditional approach where a function accepts a callback function as an argument, which is executed when the asynchronous operation is complete or an error occurs. Callbacks can lead to callback hell and make code harder to read and maintain.
- Promises: Promises were introduced in ES6 and provide a cleaner and more structured way to handle asynchronous operations. Promises represent a single future value or error that will be available at some point. They allow chaining of operations using
.then()
and handling errors using.catch()
. - Async/await: Async/await is a syntax introduced in ES8 (ES2017) that simplifies the handling of asynchronous operations even further. It allows writing asynchronous code in a more synchronous and sequential manner, making it easier to understand and maintain. The
async
keyword is used to define an asynchronous function, and theawait
keyword is used to wait for a promise to resolve before proceeding. - Generators: Generators can be used in combination with Promises to achieve more readable asynchronous code. By using the
yield
keyword inside a generator function, asynchronous operations can be paused and resumed, allowing for a more sequential flow of code.
30. What are JavaScript Promises and how do they work?
JavaScript Promises are objects used for asynchronous programming. They represent a single future value or error that will be available at some point. Promises simplify handling of asynchronous operations and provide a structured way to chain operations and handle errors.
Promises have three states:
- Pending: The initial state. The promise is still being resolved or rejected.
- Fulfilled: The asynchronous operation completed successfully, and the promise is resolved with a value.
- Rejected: An error occurred during the asynchronous operation, and the promise is rejected with a reason (an error object or a value representing the error).
Here’s an example of a Promise that simulates an asynchronous delay and resolves with a value after a specified time:
function delay(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, ms);
});
}
delay(2000)
.then((result) => {
console.log(result); // Output: Done!
})
.catch((error) => {
console.error(error);
});
In this example, the delay
function returns a new Promise. After the specified delay (in milliseconds), the Promise resolves with the value 'Done!'
. The then()
method is used to handle the fulfilled state, and the catch()
method is used to handle any errors that may occur during the asynchronous operation.
Promises can be chained together using the then()
method, allowing for a sequential flow of asynchronous operations. They provide a more structured and readable way to handle asynchronous code compared to traditional callbacks.
31. What is a ‘Set’ in JavaScript and provide an example?
A ‘Set’ in JavaScript is a built-in object that allows storing unique values of any type, whether they are primitive values or object references. A Set can only contain unique values, eliminating duplicates automatically.
Here’s an example of using a Set to store unique values:
const set = new Set();
set.add(1);
set.add('Hello');
set.add(true);
console.log(set.has(1)); // Output: true
console.log(set.size); // Output: 3
set.add(1); // Adding a duplicate value, which will be ignored
console.log(set.size); // Output: 3
set.delete('Hello');
console.log(set.size); // Output: 2
set.clear();
console.log(set.size); // Output: 0
In this example, we create a new Set using the new Set()
syntax. We can then add values to the Set using the add()
method, check if a value exists using the has()
method, get the size of the Set using the size
property, delete values using the delete()
method, and clear all values using the clear()
method.
32. What is a ‘Map’ in JavaScript and provide an example?
A ‘Map’ in JavaScript is a built-in object that allows storing key-value pairs, where both the keys and values can be of any type. Unlike objects, Maps preserve the order of elements and allow any value to be used as a key.
Here’s an example of using a Map to store key-value pairs:
const map = new Map();
const key1 = 'key1';
const key2 = {};
map.set(key1, 'Value 1');
map.set(key2, 'Value 2');
console.log(map.get(key1)); // Output: Value 1
console.log(map.get(key2)); // Output: Value 2
console.log(map.size); // Output: 2
map.delete(key1);
console.log(map.size); // Output: 1
console.log(map.has(key2)); // Output: true
map.clear();
console.log(map.size); // Output: 0
In this example, we create a new Map using the new Map()
syntax. We can then add key-value pairs using the set()
method, retrieve values using the get()
method, check if a key exists using the has()
method, get the size of the Map using the size
property, delete key-value pairs using the delete()
method, and clear all key-value pairs using the clear()
method.
33. What is the significance of ‘async/await’ in JavaScript and how does it work?
‘async/await‘ is a syntax introduced in ES8 (ES2017) for handling asynchronous operations in a more synchronous and readable manner. It provides a way to write asynchronous code that looks and behaves more like synchronous code, making it easier to understand and maintain.
The ‘async‘ keyword is used to define an asynchronous function, which returns a Promise. Inside an ‘async‘ function, the ‘await‘ keyword is used to pause the execution of the function until a Promise is resolved or rejected. This allows sequential execution of asynchronous operations, similar to how synchronous code flows.
Here’s an example that demonstrates the usage of ‘async/await’:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function asyncFunction() {
console.log('Before delay');
await delay(2000);
console.log('After delay');
}
asyncFunction();
In this example, the asyncFunction
is defined as an asynchronous function using the ‘async’ keyword. Inside the function, the await
keyword is used to pause the execution at the await delay(2000)
line until the delay
Promise resolves. This ensures that the ‘After delay’ message is logged after a delay of 2000 milliseconds.
By using ‘async/await‘, you can write asynchronous code in a more readable and sequential manner, without relying on callbacks or chaining promises using .then()
. It simplifies error handling as well, allowing the use of try-catch blocks to handle potential errors within the asynchronous code block.
Advanced Questions
1. How does JavaScript handle automatic type conversion?
JavaScript uses automatic type conversion, also known as type coercion, to convert values from one type to another when performing operations or comparisons. It tries to make the operation valid by converting one or both operands to a compatible type.
Here’s an example:
// Numeric addition
var num = 10;
var str = "5";
var result = num + str;
console.log(result); // "105" (string concatenation)
// Comparison
var num1 = 10;
var num2 = "5";
var isEqual = num1 == num2;
console.log(isEqual); // true (number 5 is converted to a string and compared)
In the first example, the +
operator is used with a number and a string. JavaScript converts the number 10
to a string and performs string concatenation instead of numeric addition.
In the second example, the ==
operator is used to compare a number and a string. JavaScript converts the string "5"
to a number and performs the comparison, considering them equal.
2. What is ‘hoisting’? How does it work?
Hoisting is a JavaScript behavior where variable and function declarations are moved to the top of their containing scope during the compilation phase. This means that you can use variables and functions before they are declared in the code.
Here’s an example:
console.log(num); // undefined
var num = 5;
hoistedFunction(); // "This is a hoisted function."
function hoistedFunction() {
console.log("This is a hoisted function.");
}
In this example, the variable num
and the function hoistedFunction
are referenced before their declarations. However, due to hoisting, the code is interpreted as if the declarations were moved to the top of their scope.
The console.log(num)
statement outputs undefined
because the variable is declared but not yet assigned a value. The function hoistedFunction
can be called before its actual declaration, and it prints the expected message.
3. What is a closure? Can you provide an example of a closure in JavaScript?
A closure is a combination of a function and the lexical environment within which it was declared. It allows a function to access variables from an outer function, even after the outer function has finished executing.
Here’s an example:
function outerFunction() {
var outerVariable = "I'm from the outer function.";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
var closure = outerFunction();
closure(); // "I'm from the outer function."
In this example, the outerFunction
defines a variable outerVariable
and an inner function innerFunction
. The innerFunction
has access to the outerVariable
due to closure. When outerFunction
is called and returns the innerFunction
, the variable outerVariable
is still accessible and can be accessed by invoking closure()
.
4. What is the event loop in JavaScript? How does it work?
The event loop is a mechanism in JavaScript that handles asynchronous operations and ensures that the execution of code continues smoothly. It enables JavaScript to be single-threaded while handling concurrent tasks asynchronously.
Here’s an example:
console.log("Start");
setTimeout(function () {
console.log("Inside setTimeout");
}, 0);
Promise.resolve().then(function () {
console.log("Inside Promise");
});
console.log("End");
In this example, the code first logs "Start"
and "End"
synchronously. Then, it schedules two asynchronous operations: a setTimeout
callback and a Promise
callback.
The event loop continuously checks if there are any pending tasks in the event queue. In this case, the setTimeout
callback and the Promise
callback are added to the event queue after their specified delay or when the promise is resolved.
After the synchronous code finishes executing, the event loop processes the pending tasks in the event queue. The setTimeout
callback and the Promise
callback are executed, resulting in the output:
Start
End
Inside Promise
Inside setTimeout
5. What are the pros and cons of using Promises instead of callbacks?
Promises provide a more structured and elegant way to handle asynchronous operations compared to traditional callbacks. Here are some pros and cons:
Pros of Promises:
- Promotes better code organization with a clear separation of concerns.
- Allows chaining multiple asynchronous operations using
then
method. - Provides better error handling with the
catch
method. - Enables easier conversion of callback-based code to Promise-based code.
Cons of Promises:
- Requires familiarity with the Promise API.
- Can lead to excessive nesting of
then
callbacks when multiple asynchronous operations are involved. - Not supported in older versions of JavaScript (prior to ES6), requiring polyfills or transpilers.
Here’s an example comparing a callback-based approach to a Promise-based approach:
Callback-based approach:
function asyncOperation(callback) {
setTimeout(function () {
callback(null, "Data");
}, 1000);
}
asyncOperation(function (err, data) {
if (err) {
console.error("Error:", err);
} else {
console.log("Data:", data);
}
});
Promise-based approach:
function asyncOperation() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("Data");
}, 1000);
});
}
asyncOperation()
.then(function (data) {
console.log("Data:", data);
})
.catch(function (err) {
console.error("Error:", err);
});
In the callback-based approach, the asynchronous operation is passed a callback function to handle the result or error. In the Promise-based approach, the asyncOperation
function returns a Promise that can be chained with then
and catch
methods, making the code more readable and maintainable.
6. What is the difference between a ‘shallow copy’ and a ‘deep copy’ of an object?
Shallow Copy | Deep Copy | |
---|---|---|
1. | Copies the references of nested objects | Creates a completely independent copy |
2. | Modifying nested objects affects the original object | Modifying nested objects doesn’t affect the original object |
3. | Copying is performed on the top-level only | Copying is performed recursively for all levels |
4. | Objects share the same memory | Objects have separate memory |
5. | Less memory consumption | More memory consumption |
6. | Faster to perform | Slower to perform |
7. Explain the concept of prototypical inheritance. How is it different from classical inheritance?
Prototypical inheritance is a way of creating objects where an object inherits properties and methods directly from another object. In JavaScript, every object has an internal property called [[Prototype]]
, which refers to its parent object or “prototype”. If a property or method is not found on the object itself, it is automatically looked up in its prototype.
When an object is used as a prototype for another object, the second object inherits all the properties and methods from the prototype. This allows for code reuse and the creation of object hierarchies.
The main difference between prototypical inheritance and classical inheritance is the way inheritance is achieved. In classical inheritance, classes are defined, and objects are instances of these classes. In prototypical inheritance, objects are used as prototypes for other objects directly, without the need for classes.
8. What are ES6 classes? How do they differ from function constructors?
ES6 classes are a syntactic sugar over the existing prototype-based inheritance model in JavaScript. They provide a more familiar class-based syntax for creating objects and defining their behavior.
Here’s an example of an ES6 class and its equivalent using a function constructor:
ES6 class:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
}
const john = new Person("John");
john.sayHello();
Function constructor:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, my name is " + this.name + ".");
};
var john = new Person("John");
john.sayHello();
Both the ES6 class and the function constructor achieve the same result. However, the class syntax provides a more intuitive and concise way to define classes and their methods, making the code easier to read and maintain.
9. What are the differences between ‘rest’ and ‘spread’ syntax?
The ‘rest’ and ‘spread’ syntax are two features introduced in ES6 that use the same ...
notation but serve different purposes:
Rest Syntax: It allows gathering multiple elements into an array.
Example:
function sum(...numbers) {
return numbers.reduce((acc, curr) => acc + curr, 0);
}
console.log(sum(1, 2, 3, 4)); // Output: 10
In this example, the rest syntax (...numbers
) gathers all the arguments passed to the sum
function into an array called numbers
.
Spread Syntax: It allows expanding an array or iterable into individual elements.
Example:
const numbers = [1, 2, 3, 4];
console.log(...numbers); // Output: 1 2 3 4
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const mergedArray = [...arr1, ...arr2];
console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
In the first example, the spread syntax is used to expand the numbers
array into individual elements when passed to console.log()
. It prints each element separately.
In the second example, the spread syntax is used to merge two arrays (arr1
and arr2
) into a new array called mergedArray
.
10. How does ‘destructuring assignment’ work?
Destructuring assignment is a feature in JavaScript that allows extracting values from objects or arrays into distinct variables, making it easier to access and work with the data.
Example using object destructuring:
const person = {
name: "John",
age: 30,
city: "New York",
};
const { name, age, city } = person;
console.log(name, age, city); // Output: John 30 New York
In this example, the object person
has properties name
, age
, and city
. By using object destructuring, we can extract those properties into separate variables with the same names as the properties.
Example using array destructuring:
const numbers = [1, 2, 3, 4];
const [first, second, ...rest] = numbers;
console.log(first, second, rest); // Output: 1 2 [3, 4]
In this example, the array numbers
contains elements [1, 2, 3, 4]
. Using array destructuring, we can assign the first element to the variable first
, the second element to second
, and the rest of the elements to the array rest
.
11. What are JavaScript Symbols? How and why would you use them?
JavaScript Symbols are a new primitive type introduced in ES6. They are unique and immutable, meaning that each Symbol value is distinct and cannot be changed.
Symbols are often used as property keys for object properties to ensure uniqueness and avoid naming conflicts. They can also be used to define well-known symbols or create private members in objects.
Example:
const sym1 = Symbol("description");
const sym2 = Symbol("description");
console.log(sym1 === sym2); // Output: false
const obj = {
[sym1]: "value",
};
console.log(obj[sym1]); // Output: "value"
In this example, two symbols sym1
and sym2
are created with the same description. However, they are distinct values, and sym1 === sym2
returns false
.
The symbol sym1
is used as a property key in the object obj
, allowing access to the associated value using bracket notation (obj[sym1]
).
12. How does the ‘this’ keyword work in JavaScript? How is it different in arrow functions? give code example
In JavaScript, the this
keyword refers to the context in which a function is executed. It can behave differently based on how the function is called.
In a regular function, the value of this
is determined by how the function is invoked. It can vary based on the function’s execution context, whether it is called as a method, as a constructor, or with the call
or apply
methods.
Example:
function greet() {
console
.log(`Hello, ${this.name}!`);
}
const person = {
name: "John",
greet: greet,
};
person.greet(); // Output: "Hello, John!"
In this example, this
inside the greet
function refers to the person
object because the function is called as a method of person
.
Example:
const person = {
name: "John",
greet: function () {
setTimeout(() => {
console.log(`Hello, ${this.name}!`);
}, 1000);
},
};
person.greet(); // Output: "Hello, John!"
In this example, the arrow function inside the setTimeout
function retains the this
value of the greet
method, which is the person
object. Therefore, it correctly accesses the name
property and prints "Hello, John!"
after a delay of 1 second.
13. What is a pure function in JavaScript? give code example
A pure function in JavaScript is a function that always produces the same output for the same input and has no side effects. It does not modify any external state or rely on external mutable data.
Pure functions have the following characteristics:
- The return value solely depends on the function’s arguments.
- The function does not modify any mutable state or variables outside its scope.
- The function does not perform any I/O operations or produce side effects.
Example of a pure function:
function add(a, b) {
return a + b;
}
console.log(add(3, 5)); // Output: 8
console.log(add(3, 5)); // Output: 8 (same output for the same input)
In this example, the add
function takes two arguments and returns their sum. It does not rely on any external state and consistently produces the same output for the same input. There are no side effects or modifications to any variables outside its scope.
14. What are the different ways to handle asynchronous operations in JavaScript?
JavaScript provides several ways to handle asynchronous operations:
- Callbacks: Callback functions are passed as arguments to asynchronous functions and called when the operation completes or an error occurs.
Example:
function fetchData(callback) {
// Asynchronous operation
setTimeout(function () {
callback(null, "Data");
}, 1000);
}
fetchData(function (err, data) {
if (err) {
console.error("Error:", err);
} else {
console.log("Data:", data);
}
});
- Promises: Promises provide a more structured approach to handle asynchronous operations. They represent the eventual completion or failure of an asynchronous operation and allow chaining of multiple operations.
Example:
function fetchData() {
return new Promise(function (resolve, reject) {
// Asynchronous operation
setTimeout(function () {
resolve("Data");
}, 1000);
});
}
fetchData()
.then(function (data) {
console.log("Data:", data);
})
.catch(function (err) {
console.error("Error:", err);
});
- Async/await: Async/await is a syntactic sugar on top of Promises. It allows writing asynchronous code in a synchronous-like manner, making it easier to read and understand.
Example:
async function fetchData() {
return new Promise(function (resolve, reject) {
// Asynchronous operation
setTimeout(function () {
resolve("Data");
}, 1000);
});
}
async function getData() {
try {
const data = await fetchData();
console.log("Data:", data);
} catch (err) {
console.error("Error:", err);
}
}
getData();
- Observables: Observables provide a powerful way to handle asynchronous and event-based programming. They allow subscribing to a stream of events or data and provide a wide range of operators to transform and manipulate the data.
Example using RxJS library:
import { Observable } from 'rxjs';
function fetchData() {
return new Observable(function (observer) {
// Asynchronous operation
setTimeout(function () {
observer.next("Data");
observer.complete();
}, 1000);
});
}
fetchData().subscribe({
next: function (data) {
console.log("Data:", data);
},
error: function (err) {
console.error("Error:", err);
},
complete: function () {
console.log("Completed");
},
});
15. Explain the differences between ‘map’, ‘forEach’, and ‘reduce’.
map
, forEach
, and reduce
are methods available for arrays in JavaScript to iterate over their elements and perform operations. Here are the differences between them:
forEach
: It executes a provided function once for each element in the array and does not return a new array. It is primarily used for its side effects.
Example:
const array = [1, 2, 3];
array.forEach(function (element) {
console.log(element);
});
map
: It creates a new array by calling a provided function on each element of the original array. It returns a new array with the transformed values.
Example:
const array = [1, 2, 3];
const mappedArray = array.map(function (element) {
return element * 2;
});
console.log(mappedArray); // Output: [2, 4, 6]
reduce
: It reduces the array to a single value by applying a provided function that accumulates the result. It takes an initial value and performs the reduction operation from left to right.
Example:
const array = [1, 2, 3];
const sum = array.reduce(function (accumulator, element) {
return accumulator + element;
}, 0);
console.log(sum); // Output: 6
The main differences between map
and forEach
are that map
returns a new array, whereas forEach
does not. Additionally, reduce
reduces the array to a single value by iteratively applying the provided function and accumulating the result.
16. What is the difference between ‘null’ and ‘undefined’?
Property | null | undefined |
---|---|---|
1. | It is an assignment value that represents the absence of an object value | It is a variable that has been declared but has not been assigned a value |
2. | Must be assigned explicitly to a variable | Automatically assigned by JavaScript |
3. | Represents an intentional absence of any object value | Represents an unintentional absence of value |
4. | typeof null returns “object” | typeof undefined returns “undefined” |
5. | Can be assigned to a variable | Cannot be assigned explicitly |
17. What are JavaScript Observables and how do they differ from Promises?
JavaScript Observables are a way to handle asynchronous and event-based programming, similar to Promises. They represent a stream of values or events that can be observed by subscribing to them.
Here’s an example using the RxJS library to create an Observable:
import { Observable } from 'rxjs';
const observable = new Observable(function (observer) {
observer.next("Data 1");
observer.next("Data 2");
observer.next("Data 3");
observer.complete();
});
observable.subscribe({
next: function (data) {
console.log("Data:", data);
},
error: function (err) {
console.error("Error:", err);
},
complete: function () {
console.log("Completed");
},
});
In this example, an Observable is created that emits three values ("Data 1"
, "Data 2"
, "Data 3"
) and then completes. The subscribe
method is used to observe the emitted values. The subscriber object defines three handlers: next
to handle each emitted value, error
to handle any errors, and complete
to handle the completion of the Observable.
18. What is a JavaScript Proxy and what is it used for?
A JavaScript Proxy is an object that wraps another object and intercepts fundamental operations, allowing custom behavior to be defined for these operations. Proxies enable the creation of objects with modified behavior or enhanced functionality.
Example:
const target = {
name: "John",
age: 30,
};
const handler = {
get: function (target, prop) {
console.log(`Accessed property: ${prop}`);
return target[prop];
},
set: function (target, prop, value) {
console.log(`Set property: ${prop} = ${value}`);
target[prop] = value;
return true;
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Output: "John" (get operation intercepted)
proxy.age = 31; // Output: "Set property: age = 31" (set operation intercepted)
console.log(proxy.age); // Output: 31 (get operation intercepted)
In this example, a Proxy is created for the target
object. The handler
object defines the behavior for the get
and set
operations. When a property is accessed (proxy.name
) or set (proxy.age = 31
), the corresponding handler methods are invoked, allowing custom logic to be executed.
19. Explain the concept of ‘memoization’ in JavaScript.
Memoization is a technique used to optimize functions by caching the results of expensive function calls and returning the cached result when the same inputs occur again. It helps to avoid redundant computations and improve performance.
Example:
function fibonacci(n, cache = {}) {
if (n in cache) {
return cache[n];
}
if (n <= 1) {
return n;
}
const result = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
cache[n] = result;
return result;
}
console.log(fibonacci(10)); // Output: 55
In this example, the fibonacci
function calculates the Fibonacci sequence recursively. The cache
object is used to store previously computed values. When the function is called with the same n
value, it checks if the result is already in the cache and returns it directly, avoiding redundant calculations.
20. What is currying in JavaScript and why might you use this technique?
Currying is a technique in JavaScript that involves transforming a function with multiple arguments into a sequence of functions, each taking a single argument. It allows partial application of a function by fixing some of its arguments in advance.
The benefits of currying include:
- Increased reusability and flexibility of functions.
- Facilitates function composition.
- Enables the creation of specialized functions from more general ones.
Example:
function add(a) {
return function (b) {
return a + b;
};
}
const addFive = add(5);
console.log(addFive(3)); // Output: 8
console.log(addFive(7)); // Output: 12
In this example, the add
function takes a single argument a
and returns an inner function that takes another argument b
. The inner function adds a
and b
together. By calling add(5)
, we create a new function addFive
that adds 5
to its argument. This allows us to reuse the addFive
function multiple times with different values of b
.
21. What is function composition in JavaScript?
Function composition is a technique in JavaScript where multiple functions are combined to create a new function that executes the composed functions in a specific order. The output of one function becomes the input of the next function, forming a pipeline of data transformations.
Example:
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function subtract(a, b) {
return a - b;
}
const compose = (...functions) => (initialValue) =>
functions.reduce((acc, fn) => fn(acc), initialValue);
const calculation = compose(
(value) => add(value, 5),
(value) => multiply(value, 2),
(value) => subtract(value, 10)
);
console.log(calculation(20)); // Output: 55
In this example, the functions add
, multiply
, and subtract
perform basic arithmetic operations. The compose
function takes an array of functions and returns a new function that applies each function in the array to an initial value. The result is a composition of functions that calculates 20 + 5
, then multiplies the result by 2
, and finally subtracts 10
, resulting in 55
.
22. What is the purpose of JavaScript’s built-in ‘apply’, ‘call’, and ‘bind’ methods?
The built-in methods apply
, call
, and bind
in JavaScript are used to control the value of this
in a function and to invoke functions with a specified this
value.
apply
: Calls a function with a giventhis
value and an array-like or iterable object as the arguments.
Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}!`);
}
const person = {
name: "John",
};
greet.apply(person, ["Hello"]); // Output: "Hello, John!"
call
: Calls a function with a giventhis
value and individual arguments.
Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}!`);
}
const person = {
name: "John",
};
greet.call(person, "Hello"); // Output: "Hello, John!"
bind
: Returns a new function with a giventhis
value and pre-filled arguments.
Example:
function greet(greeting) {
console.log(`${greeting}, ${this.name}!`);
}
const person = {
name: "John",
};
const greetPerson = greet.bind(person);
greetPerson("Hello"); // Output: "Hello, John!"
In all three cases, the methods allow explicit control over the value of this
inside a function. apply
and call
invoke the function immediately, while bind
returns a new function that can be called later.
23. What are JavaScript Generators and how do they work?
JavaScript Generators are special functions that can be paused and resumed during their execution. They provide a way to generate a sequence of values over time, allowing for more efficient memory usage and better control flow.
Generators are defined using the function*
syntax and use the yield
keyword to pause the function and return a value. The generator function can be iterated using a for...of
loop or by manually calling the next
method on the generator object.
Example:
function* countTo(limit) {
for (let i = 1; i <= limit; i++) {
yield i;
}
}
const generator = countTo(5);
console.log(generator.next()); // Output: { value: 1, done: false }
console.log(generator.next()); // Output: { value: 2, done: false }
console.log(generator.next()); // Output: { value: 3, done: false }
console.log(generator.next()); // Output: { value: 4, done: false }
console.log(generator.next()); // Output: { value: 5, done: false }
console.log(generator.next()); // Output: { value: undefined, done: true }
In this example, the countTo
generator function generates a sequence of numbers from 1
to the specified limit
. Each time the yield
statement is encountered, the function pauses and returns the current value. The generator object is created by calling the generator function, and the next
method is used to iterate through the sequence of values.
24. What is the difference between JavaScript’s window, document, and screen objects?
window
: Thewindow
object represents the browser window or tab that contains the JavaScript code. It provides properties and methods to interact with the browser window, handle events, set timers, and manage the document.document
: Thedocument
object represents the web page loaded in the browser window. It provides properties and methods to access and manipulate the elements, content, and structure of the document.screen
: Thescreen
object represents the user’s screen or display. It provides properties to get information about the user’s screen, such as width, height, pixel density, and color depth.
25. What are JavaScript decorators and what are they used for?
JavaScript decorators are a feature introduced in ES2016/ES7 that allow modifying the behavior of classes, methods, and properties using a special syntax. They are essentially higher-order functions that wrap or annotate the target with additional functionality.
Decorators are prefixed with the @
symbol and can be used to implement cross-cutting concerns, such as logging, caching, validation, or authorization.
Example:
function log(target, name, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
console.log(`Calling ${name} with arguments:`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
const calculator = new Calculator();
console.log(calculator.add(2, 3)); // Output: "Calling add with arguments: [2, 3]" 5
In this example, the log
decorator is defined as a function that modifies the behavior of the add
method in the Calculator
class. It logs the method name and arguments before invoking the original method.
26. How does JavaScript’s ‘try-catch-finally’ construct work?
The try-catch-finally
construct in JavaScript allows handling exceptions and executing code in the presence or absence of exceptions. It provides a way to handle errors and gracefully recover from them.
The try
block contains the code that may throw an exception. If an exception occurs within the try
block, the control is transferred to the corresponding catch
block. The catch
block handles the exception by specifying an identifier to hold the error object.
The finally
block is optional and always executes, regardless of whether an exception occurred or not. It is commonly used to perform cleanup tasks, such as closing resources or releasing memory, regardless of the outcome of the try-catch block.
Example:
try {
// Code that may throw an exception
throw new Error("Something went wrong!");
} catch (error) {
// Handling the exception
console.error(error);
} finally {
// Cleanup or other tasks
console.log("Finally block executed");
}
In this example, the try
block intentionally throws an Error
object. The catch
block captures the error and logs it to the console. Finally, the finally
block is executed, printing the message “Finally block executed” to the console.
27. What are the principles of Object-Oriented Programming (OOP) in JavaScript?
The principles of Object-Oriented Programming (OOP) in JavaScript are:
- Encapsulation: The bundling of related data and methods into objects, and the protection of data from direct access by using access modifiers.
- Inheritance: The ability of objects to inherit properties and methods from a parent object, allowing code reuse and creating hierarchies of objects.
- Polymorphism: The ability of objects to take on multiple forms and exhibit different behavior based on their type or the context in which they are used.
- Abstraction: The process of simplifying complex systems by breaking them down into smaller, more manageable parts. It involves hiding unnecessary details and exposing only the essential information.
- Composition: The building of complex objects or behaviors by combining simpler objects or behaviors. It promotes code reuse and modular design.
28. What is the purpose of the ‘use strict’ directive in JavaScript?
The 'use strict'
directive is used to enable strict mode in JavaScript. It is a way to opt into a restricted variant of JavaScript that enforces stricter rules and prevents certain common mistakes.
Enabling strict mode has several benefits:
- Variables must be declared with
var
,let
, orconst
. Implicit global variables are not allowed, reducing the risk of accidental global scope pollution. - Assigning a value to an undeclared variable, deleting variables or functions, or using restricted keywords (such as
eval
orarguments
) raises an error. - Duplicate parameter names in function declarations raise an error.
this
is undefined in functions that are not methods or constructors, preventing accidental usage of the global object as the context.
29. What are JavaScript mixins? How are they used and what are some potential pitfalls?
JavaScript mixins are a way to enhance the functionality of an object by combining multiple sets of properties and methods from different sources. Mixins provide a mechanism for code reuse and composition, allowing objects to inherit behavior from multiple mixins.
A mixin is typically an object that contains a set of methods or properties. These methods or properties can be copied or merged into the target object using various techniques such as Object.assign()
, Object.setPrototypeOf()
, or by extending the object with a mixin class.
Example:
const sayHelloMixin = {
sayHello() {
console.log(`Hello, ${this.name}!`);
},
};
class Person {
constructor(name) {
this.name = name;
}
}
Object.assign(Person.prototype, sayHelloMixin);
const john = new Person("John");
john.sayHello(); // Output: "Hello, John!"
In this example, the sayHelloMixin
object contains the sayHello
method. The method is mixed into the Person
class by assigning it to the Person.prototype
object using Object.assign()
. As a result, instances of the Person
class can now invoke the sayHello
method.
Potential pitfalls of mixins include:
- Naming conflicts: If two mixins define methods or properties with the same name, there can be conflicts and unintended behavior.
- Dependency management: Managing dependencies between mixins can become complex, especially when multiple mixins depend on each other or override the same methods.
- Inheritance limitations: Mixins do not provide true inheritance, as they copy or merge properties and methods into an object rather than creating a prototype chain. This can lead to limitations in method overriding or accessing the mixin’s state.
30. What are the differences between ES5 and ES6, and what new features were introduced in ES6?
ES5 (ECMAScript 5) and ES6 (ECMAScript 2015, also known as ES2015) are two versions of the ECMAScript standard, which is the specification for JavaScript.
Differences between ES5 and ES6:
- Syntax enhancements: ES6 introduces new syntax features like
let
andconst
for block-scoped variables, arrow functions, template literals, and destructuring assignment. - Classes: ES6 introduces the
class
syntax for defining classes, making it easier to create objects with inheritance and encapsulation. - Modules: ES6 introduces native support for modules with the
import
andexport
keywords, enabling better code organization and dependency management. - Arrow functions: ES6 arrow functions provide a concise syntax and lexical scoping for defining functions.
- Promises: ES6 introduces native support for Promises, simplifying asynchronous programming and providing better error handling and composition of asynchronous operations.
- Generators: ES6 introduces generator functions that can pause and resume their execution, allowing for more efficient asynchronous programming and iterators.
- Enhanced object literals: ES6 provides enhancements to object literals, such as shorthand syntax for defining methods, computed property names, and property value shorthands.
- Iterators and for…of loop: ES6 introduces the
Symbol.iterator
interface and thefor...of
loop, making it easier to iterate over data structures like arrays, strings, and custom iterables.
Coding Questions
1. Find the Length of a String
function findStringLength(str) {
return str.length;
}
2. Reverse a String
function reverseString(str) {
return str.split('').reverse().join('');
}
3. Factorialize a Number
function factorialize(num) {
if (num === 0 || num === 1) {
return 1;
}
let result = 1;
for (let i = 2; i <= num; i++) {
result *= i;
}
return result;
}
4. Check for Palindromes
function isPalindrome(str) {
str = str.toLowerCase().replace(/[^a-z0-9]/g, '');
const reversedStr = str.split('').reverse().join('');
return str === reversedStr;
}
5. Find the Longest Word in a String
function findLongestWord(str) {
const words = str.split(' ');
let longestWord = '';
for (let i = 0; i < words.length; i++) {
if (words[i].length > longestWord.length) {
longestWord = words[i];
}
}
return longestWord.length;
}
6. Title Case a Sentence
function titleCase(str) {
const words = str.toLowerCase().split(' ');
for (let i = 0; i < words.length; i++) {
words[i] = words[i][0].toUpperCase() + words[i].slice(1);
}
return words.join(' ');
}
7. Return Largest Numbers in Arrays
function largestNumbers(arr) {
const largestArr = [];
for (let i = 0; i < arr.length; i++) {
let largestNum = arr[i][0];
for (let j = 1; j < arr[i].length; j++) {
if (arr[i][j] > largestNum) {
largestNum = arr[i][j];
}
}
largestArr.push(largestNum);
}
return largestArr;
}
8. Confirm the Ending
function confirmEnding(str, target) {
return str.endsWith(target);
}
9. Finders Keepers
function findElement(arr, func) {
for (let i = 0; i < arr.length; i++) {
if (func(arr[i])) {
return arr[i];
}
}
return undefined;
}
10. Arguments Optional
function addTogether(a, b) {
if (typeof a !== 'number') {
return undefined;
}
if (b === undefined) {
return function (c) {
if (typeof c !== 'number') {
return undefined;
}
return a + c;
};
}
if (typeof b !== 'number') {
return undefined;
}
return a + b;
}
MCQ Questions
1. What is JavaScript?
a) A scripting language used for client-side web development
b) A programming language used for server-side application development
c) A markup language used for designing web pages
d) A database management language
Answer: a) A scripting language used for client-side web development
2. What is the typeof operator used for in JavaScript?
a) Determining the type of a variable
b) Converting a value to a specific type
c) Checking if a variable is defined
d) Assigning a value to a variable
Answer: a) Determining the type of a variable
3. What is the output of the following code snippet?
console.log(1 + "2" + "3");
a) 123
b) 15
c) “123”
d) “15”
Answer: c) “123”
4. Which keyword is used to declare a variable in JavaScript?
a) var
b) let
c) const
d) All of the above
Answer: d) All of the above
5. What is the result of the following expression?
typeof NaN
a) “number”
b) “NaN”
c) “undefined”
d) “NaN”
Answer: a) “number”
6. Which of the following is not a valid JavaScript data type?
a) boolean
b) number
c) character
d) string
Answer: c) character
7. What is the result of the following expression?
"5" - 2
a) 3
b) “3”
c) 7
d) “7”
Answer: a) 3
8. What is the purpose of the “this” keyword in JavaScript?
a) It refers to the current HTML element
b) It refers to the current JavaScript file
c) It refers to the current function
d) It refers to the current object
Answer: d) It refers to the current object
9. What is the output of the following code snippet?
console.log(2 + 3 + "5");
a) 10
b) “55”
c) “235”
d) 25
Answer: b) “55”
10. Which of the following is a correct way to define a function in JavaScript?
a) function = myFunction() {}
b) function myFunction() {}
c) var myFunction() = function() {}
d) var myFunction = () => {}
Answer: b) function myFunction() {}
11. What is the result of the following expression?
3 > 2 > 1
a) true
b) false
c) SyntaxError
d) TypeError
Answer: b) false
12. How do you comment a single line in JavaScript?
a) // This is a comment
b) / This is a comment
c) /* This is a comment */
d) % This is a comment %
Answer: a) // This is a comment
13. What is the output of the following code snippet?
console.log("2" * "3");
a) 5
b) 6
c) “6”
d) “23”
Answer: b) 6
14. Which of the following is not a JavaScript loop statement?
a) for
b) while
c) loop
d) do…while
Answer: c) loop
15. What is the result of the following expression?
null == undefined
a) true
b) false
c) SyntaxError
d) TypeError
Answer: a) true
16. How do you declare a JavaScript array?
a) var arr = [];
b) var arr = new Array();
c) Both a and b are correct
d) None of the above
Answer: c) Both a and b are correct
17. What is the output of the following code snippet?
console.log(10 === "10");
a) true
b) false
c) SyntaxError
d) TypeError
Answer: b) false
18. What is the purpose of the “break” statement in JavaScript?
a) It terminates the current loop or switch statement
b) It skips the current iteration of a loop and continues with the next iteration
c) It defines a block of code to be executed if a specific condition is true
d) It defines a function in JavaScript
Answer: a) It terminates the current loop or switch statement
19. What is the output of the following code snippet?
console.log("Hello" + 5);
a) “Hello5”
b) “Hello”
c) 10
d) SyntaxError
Answer: a) “Hello5”
20. Which of the following is not a valid JavaScript comparison operator?
- a) ==
- b) !=
- c) <=
- d) ><
Answer: d) ><
21. What is the output of the following code snippet?
console.log(typeof typeof 1);
a) “number”
b) “string”
c) “object”
d) “undefined”
Answer: b) “string”
22. How do you access the length of a JavaScript array?
a) arr.length()
b) arr.length
c) arr.size()
d) arr.size
Answer: b) arr.length
23. What is the result of the following expression?
true + true
a) 2
b) true
c) false
d) NaN
Answer: a) 2
24. How do you check if a variable is an array in JavaScript?
a) typeof arr === “array”
b) arr.isArray()
c) Array.isArray(arr)
d) arr instanceof Array
Answer: c) Array.isArray(arr)
25. What is the output of the following code snippet?
console.log("5" + 2 - 1);
a) 52
b) “521”
c) 6
d) NaN
Answer: d) NaN
26. Which of the following is a correct way to create a JavaScript object?
a) var obj = new Object();
b) var obj = {};
c) Both a and b are correct
d) None of the above
Answer: c) Both a and b are correct
27. What is the output of the following code snippet?
console.log(1 + true);
a) 1
b) true
c) 2
d)NaN
Answer: c) 2
28. How do you convert a string to a number in JavaScript?
a) parseInt(str)
b) parseFloat(str)
c) Number(str)
d) All of the above
Answer: d) All of the above
29. What is the output of the following code snippet?
console.log(10 % 3);
a) 1
b) 3
c) 0
d) 10
Answer: a) 1
30. What is the purpose of the “continue” statement in JavaScript?
a) It terminates the current loop or switch statement
b) It skips the current iteration of a loop and continues with the next iteration
c) It defines a block of code to be executed if a specific condition is true
d) It defines a function in JavaScript
Answer: b) It skips the current iteration of a loop and continues with the next iteration
31. What is the output of the following code snippet?
console.log("5" - 2);
a) 3
b) “3”
c) 7
d) “7”
Answer: a) 3
32. How do you declare a JavaScript function?
a) function myFunction() {}
b) var myFunction = function() {}
c) () => {}
d) All of the above
Answer: d) All of the above
33. What is the output of the following code snippet?
console.log(10 / 0);
a) 0
b) Infinity
c) NaN
d) SyntaxError
Answer: b) Infinity
34. What is the purpose of the “typeof” operator in JavaScript?
a) It returns the data type of a variable
b) It checks if a variable is defined
c) It converts a value to a specific type
d) It assigns a value to a variable
Answer: a) It returns the data type of a variable
35. What is the output of the following code snippet?
console.log(10 == "10");
a) true
b) false
c) SyntaxError
d) TypeError
Answer: a) true
36. How do you concatenate two strings in JavaScript?
a) str1.add(str2)
b) str1.concat(str2)
c) str1 + str2
d) All of the above
Answer: c) str1 + str2
37. What is the output of the following code snippet?
console.log(10 > 9 > 8);
a) true
b) false
c) SyntaxError
d) TypeError
Answer: b) false
38. How do you round a number to the nearest integer in JavaScript?
a) Math.floor(num)
b) Math.ceil(num)
c) Math.round(num)
d) All of the above
Answer: c) Math.round(num)
39. What is the output of the following code snippet?
console.log(0.1 + 0.2 == 0.3);
a) true
b) false
c) SyntaxError
d) TypeError
Answer: b) false
40. How do you convert a number to a string in JavaScript?
a) num.toString()
b) String(num)
c) “” + num
d) All of the above
Answer: d) All of the above
41. What is the output of the following code snippet?
console.log(Math.max(1, 2, 3));
a) 1
b) 2
c) 3
d) SyntaxError
Answer: c) 3
42. How do you get the current date and time in JavaScript?
a) Date.now()
b) new Date()
c) getCurrentDate()
d) All of the above
Answer: b) new Date()
43. What is the output of the following code snippet?
console.log(10 ** 2);
a) 10
b) 20
c) 100
d) 102
Answer: c) 100
44. How do you convert a string to uppercase in JavaScript?
a) str.toUpperCase()
b) str.lowerCase()
c) str.toUpper()
d) All of the above
Answer: a) str.toUpperCase()
45. What is the output of the following code snippet?
console.log(Math.floor(4.7));
a) 4
b) 5
c) 4.7
d) SyntaxError
Answer: a) 4
46. How do you check if a string contains a specific substring in JavaScript?
a) str.includes(substring)
b) str.contains(substring)
c) str.indexOf(substring)
d) All of the above
Answer: a) str.includes(substring)
47. What is the output of the following code snippet?
console.log(typeof [1, 2, 3]);
a) “array”
b) “object”
c) “undefined”
d) “null”
Answer: b) “object”
48. How do you convert a string to lowercase in JavaScript?
a) str.toLowerCase()
b) str.upperCase()
c) str.toLower()
d) All of the above
Answer: a) str.toLowerCase()