JS Fundamentals
* Basic JS functions:
1. parseInt(string, radix) | parseInt("A", 16); // Returns 10 (hexadecimal to decimal), parseFloat() parses a string and returns a floating-point number.
2. ${} is used to evaluate and embed expressions dynamically in template literals. It is used for expression interpolation within template literals. Template literals are strings enclosed by backticks (` `) instead of single quotes (' ') or double quotes (" "). The expression inside the ${} is evaluated, and its result is converted to a string and inserted into the template literal.
3. slice(optional start, optional end)
4. substr(start, till what length)
5. splice(start, optional delete count, optional items to add) it returns an array containing deleted elements, delete count=0 if we don't want to delete any item.
6. push(put from front), pop(from right), shift(from left), unshift(put at end)
7. indexOf, lastindexOf, str.replace(a1, a2), value.split(delimiter) gives an array
8. arr1.concat(arr2) is used to merge two or more arrays, or values, into a new array. The original arrays are not modified.
9. arr1.forEach(callbackFn, optional thisArg) expects synchronous function and calls this function once for each element in an array in ascending index order.
10. Var is global and can be re-assigned, let is local and can be re-assigned, const is local and is fixed.
11. Internal linking is basically extraction of output from html file itself, while external linking is extraction of code from separate Js files (linked through src in html file).
12. Time functions .getDate(), .getMonth(), .getFullYear(), .getHours/Minutes/Seconds() similarly set functions are used.
* Key Notes:
13. The 'new' operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
14. JavaScript 'Date' objects represent a single moment in time in a platform-independent format. Date objects encapsulate an integral number that represents milliseconds since the midnight at the beginning of January 1, 1970, UTC (the epoch).
15. setInterval(func, delay) method repeatedly calls a function or executes a code snippet, with a fixed time delay b/w each call.
16. The global setTimeout(functionRef, delay) method sets a timer which executes a function or specified piece of code once the timer expires.
17. JSON.parse() static method parses a JSON string, constructing the JS value/object described by the string. JSON.stringify() static method converts a JS value/object to a JSON string. MDN JSON Doc
18. In Js even if the callback is resolved and ready with its return value but our thread is busy somewhere the control will not reach the callback(as it is sent to callback queue). Only when the thread is ideal, the event loop runs and the control will reach out to address pending callbacks by placing them in call stack.
19. 'Promises' in Js are just syntactical sugar that still uses callbacks under the hood but it helps to get rid of callbacks syntactically in asynchronous code.
Promise.race() method will return the promise instance which is firstly resolved or rejected. Let's take an example of race() method where promise2 is resolved first:
var promise1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "one");
});
var promise2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([promise1, promise2]).then(function (value) {
console.log(value); // "two" // Both promises will resolve, but promise2 is faster
});
20. Every browser has a Js engine inside it so that Js code can be executed in that browser. NodeJs is a runtime environment of Js, a place where all of our program will be executed having access to file system, databases, and network attached to the server. Culmination of V8 engine and C++, due to which Js code can be executed outside of browser. Node.js has its own APIs, provided by libuv.
21. JS on its own is just a programming language, it has variables, functions, loops, basic objects etc. but it does not have built-in ways to interact with the browser window like handle timers, read/ write files, or make network requests. The runtime environment has some methods built into it and any program executed in that environment has access to these methods.
22. HTTP servers are used to expose some kind of functionality over the internet so that others can access it as well.
23. Understanding URLs
Eg.1 https://www.example.com/: This URL points to the main page of the website example.com using the HTTPS protocol, including the www. subdomain. The trailing slash indicates that this URL points to a directory or a resource.
Eg.2 http://images.example.com/photo.jpg: This URL points to the image file photo.jpg located within the images subdomain of example.com, using the HTTP protocol.
Eg.3 https://blog.example.com/post/article1?author=john&date=2025-05-31#section2: This URL points to a specific blog post, article1, within the blog subdomain of example.com, and includes parameters like author=john and date=2025-05-31, and a fragment section2, all using the HTTPS protocol.
For a more detailed explanation visit: MDN URLs Doc
24. In const app = express(); app is an instance of express and will act as our handler function, it represents our HTTP server logic, just like what http.createServer((req, res)=>{...}); represents. In app.METHOD(path, handler), handler is the function executed when the route is matched.
25. We use JWT tokens so that once the user is authenticated we use the token to confirm the identity in all future requests without needing the user to enter password again for every other route. Every time a user manually signs in, a completely new JWT token is generated to represent a fresh authenticated session.
JWTs are stateless(thus no need to store app sessions in memory) and self-contained, the token itself serves as proof of authentication and includes metadata like the issued time and expiration. This approach also prevents misuse of old or previously leaked tokens.
JWTs enable stateless authentication by embedding user information (like ID, role, and permissions) directly within a self-contained token issued after successful login (e.g., verifying username/password). This token, structured as `Header.Payload.Signature` (each Base64Url-encoded), includes critical metadata like the issuing algorithm (alg) in the header and user claims (sub, role) plus timestamps (iat, exp) in the payload. The server signs the token (e.g., using HMAC-SHA256 with a secret key) to create the signature, ensuring integrity.
The client stores this JWT securely (e.g., in local storage) and includes it in subsequent requests (typically as a `Bearer` token in the Authorization header). The server verifies requests statelessly by checking the signature against its secret key to confirm the token hasn't been tampered with and validating the expiration time (`exp`). If valid, the server grants access based on the claims within the token itself, eliminating the need for session storage.
JWT structure, payload details, and security mechanism:-
* Header: Specifies token type (JWT) and signing algorithm (e.g., HS256).
* Payload: Contains user claims like subject (sub), name, role (e.g., {"sub":"1234567890","name":"Alice","admin":true}), and critical timestamps:-
iat (Issued At): Token creation time (e.g., 1716239022 = May 21, 2024, 12:43:42 UTC).
exp (Expiration): Token expiry time (e.g., 1716242622 = May 21, 2024, 13:43:42 UTC - 1 hour later).
* Signature: Generated via HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret_key). This cryptographic seal prevents tampering by ensuring the header and payload haven't been altered after issuance.
The client stores this token (e.g., "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0...SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") securely and sends it with subsequent requests. The server verifies it statelessly by checking the signature against its secret key to validate integrity and confirming the token hasn't expired (exp). After the verification, finally access is granted
26. Clusters of Node.js processes can be used to run multiple instances of Node.js that can distribute workloads among their application threads.
* Problem: Node.js runs on a single thread, meaning it can only use one CPU core at a time. This can be a bottleneck if your server receives many requests.
* Solution: The cluster module lets you create "worker" processes that run concurrently. Each worker is a separate instance of your application, and they all share the same server port. This distributes the load across multiple CPU cores, improving performance and scalability. When process isolation is not needed, use the worker_threads module instead, which allows running multiple application threads within a single Node.js instance. Clusters Doc
27. We use encodeURI() function to encode a URL. This function requires a URL string as a parameter and it returns that encoded string. Opposingly, decodeURI() function is used to decode a URL. This function requires an encoded URL string as parameter and return that decoded string.
Note: If you want to encode characters such as / ? : @ & = + $ # then you need to use encodeURIComponent().
Eg:-
let uri = "employeeDetails?name=john&occupation=manager";
let encoded_uri = encodeURI(uri);
let decoded_uri = decodeURI(encoded_uri);
28. Server-sent events (SSE) is a server push technology enabling a browser to receive automatic updates from a server via HTTP connection without resorting to polling. These are a one way communications channel - events flow from server to client only. This has been used in Facebook/Twitter/X updates, stock price updates, news feeds etc.
We can also perform browser support for server-sent events before using it as below:
if (typeof EventSource !== "undefined") {
// Server-sent events supported. Can have some code here!
} else {
// No server-sent events supported
}
29. Memoization is a functional programming technique which attempts to increase a function’s performance by caching its previously computed results.
Each time a memoized function is called, its parameters are used to index the cache. If the data is present, then it can be returned, without executing the entire function.
Otherwise the function is executed and then the result is added to the cache. Let's take an example of adding function with memoization
const memoizeAddition = () => {
let cache = {};
return (value) => {
if (value in cache) {
console.log("Fetching from cache");
return cache[value]; // Here, cache.value cannot be used as property name starts with the number which is not a valid JavaScript identifier. Hence, can only be accessed using the square bracket notation.
} else {
console.log("Calculating result");
let result = value + 20;
cache[value] = result;
return result;
}
};
};
// returned function from memoizeAddition
const addition = memoizeAddition();
console.log(addition(20)); //output: 40 calculated
console.log(addition(20)); //output: 40 cached
Memoization Fcc
Memoization DevCommunity
30. A higher-order function in JavaScript is a function that can take other functions as arguments or can return functions. This feature enables functional programming paradigms such as map, reduce, and filter. Higher-order functions offer versatility and modularity, fostering streamlined, efficient code.
Key Characteristics:
* First-class functions: Functions in JavaScript are considered first-class, meaning they are a legitimate data type and can be treated like any other value, including being assigned to variables, stored in data structures, or returned from other functions.
* Closure support: Due to closures, a higher-order function can transport not just the enclosed data within the function definition, but also the lexical environment in which that data resides.
* Dynamic code: Because JavaScript allows functions to be dynamically constructed and named, they can be dynamically passed to higher-order functions.
Code Example: Higher-order Functions
// Simple higher-order function
function multiplier(factor) {
return function(num) {
return num * factor;
};
}
// Invoke a higher-order function
const twice = multiplier(2);
console.log(twice(5)); // Output: 10
// Functional programming with higher-order functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(multiplier(2)); // [2, 4, 6, 8, 10]
const tripled = numbers.map(multiplier(3)); // [3, 6, 9, 12, 15]
31. Currying is the process of transforming a function with multiple arguments into a sequence of nested functions, each accepting only one argument at a time.
* Before Currying (Normal n-ary Function)
const multiArgFunction = (a, b, c) => a + b + c;
console.log(multiArgFunction(1, 2, 3)); // Output: 6
* After Currying (Unary Function Chain)
const curryUnaryFunction = (a) => (b) => (c) => a + b + c;
console.log(curryUnaryFunction(1)); // Returns: function (b) => ...
console.log(curryUnaryFunction(1)(2)); // Returns: function (c) => ...
console.log(curryUnaryFunction(1)(2)(3)); // Output: 6
* Each function in the chain accepts one argument and returns the next function, until all arguments are provided and the final result is computed.
32. A pure function is a function whose output depends only on its input arguments and produces no side effects. This means that given the same inputs, a pure function will always return the same output, and it does not modify any external state or data.
Example: Pure vs. Impure Functions
// Impure Function
let numberArray = [];
const impureAddNumber = (number) => numberArray.push(number);
// Pure Function
const pureAddNumber = (number) => (inputArray) =>
inputArray.concat([number]);
// Usage
console.log(impureAddNumber(6)); // returns 1
console.log(numberArray); // returns [6]
console.log(pureAddNumber(7)(numberArray)); // returns [6, 7]
console.log(numberArray); // remains [6]
* impureAddNumber changes the external variable numberArray and returns the new length of the array, making it impure, whereas pureAddNumber creates a new array with the added number and does not modify the original array, making it pure.
33. Prototypical inheritance in JavaScript is a way for objects to inherit properties and methods from other objects. Every JavaScript object has a special hidden property called [[Prototype]] (commonly accessed via __proto__ or using Object.getPrototypeOf()) that is a reference to another object, which is called the object's "prototype".
When a property is accessed on an object and if the property is not found on that object, the JavaScript engine looks at the object's __proto__, and the __proto__'s __proto__ and so on, until it finds the property defined on one of the __proto__s or until it reaches the end of the prototype chain.
Here's an example of prototypal inheritance:
// Parent object constructor.
function Animal(name) {
this.name = name;
}
// Add a method to the parent object's prototype.
Animal.prototype.makeSound = function () {
console.log('The ' + this.constructor.name + ' makes a sound.');
};
// Child object constructor.
function Dog(name) {
Animal.call(this, name); // Call the parent constructor.
}
// Set the child object's prototype to be the parent's prototype.
Object.setPrototypeOf(Dog.prototype, Animal.prototype);
// Add a method to the child object's prototype.
Dog.prototype.bark = function () {
console.log('Woof!');
};
// Create a new instance of Dog.
const bolt = new Dog('Bolt');
// Call methods on the child object.
console.log(bolt.name); // "Bolt"
bolt.makeSound(); // "The Dog makes a sound."
bolt.bark(); // "Woof!"
34. Symbols are unique and immutable identifiers. Symbols are often used as non-enumerable, non-colliding property keys in objects, meaning they won’t show up in for...in, Object.keys(), or JSON.stringify(), and they avoid property name collisions, which is very useful in library or framework development.
35. XMLHttpRequest (XHR) and fetch() are both used for making asynchronous HTTP requests in JavaScript, but fetch() is more modern, promise-based, and offers a cleaner syntax. Unlike XHR, which uses event callbacks, fetch() supports promise chaining and better error handling with catch(). It also allows greater flexibility with headers, request bodies, and caching via the cache option. While fetch() uses an AbortController for cancelling requests, XHR has a built-in abort() method. XHR supports progress tracking, which fetch() currently lacks.
36. The Intl API through objects like Intl.NumberFormat and Intl.DateTimeFormat provides fully locale aware formatting for numbers and dates (e.g. "$1,234.56" in U.S., "1 234,56 €" in France), based on the user's language and region settings. It eliminates the need for manual formatting logic and handles variations in calendars, numbering systems, currency styles, and date conventions effortlessly.
Here's an example:-
const usd = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
console.log(usd.format(1000));
const fr = new Intl.DateTimeFormat("fr-FR");
console.log(fr.format(new Date("2025-04-03T00:00:00")));
// Output:
$1,000.00
09/08/2025 // DD/MM/YYYY format