this
keyword in JavaScript?The this
keyword in JavaScript refers to the object that is currently executing the function. Its value is determined by how a function is called.
Key points:
this
can change depending on the contextthis
refers to the global object (window in browsers, global in Node.js)this
refers to the object the method was called onthis
refers to the newly created instancethis
can be explicitly set using call()
, apply()
, or bind()
Examples:
console.log(this === window); // true (in a browser)
const obj = {
name: "John",
greet: function() {
console.log(`Hello, ${this.name}!`);
}
};
obj.greet(); // Output: Hello, John!
function Person(name) {
this.name = name;
}
const john = new Person("John");
console.log(john.name); // Output: John
function greet() {
console.log(`Hello, ${this.name}!`);
}
const person = { name: "Alice" };
greet.call(person); // Output: Hello, Alice!
const obj = {
name: "John",
greet: () => {
console.log(`Hello, ${this.name}!`);
}
};
obj.greet(); // Output: Hello, undefined!
// Arrow functions do not have their own `this` binding
Diagram: this
in Different Contexts
┌─────────────────────┐
│ Global Scope │
│ this → global obj │
│ ┌─────────────────┐│
│ │ Function ││
│ │ this → dynamic ││
│ │ ┌─────────────┐││
│ │ │Object Method│││
│ │ │ this → obj │││
│ │ └─────────────┘││
│ └─────────────────┘│
└─────────────────────┘
Prototypal inheritance is a fundamental concept in JavaScript where objects can inherit properties and methods from other objects through a prototype chain.
Key points:
[[Prototype]]
property (accessible via __proto__
)Object.prototype
, whose prototype is null
Object.create()
can be used to create an object with a specific prototypeExample:
// Constructor function
function Animal(name) {
this.name = name;
}
// Adding a method to the prototype
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound.`);
};
// Creating a new object
const dog = new Animal("Rex");
dog.speak(); // Output: Rex makes a sound.
// Checking the prototype chain
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
Diagram: Prototype Chain
┌─────────┐ ┌──────────────────┐ ┌───────────────────┐ ┌──────┐
│ dog │───▶│ Animal.prototype │───▶│ Object.prototype │───▶│ null │
└─────────┘ └──────────────────┘ └───────────────────┘ └──────┘
Arrow functions, introduced in ES6, provide a more concise syntax for writing function expressions. They differ from regular functions in several important ways.
Key differences:
this
binding: Arrow functions do not have their own this
arguments
object: Arrow functions don’t have the arguments
objectnew
keywordprototype
property: Arrow functions don’t have a prototype
propertyExamples:
// Arrow function const addArrow = (a, b) => a + b;
2. `this` binding:
```javascript
const obj = {
name: "John",
greet: function() {
setTimeout(function() {
console.log(`Hello, ${this.name}!`); // `this` is not bound to obj
}, 1000);
},
greetArrow: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}!`); // `this` is bound to obj
}, 1000);
}
};
obj.greet(); // Output: Hello, undefined!
obj.greetArrow(); // Output: Hello, John!
arguments
object:
```javascript
function regular() {
console.log(arguments);
}const arrow = () => { console.log(arguments); // ReferenceError: arguments is not defined };
regular(1, 2, 3); // Output: [1, 2, 3] arrow(1, 2, 3); // Throws an error
4. Cannot be used as constructors:
```javascript
const Person = (name) => {
this.name = name;
};
const john = new Person("John"); // TypeError: Person is not a constructor
Diagram: Arrow vs Regular Functions
┌─────────────────────────┐ ┌─────────────────────────┐
│ Regular Function │ │ Arrow Function │
├─────────────────────────┤ ├─────────────────────────┤
│ - Own `this` binding │ │ - No own `this` binding │
│ - Has `arguments` object│ │ - No `arguments` object │
│ - Can be constructor │ │ - Cannot be constructor │
│ - Has `prototype` │ │ - No `prototype` │
└─────────────────────────┘ └─────────────────────────┘
async
and await
?async
and await
are features introduced in ES2017 to simplify working with Promises and asynchronous code. They provide a more synchronous-looking way to write asynchronous code.
Key points:
async
functions always return a Promiseawait
can only be used inside an async
functionawait
pauses the execution of the async
function until the Promise is resolvedExample:
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id === 1) {
resolve({ id: 1, name: "John Doe" });
} else {
reject("User not found");
}
}, 1000);
});
}
async function getUser(id) {
try {
const user = await fetchUser(id);
console.log("User:", user);
} catch (error) {
console.error("Error:", error);
}
}
getUser(1); // Output after 1 second: User: { id: 1, name: "John Doe" }
getUser(2); // Output after 1 second: Error: User not found
Diagram: async/await Flow
┌─────────────────┐
│ async function │
│ ┌─────────────┐│
│ │ await ││
│ │ (pauses) ││
│ └──────┬──────┘│
│ │ │
│ ┌──────▼──────┐│
│ │ continue ││
│ │ execution ││
│ └─────────────┘│
└─────────────────┘
The event loop is a fundamental concept in JavaScript that enables non-blocking, asynchronous execution. It continuously checks the call stack and the callback queue, moving callbacks to the call stack when it’s empty.
Key components:
Example:
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
// Output:
// Start
// End
// Promise
// Timeout
Diagram: Event Loop
┌───────────────┐ ┌───────────────┐
│ Call Stack │ │ Web APIs │
│ │ │ (Async ops) │
└───────┬───────┘ └───────┬───────┘
│ │
│ ▼
│ ┌───────────────┐
│ │ Callback Queue│
│ └───────┬───────┘
│ │
│ ┌───────────────┐
│ │Microtask Queue│
│ └───────┬───────┘
│ │
└─────────────────────┘
Event Loop
Template literals, introduced in ES6, provide an easier way to create multiline strings and perform string interpolation.
Key features:
${expression}
Examples:
const name = "John";
const greeting = `Hello, ${name}!`;
console.log(greeting); // Output: Hello, John!
const multiline = `
This is a
multiline
string.
`;
console.log(multiline);
// Output:
// This is a
// multiline
// string.
const a = 5;
const b = 10;
console.log(`Sum: ${a + b}, Product: ${a * b}`);
// Output: Sum: 15, Product: 50
${acc}${str}<strong>${values[i] || ''}</strong>
, ‘’);
}const name = “John”;
const age = 30;
const result = highlightMy name is ${name} and I'm ${age} years old.
;
console.log(result);
// Output: My name is John and I’m 30 years old.
### 7. What is the difference between `==` and `===`?
The `==` (loose equality) and `===` (strict equality) operators are used for comparison in JavaScript, but they behave differently when comparing values of different types.
**Key differences:**
- `==` performs type coercion before comparison
- `===` does not perform type coercion and requires both value and type to be the same
**Examples:**
```javascript
// Loose equality (==)
console.log(5 == "5"); // true (string "5" is coerced to number 5)
console.log(0 == false); // true (false is coerced to number 0)
console.log(null == undefined); // true (special case)
// Strict equality (===)
console.log(5 === "5"); // false (different types)
console.log(0 === false); // false (different types)
console.log(null === undefined); // false (different types)
// More examples
console.log([] == 0); // true ([] is coerced to 0)
console.log([] === 0); // false (different types)
console.log('' == false); // true (both coerced to 0)
console.log('' === false); // false (different types)
Best practice: It’s generally recommended to use ===
for comparisons to avoid unexpected type coercion issues.
Diagram: Equality Comparison
┌─────────────────────┐ ┌─────────────────────┐
│ Loose Equality (==) │ │ Strict Equality (===)│
├─────────────────────┤ ├─────────────────────┤
│ 1. Type Coercion │ │ 1. Type Check │
│ 2. Value Comparison │ │ 2. Value Comparison │
└─────────────────────┘ └─────────────────────┘
There are several ways to create objects in JavaScript:
const obj = {
name: "John",
age: 30
};
function Person(name, age) {
this.name = name;
this.age = age;
}
const john = new Person("John", 30);
const personProto = {
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
const john = Object.create(personProto);
john.name = "John";
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
const john = new Person("John", 30);
function createPerson(name, age) {
return {
name,
age,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
}
const john = createPerson("John", 30);
Each method has its own use cases and advantages. Object literals are great for simple objects, constructor functions and classes are useful for creating multiple instances with shared methods, Object.create()
is powerful for setting up inheritance chains, and factory functions offer encapsulation and private variables.
bind
, call
, and apply
methods?bind
, call
, and apply
are methods available on function objects in JavaScript. They are used to manipulate the this
context of a function and, in the case of call
and apply
, to invoke the function immediately.
bind
method:
this
contextconst person = {
name: "John",
greet: function(greeting) {
console.log(`${greeting}, I'm ${this.name}`);
}
};
const greetJohn = person.greet.bind(person,"Hello");
greetJohn(); // Output: Hello, I'm John
call
method:
this
contextfunction greet(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person = { name: "John" };
greet.call(person, "Hello", "!"); // Output: Hello, I'm John!
apply
method:
call
, but accepts arguments as an arraythis
contextfunction greet(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person = { name: "John" };
greet.apply(person, ["Hello", "!"]); // Output: Hello, I'm John!
Key differences:
bind
creates a new function, while call
and apply
invoke the function immediatelycall
takes arguments individually, while apply
takes arguments as an arrayUse cases:
bind
: Creating methods with a fixed this
context, partial function applicationcall
: Invoking methods in a specific context, borrowing methods from other objectsapply
: Similar to call
, but useful when arguments are already in an array or when using Math.max()/min()
with arraysDiagram: bind, call, and apply
┌─────────────────────────────────────────────────────────────┐
│ Function Manipulation │
├─────────────┬─────────────────────────┬─────────────────────┤
│ bind │ call │ apply │
├─────────────┼─────────────────────────┼─────────────────────┤
│ New function│ Immediate invocation │ Immediate invocation│
│ Fixed `this`│ Specified `this` │ Specified `this` │
│ │ Individual arguments │ Array of arguments│
└─────────────┴─────────────────────────┴─────────────────────┘
Event delegation is a technique in JavaScript where you attach a single event listener to a parent element instead of attaching multiple listeners to individual child elements. This approach leverages event bubbling and can improve performance and simplify dynamic content handling.
Key benefits:
Example:
<ul id="todo-list">
<li>Task 1</li>
<li>Task 2</li>
<li>Task 3</li>
</ul>
<script>
document.getElementById('todo-list').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
console.log('Clicked on:', e.target.textContent);
e.target.style.textDecoration = 'line-through';
}
});
</script>
In this example, instead of attaching click events to each <li>
element, we attach a single listener to the parent <ul>
. When a click occurs, we check if the clicked element (e.target) is an <li>
, and if so, we handle the event.
Diagram: Event Delegation
┌─────────────────────────────┐
│ Parent Element │
│ ┌───────────────────────┐ │
│ │ Event Listener │ │
│ └───────────┬───────────┘ │
│ ▲ │ ▲ │
│ │ ▼ │ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Child │ │Child │ │Child │ │
│ └──────┘ └──────┘ └──────┘ │
└─────────────────────────────┘
Event Bubbling