javaScript for interview
javaScript for interview
console.log(x); // Output: 10
}
console.log(x); // Output: 5
3. const
Scope: Block-scoped.
Cannot be re-declared or updated (but objects and arrays can have their
contents modified).
Hoisted: Behaves like let with the "Temporal Dead Zone."
Example:
const PI = 3.14;
// PI = 3.15; // Error: Assignment to constant variable
const obj = { name: "John" };
obj.name = "Alice"; // Allowed
Operators in JavaScript
JavaScript operators are used to perform operations on variables and values. Here's
a classification of key operator types:
1. Arithmetic Operators
Used for mathematical calculations.
Examples: +, -, *, /, %, ++ (increment), -- (decrement).
let a = 10, b = 5;
console.log(a + b); // Output: 15
console.log(a % b); // Output: 0
2. Assignment Operators
Used to assign values.
Examples: =, +=, -=, *=, /=.
let x = 10;
x += 5; // Equivalent to x = x + 5
console.log(x); // Output: 15
3. Comparison Operators
Used to compare values.
Examples: ==, ===, !=, !==, <, <=, >, >=.
console.log(5 == "5"); // Output: true (type coercion)
console.log(5 === "5"); // Output: false (strict equality)
4. Logical Operators
Used to combine conditions.
Examples: && (AND), || (OR), ! (NOT).
console.log(true && false); // Output: false
console.log(!true); // Output: false
5. Bitwise Operators
Used to manipulate bits of binary numbers.
Examples: &, |, ^, ~, <<, >>.
6. Ternary Operator
A shorthand for if-else.
Syntax: condition ? exprIfTrue : exprIfFalse
5
}
Characteristics:
o Hoisted: Can be called before its definition because of hoisting.
o Named: Always has a name.
o Best for reusable named functions.
Example:
console.log(add(2, 3)); // Output: 5 (works due to hoisting)
function add(a, b) {
return a + b;
}
2. Function Expression
Syntax:
const functionName = function(parameters) {
// function body
};
Characteristics:
o Not hoisted: Cannot be called before its definition.
o Can be anonymous (no name).
o Used for functions assigned to variables or passed as arguments.
Example:
const multiply = function(a, b) {
return a * b;
};
Characteristics:
o Concise syntax.
o Does not have its own this (inherits this from its surrounding context).
o Cannot be used as constructors (no new keyword).
o Implicit return for single-line expressions.
Example:
const divide = (a, b) => a / b; // Implicit return
console.log(divide(10, 2)); // Output: 5
Hoisting Yes No No
Constructo
Yes Yes No
rs
1. Basic Example:
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
console.log(greet()); // Output: Hello, Guest!
console.log(greet("Alice")); // Output: Hello, Alice!
2. Default Parameters with Expressions:
function calculateArea(length = 5, width = length) {
return length * width;
}
console.log(calculateArea()); // Output: 25
console.log(calculateArea(10)); // Output: 100
console.log(calculateArea(10, 20)); // Output: 200
Edge Case: Passing undefined vs. null
undefined triggers the default parameter.
null does not trigger the default parameter.
function checkValue(val = 10) {
return val;
}
console.log(checkValue(undefined)); // Output: 10
console.log(checkValue(null)); // Output: null
Higher-Order Functions
Definition:
A higher-order function is a function that:
1. Takes another function as an argument.
2. Returns a function as its output.
Why Important?
1. Core concept in functional programming.
2. Widely used in array manipulation (map, filter, reduce) and asynchronous
operations.
11
Examples:
1. Higher-Order Function with Callback:
function calculate(operation, a, b) {
return operation(a, b);
}
Interview-Ready Tips
1. Common Use Cases: Explain when and why you'd use each type of function
definition.
2. Advanced Scenarios: Be ready to write and debug code using callbacks,
map, filter, and reduce.
3. Behavioral Insights: Highlight this behavior differences between arrow
functions and traditional functions.
13
decrement() {
count--;
return count;
},
reset() {
count = 0;
return count;
},
};
}
Recursion
A recursive function is one that calls itself until it reaches a base condition.
Key Concepts:
1. Base Case: Stops the recursion to prevent infinite calls.
2. Recursive Case: Function calls itself with modified parameters.
Example: Simple Recursion
Find the factorial of a number:
function factorial(n) {
if (n === 0) return 1; // Base case
return n * factorial(n - 1); // Recursive case
}
};
Interview-Ready Tips
1. Highlight Problem-Solving Skills: Explain how closures or recursion help
solve real-world problems, such as data privacy or hierarchical data traversal.
18
function showGlobal() {
console.log(globalVar); // Output: "I am global"
}
showGlobal();
Function Scope
Variables declared inside a function using var are scoped to that function.
Not accessible outside the function.
Example:
function testScope() {
var functionScoped = "I exist only here";
console.log(functionScoped); // Output: "I exist only here"
}
testScope();
console.log(functionScoped); // Error: functionScoped is not defined
19
Block Scope
Variables declared with let or const are limited to the block ({}) in which they
are defined.
Introduced in ES6, addressing var’s scoping issues.
Example:
{
let blockScoped = "I exist only in this block";
console.log(blockScoped); // Output: "I exist only in this block"
}
console.log(blockScoped); // Error: blockScoped is not defined
Lexical Scope
Inner functions can access variables from their outer functions due to their
position in the code hierarchy.
Example:
function outer() {
let outerVar = "Outer";
function inner() {
console.log(outerVar); // Output: "Outer"
}
inner();
}
outer();
2. Closures
A closure is a function that "remembers" the variables from its lexical scope even
when the outer function has completed execution.
Key Points:
1. Persistence: Variables in closures persist in memory.
2. Access to Outer Scope: Inner functions can access variables of the outer
function.
Example:
function makeCounter() {
let count = 0;
20
return function () {
count++;
return count;
};
}
function setup() {
let name = "Closure Example";
document.addEventListener("click", () => {
console.log(name);
});
}
setup();
3. Currying: Transforming a function so that it takes arguments one at a time.
function multiply(a) {
return function (b) {
return a * b;
};
}
console.log(`${greeting}, ${this.name}`);
}
Interview-Ready Tips
1. Scope:
o Know how var, let, and const behave in different scopes.
o Be prepared to debug scope-related questions like variable shadowing.
2. Closures:
o Understand how closures are created and used.
o Be ready to write functions using closures for private variables or
currying.
3. this:
o Understand how this changes based on context (regular function vs.
arrow function).
o Be prepared for practical coding questions that require you to
manipulate this.
Debouncing and Throttling
1. What is Debouncing?
Debouncing ensures a function is executed only once after a specified time has
elapsed since the last invocation.
Use Case:
Search Bar Autocomplete: Trigger API calls only when the user stops
typing.
Resize Events: Prevent the function from being called excessively while
resizing the browser window.
How it Works:
24
2. What is Throttling?
Throttling ensures a function is executed at most once in a specified time interval,
regardless of how many times the event is triggered.
Use Case:
Scrolling: Improve performance by limiting frequent updates during scroll
events.
Button Clicks: Prevent rapid firing of API requests.
How it Works:
25
1. The function executes immediately and then pauses until the specified
interval has elapsed.
2. Further calls during the interval are ignored.
Implementation:
function throttle(func, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
func.apply(this, args);
}
};
}
window.addEventListener("scroll", handleScroll);
Key Points:
The function is guaranteed to run at regular intervals, improving
responsiveness without overloading resources.
Definitio Executes the function after a delay Executes the function at regular
n since the last call intervals
Frequen
Runs only once if triggered repeatedly Runs at most once per interval
cy
26
});
Interview Tips
1. Know the Basics:
o Understand the difference between debouncing and throttling.
o Be ready to explain them conceptually and implement them in vanilla
JavaScript.
2. Be Prepared for Scenarios:
o How would you debounce/throttle an Angular event like keyup or
scroll?
o Discuss real-world challenges, such as ensuring that the final API call
happens after a debounced event.
3. Write Clear Code:
o Ensure your code demonstrates reusability (e.g., using higher-order
functions for debounce/throttle logic).
4. Be Ready for Edge Cases:
o Handling this context issues in classes or components.
o Combining debouncing and throttling for optimal results.
Error Handling in JavaScript
Error handling is critical in any application to gracefully handle unexpected
situations without crashing the entire system. JavaScript provides mechanisms to
catch and manage errors, ensuring a smoother user experience and better
debugging.
28
try {
validateAge(-5); // This will throw an error
} catch (error) {
console.log("Caught error:", error.message); // Output: "Caught error: Age cannot
be negative"
30
}
try-catch:
The try-catch statement is used to catch errors and handle them
gracefully when executing code that could potentially fail.
The catch block only executes if an error is thrown inside the try block.
Example:
try {
let result = riskyOperation(); // This might throw an error
console.log(result);
} catch (error) {
console.log("Error caught:", error.message); // Handling the error
}
Difference in Usage:
throw is used to generate an error intentionally.
try-catch is used to catch and handle an error that occurs in a block of
code.
try {
// Code that might throw
} catch (error) {
// Error handling
} finally {
// Cleanup code, always runs
}
Log Errors: Make sure to log the errors for easier debugging, especially for
production environments.
Avoid Overuse of try-catch: try-catch should be used for error handling
and not for normal control flow. Instead, use conditions or validations where
appropriate.
Conclusion
throw and try-catch are essential for managing errors in JavaScript, and as
an Angular developer, you'll often work with asynchronous code, API calls,
and form validations where error handling becomes crucial.
Best Practices like using specific error classes, logging, and leveraging the
finally block can make your error handling much more robust.
1. Explain the JavaScript Event Loop and its Role in Asynchronous
Programming
JavaScript is a single-threaded language, meaning it can only execute one
operation at a time. However, it can handle multiple operations asynchronously by
leveraging the Event Loop, Call Stack, and Callback Queue.
Key Concepts:
1. Call Stack:
o This is where JavaScript keeps track of function calls. The call stack
operates on the Last In, First Out (LIFO) principle. When a function
is invoked, it is pushed onto the call stack. Once the function
completes, it is popped off.
Example:
function firstFunction() {
console.log('First Function');
}
function secondFunction() {
console.log('Second Function');
}
setTimeout(() => {
console.log('Inside setTimeout'); // 3rd: Asynchronous callback
}, 0);
Output:
sql
Start
End
Inside setTimeout
Explanation: The event loop allows setTimeout to run asynchronously, so
the 'End' message prints before the callback from setTimeout.
Purpose: Schedules the next function call to be executed before the next
repaint (next frame). It is specifically designed for animation.
Use Case: Ideal for performing smooth animations, especially when
manipulating the DOM, as it aligns with the browser's refresh rate.
Behavior: Runs before the next frame is rendered (typically 60 FPS), which is
more optimized than using setTimeout or setInterval for animations.
Example:
function animate() {
console.log('Animating...');
requestAnimationFrame(animate); // Keep calling itself
}
Executes
Execution Executes once after Executes before next repaint,
repeatedly at
Type delay optimized for animations
intervals
Repeated
Delayed one-time Smooth animations and
Use Case execution at set
execution rendering
intervals
Performan Not optimized for Not optimized for Optimized for smooth
ce animations animations animations
console.log('End');
Output:
Start
End
Promise Resolved
setTimeout
2. Optimizing Performance with requestAnimationFrame:
o requestAnimationFrame is the most efficient method for creating
animations because it automatically aligns with the browser's repaint
cycle, ensuring smooth rendering without unnecessary computation.
Conclusion:
The Event Loop is the backbone of asynchronous programming in
JavaScript, enabling non-blocking execution.
setTimeout, setInterval, and requestAnimationFrame are essential tools
for managing asynchronous tasks, but each serves different purposes, with
requestAnimationFrame being the most optimized for animations.
Understanding how the event loop interacts with these asynchronous
functions is critical for building efficient applications, especially for UI-heavy
or real-time applications.
1. How do Promises Work? What are resolve, reject, and then?
37
if (success) {
resolve('Operation was successful!');
} else {
reject('Something went wrong!');
}
});
promise
.then(result => {
console.log(result); // If the promise is resolved
})
.catch(error => {
38
try {
console.log('Fetching data...');
let result = await fetchData(); // Wait for the promise to resolve
console.log(result); // Logs 'Data fetched successfully' after 2 seconds
} catch (error) {
console.log('Error:', error); // If any error occurs
}
}
displayData();
Explanation:
The fetchData function returns a promise that resolves after 2 seconds.
The displayData function is marked as async and uses await to wait for the
fetchData promise to resolve before continuing with the code execution.
The await keyword ensures that we handle the asynchronous operation more
synchronously, making the code easier to read and maintain.
Benefits of async/await:
Makes asynchronous code look like synchronous code.
More readable and reduces the need for chaining multiple .then() calls.
Provides better error handling with try-catch.
Microtasks represent tasks that are typically related to promises and other
asynchronous operations that need to be processed immediately after the
current execution context (before the next rendering or macrotask).
They have higher priority than macrotasks, meaning they are always
executed before any macrotasks, even if the macrotask was scheduled first.
Examples: Promises (then, catch), MutationObserver.
Order of Execution:
First, synchronous code is executed.
Then, all microtasks in the microtask queue are executed.
Finally, after all microtasks have been processed, macrotasks are executed.
Example of Microtasks and Macrotasks:
console.log('Start');
setTimeout(() => {
console.log('Macrotask - setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Microtask - Promise resolved');
});
console.log('End');
Output:
Start
End
Microtask - Promise resolved
Macrotask - setTimeout
Explanation:
Even though setTimeout is scheduled first, its callback is executed later
because it’s a macrotask.
The promise callback (then) is a microtask and is executed before the
macrotask, even though it was scheduled later.
41
4. Important Considerations:
Error Handling with async/await: Always use try-catch blocks within async
functions to handle promise rejections.
Concurrency: While async/await looks synchronous, it’s non-blocking.
Multiple await calls can be handled concurrently by using Promise.all().
Example:
async function fetchData1() { return 'Data 1'; }
async function fetchData2() { return 'Data 2'; }
fetchAll();
await only with promises: The await keyword only works with promises.
Using it with non-promises will throw a syntax error.
Conclusion:
Promises are used to manage asynchronous operations in JavaScript,
making it easier to handle success or failure without nested callbacks.
async/await simplifies working with promises, making asynchronous code
more readable and easier to understand.
Understanding microtasks and macrotasks is crucial for understanding the
order in which asynchronous operations are executed, especially in complex
applications.
1. How do you traverse the DOM using JavaScript?
DOM Traversal refers to the process of navigating through the Document Object
Model (DOM) to access and manipulate HTML elements.
Common Methods for DOM Traversal:
1. Accessing Elements:
o getElementById(id): Returns an element with the specified id.
let element = document.getElementById('myElement');
42
button.addEventListener('click', function() {
console.log('Button clicked!');
});
Key Points:
You can attach multiple listeners to the same element.
It allows for more control over event propagation (e.g., capture phase vs.
bubble phase).
You can remove event listeners with removeEventListener.
Inline Event Handlers:
Limited Flexibility: Only one event handler can be attached to an element.
Mixing HTML and JavaScript: Event handling is written directly in the
HTML, which can lead to messy code and difficulty in debugging.
Less Control: You don't have control over event propagation or more
advanced features like capture.
No Removal: It's difficult to remove an inline event handler once it’s
attached.
Example:
<button onclick="alert('Button clicked!')">Click Me</button>
45
Key Points:
Event handling is directly written in HTML, which mixes content and behavior.
You can only attach one event handler for a specific event.
It’s harder to maintain as the code grows, especially in larger applications.
Conclusion:
DOM Traversal is fundamental for selecting and manipulating elements,
with various methods for navigating parent, child, and sibling elements.
Event Delegation improves performance and simplifies event handling,
especially for dynamically added content.
46
Animal.prototype.sayHello = function() {
console.log(`${this.name} says hello!`);
};
sayHello() {
console.log(`${this.name} says hello!`);
}
}
You can use the extends keyword to create subclasses, making inheritance more
straightforward.
Example:
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call the parent class constructor
this.breed = breed;
}
bark() {
console.log(`${this.name} barks!`);
}
}
constructor(width, height) {
this._width = width;
this._height = height;
}
// Area method
get area() {
return this._width * this._height;
}
}
// main.js
const greet = require('./module1');
greet('John'); // Output: Hello, John!
ES6 Modules:
Asynchronous: ES6 modules are designed for asynchronous loading and can
be imported dynamically, which is better for modern web development
(browser and server-side).
import and export: ES6 modules use import to bring in modules and export
to share functions, objects, or variables.
Static: ES6 imports are static, meaning the dependencies are determined at
compile-time, which allows for better optimizations like tree shaking
(removing unused code).
Universal: ES6 modules can be used both in the browser and Node.js (with
support from tools like Babel or newer versions of Node.js).
Example:
// module1.js
export function greet(name) {
52
console.log(`Hello, ${name}!`);
}
// main.js
import { greet } from './module1';
greet('John'); // Output: Hello, John!
Key Differences:
Syntax: require() and module.exports (CommonJS) vs. import and export
(ES6).
Loading Mechanism: CommonJS is synchronous, while ES6 modules are
asynchronous and support dynamic imports.
Scope: ES6 modules have a block scope, meaning variables and functions
inside modules are scoped to the module itself, avoiding pollution of the
global namespace.
Support for Named Exports: ES6 modules support both named exports
and default exports, while CommonJS generally exports a single entity.
// main.js
import { add, subtract } from './math';
console.log(add(5, 3)); // Output: 8
console.log(subtract(5, 3)); // Output: 2
Default Export:
53
A module can have only one default export, which allows you to export a
single function, class, or object.
Example:
// greet.js
export default function greet(name) {
console.log(`Hello, ${name}!`);
}
// main.js
import greet from './greet';
greet('John'); // Output: Hello, John!
You can also combine named exports with a default export:
Example:
// utils.js
export const multiply = (a, b) => a * b;
export default function add(a, b) {
return a + b;
}
// main.js
import add, { multiply } from './utils';
console.log(add(2, 3)); // Output: 5
console.log(multiply(2, 3)); // Output: 6
Dynamic Import (ES6):
You can also use dynamic imports to load modules asynchronously, which is
useful when you need to load a module on demand (lazy loading).
Example:
// main.js
function loadModule() {
import('./math.js').then(module => {
console.log(module.add(2, 3)); // Output: 5
});
54
}
Re-exporting Modules:
You can re-export from other modules using export * or export
{ specificFunction }.
Example:
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// utils.js
export * from './math'; // Re-exports all exports from math.js
// main.js
import { add, subtract } from './utils';
console.log(add(5, 3)); // Output: 8
console.log(subtract(5, 3)); // Output: 2
Conclusion:
55
function createObject() {
let obj = { name: 'John' }; // obj is reachable here
obj = null; // Now, obj is unreachable and can be garbage collected
}
createObject();
In this example, after setting obj to null, the object becomes unreachable and will
eventually be collected by the garbage collector.
2. What Are Memory Leaks, and How Can You Avoid Them?
A memory leak happens when an application retains more memory than
necessary, often due to objects that are no longer used but are still kept in memory.
Over time, memory leaks can cause an application to slow down or even crash, as
the program consumes more and more resources without releasing them.
Common Causes of Memory Leaks in JavaScript:
1. Global Variables: If you accidentally create global variables or fail to clean
up references, they stay in memory for the entire lifetime of the application.
o Solution: Minimize the use of global variables, and always declare
variables using let, const, or var in a local scope.
2. Unintentional Closures: JavaScript closures can unintentionally "capture"
variables that are no longer needed, causing memory to be held
unnecessarily.
o Solution: Be cautious when using closures. Avoid holding unnecessary
references inside closures that outlive their intended scope.
3. Detached DOM Nodes: If you remove DOM elements but still hold
references to them in JavaScript, the garbage collector won't be able to clean
them up.
o Solution: Ensure that event listeners and references to DOM elements
are cleaned up when they are removed from the DOM.
4. Event Listeners: If you attach event listeners to DOM elements but forget to
remove them when they are no longer needed, those event listeners will keep
references to the elements and prevent them from being garbage collected.
o Solution: Always remove event listeners when they are no longer
necessary using removeEventListener.
5. Timers and Intervals: Functions like setInterval or setTimeout can create
memory leaks if the intervals are never cleared, especially if they reference
large objects.
o Solution: Always clear intervals or timeouts using clearInterval or
clearTimeout when they are no longer needed.
57
Conclusion:
Garbage Collection in JavaScript is an automatic process where the
JavaScript engine frees up memory by removing unreachable objects. It
typically uses techniques like mark-and-sweep or reference counting.
Memory Leaks occur when objects are kept in memory even though they
are no longer needed. Common causes include global variables, unintentional
closures, detached DOM nodes, and unremoved event listeners.
To avoid memory leaks, it’s crucial to manage scope effectively, clean up
references, and monitor memory usage.
Understanding memory management and how to avoid memory leaks is crucial,
especially in performance-sensitive applications like those built with Angular, where
improper memory management can lead to slow applications and bad user
experiences.
1. How Can You Optimize JavaScript Code for Performance?
Optimizing JavaScript is crucial for enhancing user experience by improving the
performance of applications. Below are some techniques that can be used:
58
clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
};
}
window.addEventListener('resize', debounce(() => console.log('Resized!'), 300));
1.4 Avoid Memory Leaks
Memory leaks can degrade performance over time, so it’s essential to remove
event listeners, clean up intervals, and free up resources when they’re no
longer needed.
Use WeakMap for storing data to avoid preventing garbage collection.
1.5 Optimize Loops
Loops can be slow if they are not optimized. Use local variables in the loop,
avoid array.length in the loop condition (since array.length is recalculated on
every iteration), and use modern loop methods like forEach, map, or reduce
where appropriate.
Example:
let len = array.length;
for (let i = 0; i < len; i++) {
console.log(array[i]);
}
1.6 Lazy Loading
Load resources only when they are needed. For example, you can load
images or modules only when they come into view (lazy loading), which can
greatly improve performance.
Example (Lazy Loading Images):
html
<img data-src="image.jpg" class="lazy" />
document.addEventListener('scroll', function() {
let images = document.querySelectorAll('.lazy');
images.forEach((img) => {
if (img.getBoundingClientRect().top < window.innerHeight) {
img.src = img.dataset.src;
img.classList.remove('lazy');
}
60
});
});
2. What Are the Best Practices to Improve the Load Time of a Web
Application?
Improving the load time of a web application is crucial to providing a fast and
smooth user experience. The following practices can help you reduce load times:
2.1 Minify and Compress Code
Minify your JavaScript, CSS, and HTML files to remove unnecessary
whitespace, comments, and line breaks.
Compress files using gzip or Brotli to reduce the size of the files being sent
over the network. This significantly reduces download time.
2.2 Bundle Files
Instead of loading many individual JavaScript files, bundle your JavaScript
and CSS files into fewer, larger files. This reduces the number of HTTP
requests made during page load.
Tools like Webpack, Rollup, and Parcel can help you bundle and optimize
files.
2.3 Implement Code Splitting
Code splitting allows you to break your JavaScript code into smaller chunks
and load only the code that is necessary for a particular page or route,
instead of loading the entire application upfront.
Example (Code Splitting with Webpack):
import(/* webpackChunkName: "myModule" */ './myModule').then(module => {
// Use the module
});
2.4 Lazy Load Non-Essential Resources
Load JavaScript, images, and other resources only when they are needed.
Lazy load non-critical resources such as images, videos, or even parts of the
JavaScript code for faster initial page load.
2.5 Use a Content Delivery Network (CDN)
Serve static assets like images, CSS, and JavaScript from a Content Delivery
Network (CDN). CDNs have multiple servers across the globe, which helps
reduce latency by serving files from the server closest to the user.
2.6 Optimize Images
61
Images can often be the largest asset on a web page. Use modern image
formats like WebP and compress images to reduce file size.
Consider using responsive images (srcset) to load images based on the
device's resolution.
Example:
<img srcset="image-500w.jpg 500w, image-1000w.jpg 1000w" src="image.jpg"
alt="Responsive Image">
2.7 Reduce HTTP Requests
Minimize the number of requests made to the server. This can be done by:
o Combining CSS and JavaScript files.
o Using SVG sprites for icons.
o Using data URIs for small images (like icons).
2.8 Use Service Workers for Caching
Service workers allow you to cache resources on the client side, so
subsequent visits to the website will load faster. Service workers can
intercept network requests and serve cached resources even when the user is
offline.
2.9 Enable HTTP/2 or HTTP/3
Use HTTP/2 or HTTP/3 to allow multiplexing, header compression, and other
improvements that can reduce latency and speed up loading.
2.10 Optimize JavaScript Execution
Defer or async the loading of JavaScript that is not needed immediately.
Use Web Workers to offload CPU-intensive tasks to background threads,
allowing the main thread to remain free and responsive.
Example:
<script src="script.js" defer></script>
2.11 Use Browser Caching
Set cache-control headers for static resources to instruct the browser to
cache them locally. This way, files don’t need to be downloaded again on
subsequent page loads.
Conclusion:
To optimize JavaScript code for performance:
Minimize DOM manipulations, use debouncing and throttling, avoid
unnecessary memory usage, and optimize loops.
62
2. Can You Explain the Singleton and Observer Patterns with Examples?
2.1 Singleton Pattern:
The Singleton pattern ensures that a class has only one instance and provides a
global point of access to it.
Example: Imagine you are building an application that uses a logging service. You
want to ensure that there is only one instance of the logger throughout the entire
application.
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance; // Return the existing instance
}
this.logs = [];
Logger.instance = this; // Store the instance
}
log(message) {
64
this.logs.push(message);
console.log(message);
}
getLogs() {
return this.logs;
}
}
// Usage
const logger1 = new Logger();
const logger2 = new Logger();
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notifyObservers() {
this.observers.forEach(observer => observer.update(this));
}
setName(name) {
this.name = name;
this.notifyObservers(); // Notify all observers about the name change
}
}
class Logger {
update(user) {
console.log(`User's name has changed to: ${user.name}`);
}
}
class EmailService {
update(user) {
console.log(`Sending email about name change to: ${user.name}`);
}
}
66
// Usage
const user = new User("John");
user.addObserver(logger);
user.addObserver(emailService);
Conclusion:
Singleton Pattern: Ensures that a class has only one instance and provides
a global point of access to it. It's useful for shared resources like logging,
configuration settings, or database connections.
Observer Pattern: Allows one object (subject) to notify other objects
(observers) about changes to its state. This pattern is great for implementing
event-driven systems and is widely used in UI frameworks where various
components need to react to changes in state.
Both of these patterns are essential tools in a developer's toolkit for designing
clean, scalable, and maintainable applications. By understanding these and other
design patterns, you'll be able to create better, more efficient code, which is crucial
when building complex applications, especially in product-based companies.
14. JavaScript Frameworks and Libraries
Understanding JavaScript frameworks and libraries is essential for any senior
developer. These tools can help streamline development by providing pre-built
solutions to common problems, and can significantly improve performance and
scalability. Below are key concepts regarding JavaScript frameworks and libraries,
focusing on the Virtual DOM and Angular's Change Detection and Zone.js.
67
1. What is the Virtual DOM, and How is it Different from the Real DOM?
The Virtual DOM (VDOM) is an abstraction of the Real DOM (RD) used in modern
JavaScript frameworks and libraries like React. Understanding the distinction
between the two is important for performance optimization and efficient rendering.
Virtual DOM:
Definition: The Virtual DOM is an in-memory representation of the actual
DOM elements. It is essentially a lightweight copy of the real DOM.
Purpose: The main goal of the Virtual DOM is to minimize direct
manipulation of the real DOM, which can be slow and inefficient,
especially in large applications. By using the VDOM, changes can be made
quickly in memory and then "patched" to the real DOM in a more optimized
and batch-processed way.
How It Works:
o When the state of an application changes, the Virtual DOM is updated
first.
o A diffing algorithm compares the current version of the Virtual DOM
with a previous version, calculating the minimal set of changes
required.
o Only the necessary changes are then applied to the real DOM. This
process is known as reconciliation.
Example: In React, every time state or props change, the VDOM is updated and
compared to the previous version. The framework efficiently updates only the parts
of the actual DOM that have changed.
javascript
// Virtual DOM diffing in React
function App() {
const [counter, setCounter] = useState(0);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>
Count: {counter}
</button>
</div>
);
68
}
In this example, the button’s text changes when the state counter updates. React’s
Virtual DOM helps ensure that only the button text is updated in the real DOM,
rather than re-rendering the entire page.
Real DOM:
Definition: The Real DOM is the actual DOM that the browser uses to render
the page on screen.
Performance: Manipulating the real DOM directly can be expensive because
it forces the browser to recalculate styles, reflow layouts, and repaint the
page. Frequent and large updates to the real DOM can result in poor
performance.
Key Differences:
Virtual DOM is faster and more efficient because it minimizes direct
manipulations of the real DOM.
The Real DOM is slower, especially with frequent updates, because the
browser must fully re-render the page or section of the page.
increment() {
this.count++;
}
}
In this case, when the button is clicked, the increment method updates the count
property, and Angular triggers change detection to update the view.
2.2 Zone.js:
Zone.js is a library used in Angular to manage asynchronous operations (such as
HTTP requests, timers, etc.). It is integral to Angular's change detection system
because it helps track asynchronous activities and automatically triggers change
detection when the asynchronous code completes.
How It Works:
o Zone.js creates an execution context (a zone) around asynchronous
tasks. This allows Angular to detect when tasks like HTTP requests or
timers have completed, and it triggers change detection in response to
these events.
o Zone.js "monitors" the execution flow and can hook into events such as
setTimeout, HTTP requests, or promise resolutions.
o When any asynchronous task completes, Zone.js informs Angular,
which triggers change detection to update the UI.
Example: Angular uses Zone.js behind the scenes to automatically detect
when asynchronous tasks are completed. For example, when an HTTP request
returns, Zone.js helps Angular know when to check if the view needs to be
updated.
this.http.get('/api/data').subscribe(data => {
70
Conclusion:
Virtual DOM: The Virtual DOM is a lightweight representation of the real
DOM. It improves performance by minimizing direct updates to the real DOM
and allows for optimized rendering via diffing algorithms. It's used primarily in
React but is also part of many other modern frameworks.
Angular’s Change Detection and Zone.js: Angular uses change detection
to update the view whenever the data model changes. The OnPush strategy
allows for more efficient checks. Zone.js tracks asynchronous operations and
triggers change detection when they complete, ensuring that the UI is in sync
with the application state.
Both concepts are foundational to building performant web applications.
Understanding how the Virtual DOM works and how Angular uses change detection
with Zone.js will help you write more efficient, scalable applications and optimize
your performance, which is a key concern in product-based companies.
15. Testing in JavaScript
Testing is a critical part of software development. It ensures that the application
works as expected, reduces bugs, and increases maintainability. In JavaScript, there
are various strategies and tools available for testing code. Unit testing, mocks, and
spies are essential concepts to understand, especially when preparing for interviews
at top product-based companies. Below are explanations of key testing concepts in
JavaScript:
Unit tests should be small, isolated tests that check the functionality of a
particular unit of code.
The goal is to ensure that each unit of the program works independently and
correctly, which in turn guarantees that the overall application works as
intended.
Why Unit Testing is Important:
Ensures Code Quality: Unit tests help ensure that the logic in your code
works as expected and can catch bugs early.
Reduces Regression: By running tests whenever changes are made, unit
tests help prevent existing features from breaking when new code is added.
Increases Maintainability: Well-tested code is easier to maintain and
refactor since the tests provide confidence that the code will continue to work
after changes.
How to Implement Unit Testing in JavaScript:
To implement unit testing, you need a testing framework and possibly a mocking
library. Some popular JavaScript testing frameworks are Jest, Mocha, and
Jasmine. Here's how you can implement unit tests using Jest:
Install Jest:
If you're using npm (Node Package Manager), you can install Jest by running:
bash
npm install --save-dev jest
Writing Unit Tests:
Unit tests typically go into a __tests__ or test directory. Here's an example of a
simple unit test:
// calculator.js (Function to be tested)
function add(a, b) {
return a + b;
}
module.exports = add;
expect(add(1, 2)).toBe(3);
});
function fetchData(url) {
return fetch(url).then(response => response.json());
}
module.exports = fetchData;
Conclusion:
Unit Testing is essential for ensuring that individual units of your application
work as expected. It helps catch bugs early and ensures code quality.
Frameworks like Jest, Mocha, and Jasmine make it easy to write and run
tests.
Mocks are used to simulate external dependencies, allowing you to isolate
your code during testing, while spies are useful for tracking function calls
and verifying interactions between components.
For top product-based companies, proficiency in writing unit tests and
understanding how to use mocks and spies effectively is critical, as it demonstrates
a strong ability to maintain code quality, detect issues early, and ensure the
stability of the application.
16. TypeScript
TypeScript is a superset of JavaScript that introduces static typing, interfaces, and
advanced features to the language. Understanding TypeScript is crucial for Angular
developers because Angular is built with TypeScript. Here, we will cover the key
differences between JavaScript and TypeScript and how TypeScript improves the
development process in Angular.
75
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
JavaScript: Provides basic tooling support like syntax highlighting and basic
code completion.
TypeScript: Offers enhanced tooling support, including intellisense (auto-
completion), type checking, and error highlighting within IDEs like Visual
Studio Code, making development more efficient.
1.7 Error Detection:
JavaScript: Errors are typically detected at runtime (e.g., when the code is
executed in the browser or Node.js).
TypeScript: Errors are detected during compile time, allowing developers to
catch issues before running the application. This leads to fewer runtime
errors and more reliable code.
name: string;
age: number;
}
@Component({
selector: 'app-user',
template: `{{ user.name }}`
})
export class UserComponent {
user: User = { name: "Alice", age: 30 };
}
2.2 Enhanced IDE Support and Autocompletion:
TypeScript provides better autocompletion and intellisense support in IDEs like
Visual Studio Code, which is the most popular IDE for Angular development. It
automatically shows method signatures, properties, and interfaces, helping
developers write code faster and with fewer mistakes.
Example: While defining a new service, TypeScript suggests properties and
methods based on the type of the service, which accelerates development.
2.3 Interfaces and Dependency Injection:
In Angular, dependency injection (DI) is commonly used to inject services into
components, directives, and other services. With TypeScript, you can define
interfaces for these services, providing better documentation and ensuring the
correct service is injected.
Example:
typescript
interface AuthService {
login(username: string, password: string): Observable<boolean>;
}
@Injectable({
providedIn: 'root'
})
export class AuthServiceImpl implements AuthService {
login(username: string, password: string): Observable<boolean> {
79
// Implementation here
}
}
Here, TypeScript ensures that any class implementing AuthService must follow the
correct method signature (login method with specific parameters and return type).
2.4 Decorators and Type Inference:
Angular relies heavily on decorators like @Component(), @Injectable(), and
@Input(). TypeScript improves the development process by inferring types for
parameters and return values in methods, which reduces the need for explicit type
annotations and helps maintain type consistency across the application.
Example:
typescript
@Component({
selector: 'app-product',
template: '<p>{{ product.name }}</p>',
})
export class ProductComponent {
@Input() product: Product; // TypeScript infers the type of 'product'
}
2.5 Code Refactoring and Maintenance:
TypeScript's static typing makes refactoring safer and easier. When changing
function signatures, properties, or data models, the TypeScript compiler checks the
entire codebase for compatibility issues, preventing errors from propagating across
the application. This is especially important in large Angular projects.
Conclusion:
Key Differences Between JavaScript and TypeScript:
o TypeScript introduces static typing, type annotations, interfaces,
and advanced OOP features.
o TypeScript requires a compilation step, while JavaScript is interpreted
directly by the browser.
o TypeScript helps detect errors at compile time, improving
development efficiency and reducing bugs.
How TypeScript Improves Development in Angular:
80
2. How Does the Promise.all Method Work, and What Happens if One
Promise Fails?
2.1 Promise.all:
Promise.all is a method that takes an array of Promises and returns a single
Promise that resolves when all the Promises in the array resolve, or rejects as soon
as one of the Promises rejects.
When all promises resolve: The result is an array containing the resolved
values of each promise in the same order they were passed in.
When one promise rejects: If any of the promises reject, Promise.all
immediately rejects with the reason of the first rejected promise, and it
doesn’t wait for the other promises to settle.
Example:
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Conclusion:
Generators vs. Async/Await:
o Generators offer greater flexibility for pausing and resuming
execution, making them useful for more complex control flows and
iterators.
o Async/Await simplifies asynchronous programming by making the
code look synchronous and easier to maintain.
Promise.all:
o Promise.all executes promises in parallel and waits for all promises to
resolve. It fails immediately when any promise rejects.
o If you need to wait for all promises to settle, including rejected ones,
use Promise.allSettled.
By understanding and mastering these advanced asynchronous programming
techniques, you'll be able to handle complex asynchronous workflows and improve
the performance and maintainability of your JavaScript applications, especially in
frameworks like Angular.
Meta-programming in JavaScript
Meta-programming refers to the practice of writing programs that manipulate other
programs (or themselves). In JavaScript, this concept is implemented through
features like proxies and the Reflect API. These allow developers to intercept and
manipulate fundamental operations, providing more control over objects and
functions.
1. What Are Proxies in JavaScript, and What Are Their Use Cases?
1.1 Proxies:
A Proxy in JavaScript is an object that wraps another object (called the target) and
allows you to intercept and redefine fundamental operations (such as property
lookup, assignment, enumeration, function invocation, etc.) on that object. Proxies
enable you to define custom behavior for basic operations.
Creating a Proxy: The Proxy constructor takes two arguments:
Target: The original object you want to wrap.
85
Handler: An object that defines traps for various operations (like get, set,
etc.).
const target = {
message: 'Hello'
};
const handler = {
get: function(target, prop, receiver) {
if (prop === 'message') {
return `${target[prop]}!`;
}
return target[prop];
}
};
}
};
Conclusion:
Proxies allow you to define custom behavior for fundamental operations on
objects. They are powerful for cases where you need to intercept and
manipulate operations like property access, assignment, or method calls.
The Reflect API provides a low-level, method-based approach to interact
with objects and perform operations like get, set, and delete. It can simplify
meta-programming tasks and is often used in combination with Proxies.
Mastering these features can greatly enhance your ability to write flexible,
maintainable, and powerful JavaScript code, especially when dealing with complex
or dynamic object manipulations.
19. Security in JavaScript
Security is a critical aspect of any web application, and JavaScript, being the
backbone of web interactivity, has its own security considerations. Two key
vulnerabilities that JavaScript developers need to be aware of are Cross-Site
Scripting (XSS) and Content Security Policy (CSP).
89
// Do this:
const button = document.querySelector('button');
button.addEventListener('click', () => alert('Hello'));
90
default-src 'self': Only allows content to be loaded from the same domain
as the website.
script-src 'self' https://cdn.example.com: JavaScript can only be loaded
from the same domain or from cdn.example.com.
style-src 'self' https://fonts.googleapis.com: Stylesheets can only be
loaded from the same domain or from fonts.googleapis.com.
2.4 Advantages of Using CSP:
Prevents Inline Script Execution: By blocking inline scripts (using unsafe-
inline), CSP ensures that even if an attacker manages to inject code into a
page, it won’t be executed.
Limits External Resources: By specifying trusted sources, CSP helps to
reduce the risk of loading malicious external resources.
Helps Mitigate Data Injection Attacks: CSP can prevent attackers from
injecting malicious scripts into the DOM by restricting where content can
come from.
2.5 Example of Adding CSP to Your Website:
HTTP Header: Add the following CSP header to your server response:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;
Meta Tag: You can also define CSP using a meta tag in the <head> of your
HTML document:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src
'self' https://trusted-cdn.com;">
2.6 Challenges of Implementing CSP:
Compatibility: Some older browsers may not fully support CSP or have
limited support for certain directives.
Policy Tuning: Developing a strict CSP requires testing to ensure that
legitimate content and scripts are not blocked by the policy. This may take
time to fine-tune.
Inline Scripts: If your website relies heavily on inline JavaScript,
implementing a strict CSP may require refactoring to externalize scripts or
use nonces (one-time tokens) for inline scripts.
Conclusion:
XSS vulnerabilities can be mitigated by escaping user input, avoiding inline
scripts, and using modern frameworks that automatically sanitize data.
Additionally, using Content Security Policy (CSP) allows you to restrict
where resources can be loaded from, preventing malicious content from
being executed.
92
Inline Caching: The engine caches certain method calls to optimize access
to object properties.
Deoptimization: If a piece of code becomes too complex for optimization
(due to dynamic typing, for example), the engine may deoptimize it and
execute it in a slower but simpler manner.
Garbage Collection: The engine automatically manages memory by
cleaning up unused objects and data, helping optimize performance and
avoid memory leaks.
1.4 Execution Context and the Call Stack
JavaScript engines use an execution context to track the code’s execution. Every
time a function is called, a new execution context is created, and the call stack
manages these contexts. The stack keeps track of function calls and ensures the
code runs in the correct order.
Variables declared using let and const are also hoisted, but they remain in a
"temporal dead zone" (TDZ) until they are actually declared in the code. This means
that referencing them before the declaration results in a ReferenceError.
Example:
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 5;
In this case, the variable a is hoisted, but since it is in the TDZ, trying to access it
before the let a = 5 line causes an error.
2.4 Hoisting with Functions
Function declarations are hoisted to the top of their scope, meaning you can call a
function before its declaration.
Example:
myFunction(); // "Hello, world!"
function myFunction() {
console.log("Hello, world!");
}
In the above example, even though myFunction is called before its declaration, the
function works because the entire function declaration (both the name and the
body) is hoisted.
2.5 Hoisting with Function Expressions
If a function is assigned to a variable (i.e., a function expression), only the variable
declaration is hoisted, not the function definition. This means calling the function
before its declaration results in an error.
Example:
javascript
myFunction(); // TypeError: myFunction is not a function
Conclusion
Compilation and Execution: JavaScript engines compile code in stages,
starting with parsing and creating an AST, followed by bytecode compilation,
and eventually execution. Optimization techniques like inline caching and
garbage collection are used to improve performance.
Hoisting: Variables and function declarations are hoisted in JavaScript, with
var behaving differently than let and const. Function declarations are hoisted
fully, while function expressions (assigned to variables) are not hoisted.
Understanding these concepts is essential for efficient debugging, performance
optimization, and writing cleaner, more predictable code, especially for senior-level
positions where these details matter in everyday development.
How Do Angular Directives Work Under the Hood in JavaScript?
1.1 What Are Angular Directives?
Angular directives are special markers or functions that extend HTML functionality.
They can change the appearance, behavior, or layout of DOM elements, and they
help manage DOM manipulation in a declarative manner.
There are three main types of directives in Angular:
Component Directives: Directives that have their own template.
Structural Directives: Directives that alter the structure of the DOM (e.g.,
*ngIf, *ngFor).
Attribute Directives: Directives that change the appearance or behavior of
an element (e.g., ngClass, ngStyle).
1.2 How Do Angular Directives Work Under the Hood?
Angular directives are implemented using a class that is decorated with the
@Directive decorator. This class typically has lifecycle hooks, such as ngOnInit,
ngOnChanges, ngAfterViewInit, and so on.
Angular compiles templates that contain directives using its template compiler
during runtime. Here's how they work under the hood:
Compiler Stage: The template is parsed and converted into a set of
instructions for creating the DOM elements. Directives are identified in the
template, and Angular compiles the template to associate them with their
respective behaviors.
Renderer: Directives can use the Angular Renderer2 service to manipulate
the DOM. For instance, an attribute directive may use the Renderer2 API to
add, remove, or modify DOM attributes or classes.
Change Detection: Directives participate in Angular's change detection
mechanism. If a directive modifies data that affects the view, Angular will re-
evaluate the bindings and trigger change detection for that component or
directive.
96
22. What is RxJS, and How Does it Integrate with JavaScript to Handle
Asynchronous Operations?
2.1 What is RxJS?
RxJS (Reactive Extensions for JavaScript) is a library for composing
asynchronous and event-based programs using observable sequences. It makes it
easier to handle asynchronous operations like HTTP requests, user input events,
timers, and more, in a more functional, declarative, and composable way.
RxJS is based on the observer pattern, where observers subscribe to streams
(observables) and receive notifications when new data is emitted.
2.2 How Does RxJS Integrate with JavaScript?
RxJS works by turning async data (such as user input, HTTP requests, or intervals)
into observable streams. These streams are processed using a series of operators
that can transform, filter, combine, or react to the emitted values.
Observables: RxJS introduces the concept of an observable, which
represents a stream of values over time.
Operators: RxJS operators (e.g., map, filter, merge, combineLatest,
switchMap) allow you to transform, combine, and manage observable
streams.
Subscription: You can subscribe to an observable to listen for values
emitted by that stream. The subscription will execute a callback each time a
new value is emitted.
Handling Asynchronous Operations: RxJS helps manage async operations
by using operators like mergeMap, concatMap, or switchMap to handle
promises or HTTP responses in a more manageable way.
For example, instead of handling multiple asynchronous HTTP requests with
callbacks, you can use the switchMap operator to chain requests:
typescript
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
this.http.get('/user')
.pipe(switchMap(user => this.http.get(`/posts?userId=${user.id}`)))
.subscribe(posts => console.log(posts));
97
This code uses RxJS to manage asynchronous HTTP requests in a more readable and
maintainable manner.
24. Explain the Concept of Zones in Angular and Their Impact on JavaScript
Code Execution
4.1 What Are Zones in Angular?
A zone is an execution context that allows Angular to keep track of asynchronous
operations, such as setTimeout, promises, HTTP requests, etc. The concept of zones
is powered by the Zone.js library, which Angular uses internally to detect changes
and trigger updates to the view when data changes asynchronously.
Zone.js intercepts asynchronous tasks and keeps track of the context in
which they were initiated, ensuring that Angular knows when to trigger
change detection, even if the operation occurs outside of Angular's normal
flow (e.g., within a third-party library or custom async function).
4.2 How Do Zones Work in Angular?
When Angular runs in a zone, it can track all asynchronous tasks that occur within it.
It does this by using Zone.js, a library that wraps asynchronous operations (like
setTimeout, Promise, etc.) and adds additional logic to them.
NgZone: Angular provides a service called NgZone to manage zones within
Angular applications. The NgZone service allows Angular to control and
optimize the change detection process based on which zone the
asynchronous task is executed in.
Change Detection: When an asynchronous operation completes (e.g., an
HTTP request, timer, or event), NgZone checks if it needs to trigger change
detection. If the task is executed outside Angular's zone, Angular will not
automatically update the view. However, by running the task within Angular’s
99
zone (using NgZone.run()), Angular will trigger change detection when the
task completes.
4.3 Example:
typescript
import { Component, NgZone } from '@angular/core';
@Component({
selector: 'app-zone-example',
template: `<h1>{{message}}</h1>`
})
export class ZoneExampleComponent {
message = 'Hello';
changeMessage() {
setTimeout(() => {
this.message = 'Updated Message';
this.ngZone.run(() => {
// Triggers Angular's change detection to update the view
});
}, 1000);
}
}
In this example, setTimeout is executed inside Angular's zone. The ngZone.run()
method ensures that change detection is triggered when the asynchronous
operation completes.
4.4 Impact on JavaScript Code Execution
Zones allow Angular to manage change detection more efficiently by tracking
asynchronous operations and determining when the view needs to be
updated.
Performance: Managing zones can help improve performance by avoiding
unnecessary change detections, as only the tasks inside Angular's zone will
trigger updates.
100
Conclusion
These concepts play a critical role in Angular applications and JavaScript code
execution:
Directives enhance the declarative nature of DOM manipulation.
RxJS provides a powerful way to handle asynchronous tasks.
Angular's DI system can be customized for more flexible dependency
management.
Zones ensure that Angular tracks and responds to asynchronous tasks
correctly, triggering change detection when necessary.
1. String Methods
String.prototype.includes(substring): Checks if a string contains a given
substring.
"hello world".includes("world"); // true
String.prototype.indexOf(substring): Returns the index of the first occurrence
of a substring, or -1 if not found.
"hello world".indexOf("world"); // 6
String.prototype.slice(start, end): Extracts a section of a string.
"hello world".slice(0, 5); // "hello"
String.prototype.split(delimiter): Splits a string into an array of substrings.
"a,b,c".split(","); // ["a", "b", "c"]
String.prototype.replace(search, replacement): Replaces occurrences of a
substring with another.
"hello world".replace("world", "JavaScript"); // "hello JavaScript"
2. Array Methods
Array.prototype.push(element): Adds one or more elements to the end of an
array.
let arr = [1, 2];
arr.push(3); // [1, 2, 3]
Array.prototype.pop(): Removes the last element of an array.
let arr = [1, 2, 3];
arr.pop(); // [1, 2]
101
}
greet(); // "Hello, Guest"
7. Array.prototype.some
Purpose: Checks if at least one element in an array passes a test.
[1, 2, 3].some(num => num > 2); // true
8. Array.prototype.every
Purpose: Checks if all elements in an array pass a test.
[1, 2, 3].every(num => num > 0); // true
9. Array.prototype.find
Purpose: Returns the first element that satisfies a condition.
[1, 2, 3].find(num => num > 1); // 2
10. Template Literals
Purpose: Allow embedding expressions in strings using backticks (`).
const name = "Alice";
console.log(`Hello, ${name}!`); // "Hello, Alice!"
11. Set and Map
Set:
o A collection of unique values.
const mySet = new Set([1, 2, 2, 3]);
console.log(mySet); // Set { 1, 2, 3 }
Map:
o A collection of key-value pairs.
const myMap = new Map();
myMap.set("key", "value");
console.log(myMap.get("key")); // "value"
12. typeof vs instanceof
typeof: Determines the type of a variable (primitive types).
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
instanceof: Checks if an object is an instance of a specific class or
constructor.
105
};
console.log([...myIterable]); // [1, 2, 3]
class Example {
@readonly
name() {
return "Readonly";
}
}
5. Object Property Shorthand
Purpose: Simplify object declarations when variable names match property
names.
const name = "Alice";
const obj = { name }; // { name: "Alice" }
6. Dynamic Property Names
Purpose: Use expressions to create object keys dynamically.
const key = "dynamicKey";
const obj = { [key]: "value" }; // { dynamicKey: "value" }
7. Async Iteration with for-await-of
Purpose: Iterate over asynchronous data sources (e.g., streams).
async function fetchData() {
const data = [Promise.resolve(1), Promise.resolve(2)];
for await (const num of data) {
console.log(num);
}
}
fetchData(); // Logs: 1, 2
8. Intl API
Purpose: Handle internationalization and localization.
o DateTimeFormat:
const date = new Date();
console.log(new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date));
o NumberFormat:
const num = 1234567.89;
108
document.querySelector("#child").addEventListener("click", () =>
console.log("Bubbling Phase"));
12. WeakMap and WeakSet
Purpose: Avoid memory leaks by holding "weak" references to objects.
const wm = new WeakMap();
let obj = {};
wm.set(obj, "value");
obj = null; // The reference in WeakMap will be garbage collected.
13. Custom Error Types
Purpose: Create application-specific error messages.
class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
}
}
throw new CustomError("Something went wrong!");
14. Function Composition
Purpose: Combine multiple functions into one.
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const double = x => x * 2;
const square = x => x * x;
const composed = compose(square, double);
console.log(composed(2)); // 16
15. Module Lazy Loading
Purpose: Load parts of your application on demand for performance.
const loadModule = async () => {
const { default: module } = await import("./module.js");
module();
};
16. Custom Promises
110