JavaScript

Q1. What is JavaScript, and how does it differ from Java?

Answer:

JavaScript is a scripting language mainly used to create and control dynamic website content. Unlike Java, which is a compiled, object-oriented language, JavaScript is primarily interpreted, event-driven, and object-based. JavaScript runs in the browser, while Java can be used for server-side applications.

console.log("JavaScript is lightweight and versatile!");

Answer:

var has function scope and is hoisted. let and const have block scope; let allows reassignment, while const does not allow reassignment or redeclaration.

var x = 5; // Function-scoped
let y = 10; // Block-scoped, reassignable
const z = 15; // Block-scoped, cannot be reassigned

Answer:

NaN means “Not-a-Number” and results when a mathematical operation fails to return a number. Use Number.isNaN(value) to check for NaN specifically.

console.log(Number.isNaN("hello" / 2)); // true
Answer:

JavaScript has primitive types like String, Number, Boolean, Undefined, Null, Symbol, and a non-primitive type, Object. Primitives are immutable, while objects allow for structured data.

let str = "Hello"; // String
let num = 123; // Number
let isTrue = true; // Boolean
Answer:

Closures happen when an inner function accesses a variable from an outer function, even after that outer function has finished executing. This is essential for data encapsulation.

function outer() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  };
}
const counter = outer();
counter(); // 1
counter(); // 2
Answer:

this refers to the object from which a function is called. Its value varies depending on the execution context, like global (window), object, or event context.

const obj = {
  name: "Alice",
  greet() {
    console.log("Hello, " + this.name);
  }
};
obj.greet(); // Hello, Alice
Answer:

Functions can be declared with function declarations (function name() {}), function expressions (const name = function() {}), and arrow functions (const name = () => {}).

function sayHi() { console.log("Hi"); }
const sayHello = function() { console.log("Hello"); };
const greet = () => { console.log("Greetings"); };
Answer:

Arrays store ordered collections of elements, accessible via index. Methods like push, pop, shift, unshift, map, and filter allow manipulation of array contents.

let arr = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]
Answer:

== performs type coercion before comparing, whereas === compares both value and type, making === stricter and generally preferred for accurate comparisons.

console.log(5 == "5"); // true
console.log(5 === "5"); // false
Answer:

An anonymous function lacks a name and is often used where functions are required as arguments or values, such as setTimeout(function() {}, 1000);.

setTimeout(function() {
  console.log("Anonymous function example");
}, 1000);
Answer:

Arrow functions use a concise syntax (=>) and do not bind this context, making them suitable for callbacks without the need to manually bind this.

const greet = () => "Hello";
Answer:

null is an intentional absence of any value, set manually, while undefined means a variable is declared but lacks a value.

let a; // undefined
let b = null; // null
Answer:

Global scope allows a variable to be accessed anywhere in the code, while local (block or function) scope restricts access to the block or function where it’s declared.

let globalVar = "I am global";
function testScope() {
  let localVar = "I am local";
}
Answer:

Use Array.isArray(variable) to check if a variable is an array.

console.log(Array.isArray([1, 2, 3])); // true
Answer:

Objects can be cloned using Object.assign({}, obj) for shallow clones or JSON.parse(JSON.stringify(obj)) for deep cloning (with limitations).

let original = { a: 1 };
let clone = Object.assign({}, original);

Answer:

An event is an action or occurrence recognized by the browser, like clicks or key presses, which can be responded to using event listeners.

document.querySelector("button").addEventListener("click", function() {
  alert("Button clicked!");
});
Answer:

Use event.preventDefault() within an event handler to stop default actions, like form submissions.

document.querySelector("form").addEventListener("submit", function(event) {
  event.preventDefault();
  alert("Form submission prevented");
});
Answer:

The DOM is a structured representation of HTML elements in a webpage, allowing manipulation and interaction with elements via JavaScript.

document.getElementById("myElement").innerText = "New Text!";
Answer:

Use element.textContent or element.innerHTML to set or change the content of an HTML element.

document.querySelector("#myDiv").textContent = "Hello, World!";
Answer:

JSON (JavaScript Object Notation) is a data format for exchanging information between servers and clients. It’s used with JSON.parse() to convert JSON to an object and JSON.stringify() to convert an object to JSON.

const jsonString = '{"name":"Alice"}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // Alice
Answer:

Higher-order functions take other functions as arguments or return them as results, enabling functional programming concepts like map, filter, and reduce.

const higherOrder = (fn) => fn();
Answer:

map transforms each array element, filter selects elements based on a condition, and reduce accumulates array values into a single result.

const numbers = [1, 2, 3, 4];
const squares = numbers.map(n => n * n); // [1, 4, 9, 16]
Answer:

async functions return promises, allowing await to pause execution until a promise resolves, simplifying asynchronous code.

async function fetchData() {
  let response = await fetch("https://api.example.com");
  let data = await response.json();
  console.log(data);
}
Answer:

Promises handle asynchronous operations, representing a value that may be resolved or rejected. They improve code readability over callback functions.

let promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve("done"), 1000);
});
promise.then(result => console.log(result));
Answer:

Synchronous operations block code execution until completion, while asynchronous operations allow other tasks to run, improving responsiveness.

console.log("Start");
setTimeout(() => console.log("Async"), 1000);
console.log("End");
Answer:

Use try to wrap code that may throw an error, and catch to handle the error gracefully, preventing crashes.

try {
  let result = someFunction();
} catch (error) {
  console.error("Error:", error);
}
Answer:

The module pattern uses closures to create private/public members.

const counter = (function() {
  let count = 0;
  return {
    increment() { count++; },
    getCount() { return count; }
  };
})();
Answer:

Hoisting moves variable and function declarations to the top of their scope.

console.log(foo); // undefined due to hoisting
var foo = "bar";
Answer:

Use Object.assign() for shallow copies and JSON.parse(JSON.stringify(obj)) for deep copies.

const original = { a: 1, b: { c: 2 } };
const shallowClone = Object.assign({}, original);
const deepClone = JSON.parse(JSON.stringify(original));
Answer:

this refers to the calling object but is lexically bound in arrow functions.

const obj = {
  name: "Alice",
  sayName() { console.log(this.name); }
};
obj.sayName(); // "Alice"
Answer:

Immediately Invoked Function Expressions execute right after defining, often for scoping variables.

(function() {
  const message = "IIFE executed!";
  console.log(message);
})();

Answer:

Closures allow an inner function to access variables from an outer function.

function outer() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}
const increment = outer();
increment(); // 1
increment(); // 2
Answer:

call and apply invoke functions with a specific this value; bind returns a new function with this bound.

function greet(message) {
  console.log(`${message}, ${this.name}`);
}
const person = { name: "Alice" };
greet.call(person, "Hello"); // "Hello, Alice"
Answer:

Currying transforms a function with multiple arguments into nested functions, each taking a single argument.

const multiply = (a) => (b) => a * b;
multiply(2)(3); // 6
Answer:

JavaScript uses prototypes to allow objects to inherit properties from other objects.

function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  console.log(`Hello, ${this.name}`);
};
const alice = new Person("Alice");
alice.greet(); // "Hello, Alice"
Answer:

The event loop manages async tasks by dequeuing tasks when the call stack is empty.

Example: The event loop enables setTimeout to execute later even in synchronous code.

Answer:

Debouncing limits function execution until a wait period has passed.

function debounce(fn, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}
Answer:

null is an assigned “no value”; undefined means a variable hasn’t been assigned.

let a = null; // null assigned by developer
let b; // undefined by default
Answer:

The spread operator expands elements in arrays or objects.

const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4]; // [1, 2, 3, 4]
Answer:

Destructuring unpacks values from arrays or properties from objects.

const person = { name: "Alice", age: 25 };
const { name, age } = person;
Answer:

JavaScript uses a garbage collection process to manage memory by automatically freeing memory used by objects that are no longer referenced. In most engines, including V8 (Chrome and Node.js), this is done using a “mark-and-sweep” algorithm. When objects are created, they occupy memory, and JavaScript marks objects as unreachable when there are no references to them. These are then collected to free up memory.

Answer:

JavaScript engines employ garbage collection techniques like “mark-and-sweep” and reference counting. The “mark-and-sweep” process goes through live objects (marked) and separates them from unreachable ones (sweep), clearing memory used by those objects. This process happens in the background, helping to optimize application performance and memory usage.

Answer:

Currying is the transformation of a function with multiple arguments into a sequence of functions, each with a single argument. It enables partial application of functions, where some arguments are preset, allowing the function to be reused with different arguments in a more flexible way.

const multiply = (a) => (b) => (c) => a * b * c;
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(3)(4)); // 24
Answer:

Optimizations include reducing DOM manipulation (batch updates), using lazy loading for images and modules, minimizing reflows, avoiding memory leaks, and optimizing loops (e.g., forEach vs for loops). Minifying and bundling code, caching data, and using async techniques like async/await or Promise.all also improve performance.

Answer:

WeakMap and WeakSet store weak references to objects, allowing garbage collection to occur when there are no other references. This is useful for cases where you need temporary object references without preventing garbage collection. Unlike regular maps and sets, weak collections only allow object keys, not primitives.

const wm = new WeakMap();
let obj = { name: "Alice" };
wm.set(obj, "value");
obj = null; // now eligible for garbage collection
Answer:

Functional programming is a paradigm that treats functions as first-class citizens and emphasizes pure functions (no side effects), immutability, and declarative code. This approach is useful for building predictable, testable, and modular code.

Example: Functions that always return the same output for the same input.

const add = (a, b) => a + b;
Answer:

Async generators allow the await keyword inside generators, enabling asynchronous iteration. They yield promises and can be used in for await...of loops, making it easier to handle asynchronous sequences.

async function* asyncGen() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
}
(async () => {
  for await (const val of asyncGen()) {
    console.log(val); // 1, then 2
  }
})();
Answer:

V8, the JavaScript engine for Chrome and Node.js, uses Just-In-Time (JIT) compilation to convert code into machine code at runtime for faster execution. It also performs optimizations like inlining, hidden classes, and optimizing memory usage to enhance performance.

Answer:

Proxies enable custom behavior for fundamental operations like getting, setting, and deleting properties on objects. They are created with a handler object that defines traps, or methods, for various operations, making them useful for tasks like validation, data binding, and observability.

const target = { name: "Alice" };
const handler = {
  get: (obj, prop) => (prop in obj ? obj[prop] : "Property not found"),
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // "Alice"
console.log(proxy.age);  // "Property not found"
Answer:

Symbols are unique and immutable values used as object keys to avoid property name conflicts. They’re often used for metadata and special properties.

const id = Symbol("id");
const person = { [id]: 123 };
Answer:

Tree shaking is a process used in bundlers (e.g., Webpack) to remove unused code, particularly with ES6 modules. It reduces the bundle size and improves performance by analyzing and eliminating dead code.

Answer:

Decorators add behavior to classes or functions in a readable way. They’re commonly used in frameworks like Angular and experimental features for annotating and modifying classes.

Example: In ES6+ environments, decorators can be applied as:

function log(target) {
  console.log("Logging:", target);
}
@log
class MyClass {}
Answer:

Service Workers run in the background, enabling features like offline support, background sync, and push notifications by intercepting network requests and caching assets, thus improving app resilience and performance.

Answer:

Typed arrays provide a way to handle binary data efficiently. They are used in operations requiring performance, like manipulating image pixels, video streams, or raw data from networks or files.

const buffer = new ArrayBuffer(8);
const view = new Int32Array(buffer);
view[0] = 10;
Answer:

Memoization caches function results based on input arguments, avoiding redundant calculations. It’s useful in recursive algorithms and data-heavy applications.

const memoize = (fn) => {
  const cache = {};
  return (...args) => {
    const key = JSON.stringify(args);
    return cache[key] || (cache[key] = fn(...args));
  };
};
Answer:

Modules encapsulate code, making it reusable and maintainable. With ES6, JavaScript introduced import and export, allowing modularity and scope isolation.

// math.js
export const add = (a, b) => a + b;
// main.js
import { add } from "./math.js";
Answer:

Closures retain access to outer scope variables, but retaining variables indefinitely can cause memory leaks if those variables are no longer needed but still accessible via closures.

Answer:

Debouncing prevents excessive function calls by delaying execution until the last call in a series of rapid events. It’s particularly useful for handling events like resizing or keystrokes.

function debounce(func, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func(...args), delay);
  };
}
Answer:

Lazy loading delays resource loading until needed, which improves initial loading times. This technique is often used for images, scripts, or modules in web development to boost page performance.

Answer:

Throttling limits the rate at which a function executes, such as limiting a function call to once every few milliseconds. This is useful for performance-intensive operations like scrolling.

function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function (...args) {
    if (!lastRan) {
      func.apply(this, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(() => {
        if ((Date.now() - lastRan) >= limit) {
          func.apply(this, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}