== checks for equality of values, but it performs type coercion, which means it may convert the operands to the same type before making the comparison. For example, 0 == false would return true because both are coerced to a boolean value.
=== also checks for equality of values, but it does not perform type coercion. It compares both the values and the data types. This means 0 === false would return false because they are of different types.
== checks for equality after type coercion. === checks for equality without type coercion.
-
let
is used to declare block-scoped variables. Variables declared withlet
are not hoisted, meaning they are not accessible before the line of code where they are declared. -
const
also declares block-scoped variables. Variables declared withconst
cannot be reassigned after they are initialized, but the value they hold can still be mutated if it is mutable (e.g., an array or object). -
var
declares variables with function scope (or globally scoped if declared outside of a function). Variables declared withvar
are hoisted, meaning they are accessible throughout the entire function or global scope, regardless of where they are declared within that scope.
The event loop is a fundamental concept in JavaScript that allows for asynchronous programming despite JavaScript being a single-threaded programming language.
It works in stages:
-
Call Stack: This is where code is executed. Functions get pushed onto the stack when they are called and popped off when they return a value.
-
Web APIs: Browsers provide web APIs for tasks like making HTTP requests, timers, and event listeners. When an asynchronous operation is initiated, the function is offloaded to these APIs.
-
Callback Queue: Once an asynchronous operation is completed, its callback is placed in the callback queue.
-
Event Loop: The event loop constantly checks the call stack. If it is empty, it moves callbacks from the callback queue to the call stack for execution.
undefined
typically represents the absence of a value. It is automatically assigned to a variable by JavaScript when a variable is declared but not initialized, or when accessing an object property that does not exist.null
, on the other hand, represents the intentional absence of any value. It is a value that can be assigned to a variable to explicitly indicate that it has no value.
In JavaScript, the value of the this
keyword refers to the object from which a function is called. It dynamically binds to different values based on how the function is invoked. The value of this
is determined by the execution context in which the function is called. Here are a few common scenarios:
- When a function is called as a method of an object,
this
refers to the object itself. - When a function is called as a standalone function,
this
refers to the global object (window
in a browser,global
in Node.js) in non-strict mode, andundefined
in strict mode. - When a function is called using the
new
keyword to create an instance of a constructor function,this
refers to the newly created object. - When a function is called using the
call()
orapply()
method,this
is explicitly set to the value passed as the first argument tocall()
orapply()
.
Understanding the context in which a function is called is crucial for determining the value of this
in JavaScript.
A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises provide a cleaner and more powerful way to handle asynchronous operations compared to traditional callback-based approaches.
A Promise has three states:
- Pending: The initial state, neither fulfilled nor rejected.
- Fulfilled: The operation completed successfully, and the promise has a resulting value.
- Rejected: The operation failed, and the promise has a reason for the failure (an error).
A Promise is created using the Promise
constructor, which takes a function (executor) with two arguments: resolve
and reject
. These functions are used to indicate the outcome of the operation.
Here's a basic example:
let myPromise = new Promise((resolve, reject) => {
let success = true; // Simulating success or failure
if (success) {
resolve("Operation was successful!"); // The promise is fulfilled
} else {
reject("Operation failed."); // The promise is rejected
}
});
myPromise
.then(result => {
console.log(result); // Handle the fulfillment
})
.catch(error => {
console.error(error); // Handle the rejection
});
call()
, apply()
, and bind()
are methods that allow you to set the value of this
inside a function and invoke that function in different ways.
-
call()
:- Syntax:
functionName.call(thisArg, arg1, arg2, ...)
call()
method calls a function with a giventhis
value and arguments provided individually.- Example:
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); } const person = { name: 'Alice' }; greet.call(person, 'Hello', '!'); // Output: "Hello, Alice!"
- Syntax:
-
apply()
:- Syntax:
functionName.apply(thisArg, [argsArray])
apply()
method calls a function with a giventhis
value and arguments provided as an array (or an array-like object).- Example:
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); } const person = { name: 'Alice' }; greet.apply(person, ['Hello', '!']); // Output: "Hello, Alice!"
- Syntax:
-
bind()
:- Syntax:
functionName.bind(thisArg, arg1, arg2, ...)
bind()
method creates a new function that, when called, has itsthis
value set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.- Unlike
call()
andapply()
,bind()
does not immediately invoke the function. Instead, it returns a new function. - Example:
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); } const person = { name: 'Alice' }; const greetPerson = greet.bind(person, 'Hello'); greetPerson('!'); // Output: "Hello, Alice!"
- Syntax:
Synchronous programming:
- In synchronous programming, code is executed sequentially, line by line. Each operation must complete before the next one starts.
- If an operation takes a long time (e.g., a network request or a long computation), it blocks the execution of subsequent operations until it finishes.
- Example:
console.log('Start');
// This represents a long-running operation
for (let i = 0; i < 1e9; i++) {}
console.log('End');
// Output: 'Start', (delay), 'End'
Asynchronous programming:
- In asynchronous programming, some operations (like network requests or timers) are executed in the background, allowing the main program to continue running.
- This prevents blocking and allows the program to remain responsive.
- Asynchronous code often uses callbacks, promises, or async/await to handle the completion of these operations.
console.log('Start');
setTimeout(() => {
console.log('Async operation complete');
}, 1000);
console.log('End');
// Output: 'Start', 'End', 'Async operation complete'
Answer:
- Description: The
map()
method creates a new array populated with the results of calling a provided function on every element in the calling array. - Example:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // Output: [2, 4, 6, 8]
- Description: The filter() method creates a new array with all elements that pass the test implemented by the provided function.
- Example:
const numbers = [1, 2, 3, 4];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // Output: [2, 4]
-
Description: The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
-
Example:
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // Output: 10
Question: What are the different ways to create objects in JavaScript?
Answer:
-
Object Literal Syntax
-
This is the most common and simplest way to create an object.
-
Example:
let obj = {};
-
-
Object.create()
Method-
This method creates a new object with the specified prototype object and properties.
-
Example:
let objCreate = Object.create({});
-
-
new Object()
Syntax-
This method creates an instance of the
Object
type. -
Example:
let newObj = new Object();
-
-
Constructor Functions
-
A constructor function is used to create multiple instances of an object with the same properties and methods.
-
Example:
function Person(name, age) { this.name = name; this.age = age; } let person1 = new Person('Alice', 30); let person2 = new Person('Bob', 25);
-
-
ES6 Classes
-
Classes are a syntactical sugar over constructor functions and provide a more intuitive way to create objects and handle inheritance.
-
Example:
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}`); } } let person1 = new Person('Alice', 30); let person2 = new Person('Bob', 25);
-
-
Factory Functions
-
A factory function returns a new object every time it is called.
-
Example:
function createPerson(name, age) { return { name: name, age: age, greet() { console.log(`Hello, my name is ${this.name}`); } }; } let person1 = createPerson('Alice', 30); let person2 = createPerson('Bob', 25);
-
Arrow functions are a shorter syntax for writing functions in JavaScript. They are anonymous and lexically bind the this
value, meaning this
inside an arrow function refers to the context in which the function was defined, not the context from which it was called.
// Regular function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
- Syntax: Arrow functions have a more concise syntax.
- this Binding: In regular functions,
this
refers to the object that called the function, which could change depending on how the function is called. In arrow functions,this
is lexically bound to the surrounding context.
function Person() {
this.age = 0;
setInterval(function growUp() {
this.age++; // `this` refers to the global object, not the instance of Person
}, 1000);
}
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // `this` refers to the instance of Person
}, 1000);
}
- arguments Object: Arrow functions do not have their own arguments object. If you need to access the arguments object, you need to use a regular function.
// Regular function
function sum() {
return Array.from(arguments).reduce((acc, val) => acc + val, 0);
}
// Arrow function (doesn't have `arguments` object)
const sum = () => {
// `arguments` object is not available here
};
- Cannot be used as constructors: Arrow functions cannot be used with the
new
keyword. They do not have aprototype
property.
// Regular function
function Car(make, model) {
this.make = make;
this.model = model;
}
const myCar = new Car('Toyota', 'Corolla'); // Works fine
// Arrow function
const Car = (make, model) => {
this.make = make;
this.model = model;
};
const myCar = new Car('Toyota', 'Corolla'); // TypeError: Car is not a constructor
async
/await
is a syntactic sugar introduced in ES2017 that makes it easier to work with Promises, providing a cleaner and more readable way to write asynchronous code compared to the traditional .then()
and .catch()
methods.
- The
async
keyword is used to declare an asynchronous function. An async function always returns a Promise. - If the function returns a value, the Promise will be resolved with that value.
- If the function throws an error, the Promise will be rejected with that error.
- The
await
keyword can only be used inside an async function. - It pauses the execution of the async function and waits for the Promise to resolve.
- Once the Promise is resolved, it returns the resolved value.
- If the Promise is rejected, it throws the error.
Using Promises:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data received');
}, 2000);
});
}
fetchData()
.then(data => {
console.log(data); // Output after 2 seconds: Data received
})
.catch(error => {
console.error(error);
});
Using asnc
/await
:
async function fetchDataAsync() {
try {
const data = await fetchData();
console.log(data); // Output after 2 seconds: Data received
} catch (error) {
console.error(error);
}
}
fetchDataAsync();
- Readability: Makes the asynchronous code look synchronous, which is easier to read and understand.
- Error Handling: Can use
try...catch
blocks for error handling, making it more consistent with synchronous code. - Simplified Syntax: Reduces the need for chaining
.then()
and.catch()
methods, which can become difficult to read with multiple nested asynchronous operations.
Question: Explain the concept of event bubbling and event capturing in JavaScript.
Answer:
Event Bubbling:
- When an event occurs on an element, it will bubble up through its ancestors in the DOM tree, triggering any event listeners attached to those elements.
- This means that if an event listener is attached to a parent element, it will also be triggered when the same event occurs on a child element.
- Bubbling occurs from the target element up to the root of the DOM tree (HTML element).
Event Capturing:
- Event capturing is the opposite of event bubbling.
- When an event occurs, it starts from the root of the DOM tree and travels down to the target element.
- This allows you to intercept events at an ancestor level before they reach the target element.
- Event capturing is less commonly used than event bubbling, but it can be useful in certain scenarios, such as global event handling.
Explain the difference between JavaScript's setTimeout()
and setInterval()
functions, and provide examples of their usage
- The
setTimeout()
function is used to execute a function once after a specified delay (in milliseconds). - It executes the function only once.
- After the specified delay, the function is added to the event queue and executed when the call stack is empty.
- It returns a timer ID that can be used to cancel the execution of the function before it occurs.
setTimeout(() => { console.log('This message will be logged after 2 seconds.'); }, 2000);
- The
setInterval()
function is used to repeatedly execute a function at a specified interval (in milliseconds). - It keeps executing the function at the specified interval until it is explicitly canceled using
clearInterval()
. - Each execution of the function is added to the event queue at the specified interval.
- It returns a timer ID that can be used to cancel the repeated execution of the function.
let counter = 0; const intervalId = setInterval(() => { console.log(`Counter: ${counter}`); counter++; if (counter === 5) { clearInterval(intervalId); // Cancels the interval after 5 executions } }, 1000);``
- It's important to be cautious when using
setInterval()
as it may lead to performance issues if the interval is too short or if the function takes a long time to execute. - Always remember to clear the interval using
clearInterval()
when you no longer need it to avoid memory leaks and unnecessary resource consumption.