Debug School

rakesh kumar
rakesh kumar

Posted on

JavaScript Security and Performance Best Practices: Practical Code Examples and Outputs

Understanding JavaScript syntax is only the beginning. Production development also requires understanding which inputs to trust, how objects retain memory, what blocks the browser, and which decisions must remain on the server.

This guide follows the same 36 concepts as JavaScript Interview Concepts: Clear Explanations, Syntax, Examples, and Output. Each section explains the concept, its security implications, its performance implications, a coding example, and the expected output.

How to use this guide

Run each example separately. Examples labeled Browser need browser APIs; the others work in a modern browser console or Node.js. Module examples require separate files. Outputs describe behavior rather than engine-specific object formatting. Timer delays are approximate.

These are educational examples, not complete authentication, payment, or booking implementations. UI checks improve usability, but the backend must enforce authorization, ownership, prices, availability, validation, and resource limits. Never ship server secrets in frontend code.

Security and performance are different concerns: a feature can improve correctness without directly providing either. An optimization should have a measurable benefit on representative workloads.

1. Scope

What it means: Scope determines where a variable is accessible. Use block or function scope for temporary work.

Security: Local scope reduces accidental interference. It does not conceal a secret delivered to the browser.

Performance: Global references can keep temporary data alive unnecessarily. Local scope helps lifetime management, but does not guarantee faster execution.

function previewSize() {
  const temporaryPreview = new Uint8Array(1000000);
  return temporaryPreview.byteLength;
}
console.log(previewSize());
console.log(typeof temporaryPreview);
Enter fullscreen mode Exit fullscreen mode

Output:

1000000
undefined
Enter fullscreen mode Exit fullscreen mode

Why: The array is local. After the call, it can become eligible for collection because no reference escapes. Collection need not happen immediately.

Production practice: Keep temporary data local. Put private API credentials on the server.

2. Closures

What it means: A closure retains access to surrounding lexical bindings. Use it for callbacks and configured operations.

Security: Encapsulation is not encryption. A closure cannot protect a frontend secret from someone controlling that browser.

Performance: A retained callback can keep a large object reachable. Capture only the data needed.

function createVehicleHandler(vehicle) {
  const id = vehicle.id;
  return () => console.log(id);
}
const handler = createVehicleHandler({
  id: 101,
  photos: new Array(10000).fill("photo")
});
handler();
Enter fullscreen mode Exit fullscreen mode

Output:

101
Enter fullscreen mode Exit fullscreen mode

Why: The callback needs only id; it does not reference vehicle or its photos. Avoid a callback that unnecessarily reads from the full object.

Production practice: Unsubscribe obsolete callbacks. Confirm suspected retention using heap snapshots rather than assuming every closure leaks.

3. Hoisting and the Temporal Dead Zone

What it means: Declaration setup happens before statements execute, but initialization rules differ.

Security: Clear initialization avoids logic mistakes; TDZ is not authorization.

Performance: Choose let and const for correctness. There is no universal application-speed improvement over var.

console.log(oldFlag);
var oldFlag = false;

try {
  console.log(permission);
  const permission = false;
} catch (error) {
  console.log(error.name);
}

const canDelete = false;
console.log(canDelete);
Enter fullscreen mode Exit fullscreen mode

Output:

undefined
ReferenceError
false
Enter fullscreen mode Exit fullscreen mode

Why: var starts as undefined; a lexical binding in its TDZ cannot be read.

Production practice: Initialize before use and deny privileged operations when required permission information is missing. Enforce permissions on the server.

4. Event Loop

What it means: The runtime coordinates synchronous work, tasks, and microtasks.

Security: Excessive computation on untrusted input can exhaust CPU or block the executing thread.

Performance: Long tasks freeze browser interaction. Split work or use a Web Worker for substantial computation.

async function sumRows(rows) {
  if (!Array.isArray(rows) || rows.length > 10000) {
    throw new Error("Too many or invalid rows");
  }
  let total = 0;
  for (let i = 0; i < rows.length; i++) {
    if (!Number.isFinite(rows[i])) throw new Error("Invalid number");
    total += rows[i];
    if ((i + 1) % 500 === 0) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
  return total;
}
sumRows([10, 20, 30]).then(console.log);
console.log("Started");
Enter fullscreen mode Exit fullscreen mode

Output:

Started
60
Enter fullscreen mode Exit fullscreen mode

Why: The promise callback runs after synchronous logging. Large inputs yield between chunks; yielding improves responsiveness but does not reduce total computation.

Production practice: Bound work on both client and server. Profile long tasks. Repeated await Promise.resolve() is not an equivalent browser task yield.

5. Promises

What it means: A promise represents an eventual fulfilled or rejected result.

Security: Fulfillment does not mean the value is valid or trusted.

Performance: Promises coordinate asynchronous work; they do not automatically parallelize CPU work.

function validateVehicle(data) {
  if (!Number.isSafeInteger(data?.id) || data.id <= 0) {
    throw new Error("Invalid vehicle");
  }
  return data.id;
}
Promise.resolve({ id: "101" })
  .then(validateVehicle)
  .then(console.log)
  .catch(error => console.log(error.message));
Enter fullscreen mode Exit fullscreen mode

Output:

Invalid vehicle
Enter fullscreen mode Exit fullscreen mode

Why: The API contract expects a numeric ID, not a numeric-looking string. A thrown validation error rejects the next promise.

Production practice: Handle rejections. Check response.ok when using fetch; HTTP error responses do not automatically reject its promise.

6. Async / Await

What it means: await pauses the current async function until an awaited value settles.

Security: Check response status and validate returned data. Async syntax does not add trust.

Performance: Independent operations can overlap. Dependent operations must retain their required order.

async function loadSummary() {
  // These simulate independent asynchronous API results.
  const [vehicles, cities] = await Promise.all([
    Promise.resolve([101, 102]),
    Promise.resolve(["Goa", "Pune"])
  ]);
  if (!Array.isArray(vehicles) || !Array.isArray(cities)) {
    throw new Error("Invalid response");
  }
  console.log(vehicles.length, cities.length);
}
loadSummary();
Enter fullscreen mode Exit fullscreen mode

Output:

2 2
Enter fullscreen mode Exit fullscreen mode

Why: Both results are coordinated together. Real independent network requests can overlap; this simulated example does not benchmark a speedup.

Production practice: Cancel obsolete searches with AbortController. Cancellation does not guarantee the server stopped processing. await does not move CPU work to another thread.

7. The this Keyword

What it means: For ordinary functions, this usually depends on the call site; arrows inherit it lexically.

Security: this.user or this.role does not prove identity or permission.

Performance: Bind reusable callbacks once rather than repeatedly creating bound functions.

class BookingPanel {
  constructor(id) {
    this.id = id;
    this.onClick = this.onClick.bind(this);
  }
  onClick() {
    console.log(this.id);
  }
}
const panel = new BookingPanel(101);
const callback = panel.onClick;
callback();
console.log(callback === panel.onClick);
Enter fullscreen mode Exit fullscreen mode

Output:

101
true
Enter fullscreen mode Exit fullscreen mode

Why: Binding preserves the intended receiver and provides a stable function reference.

Production practice: Retain the handler used by addEventListener so the same reference can be removed. Authorize bookings on the backend.

8. Prototypes

What it means: Objects delegate missing-property lookup through a prototype chain.

Security: Unsafe dynamic writes or recursive merges can permit prototype pollution. Select permitted fields explicitly.

Performance: Prototype methods are shared rather than recreated for every object.

function createVehicle(input) {
  if (!input || !Object.hasOwn(input, "name") ||
      typeof input.name !== "string" || input.name.length > 120) {
    throw new Error("Invalid name");
  }
  return { name: input.name };
}
const vehicle = createVehicle({ name: "Bike", isAdmin: true });
console.log(JSON.stringify(vehicle));
Enter fullscreen mode Exit fullscreen mode

Output:

{"name":"Bike"}
Enter fullscreen mode Exit fullscreen mode

Why: Only the allowed field is copied. The code does not generically merge attacker-selected property paths.

Production practice: Review merge libraries and nested setters. Field selection is one control, not a complete defense for every object-processing operation.

9. Destructuring

What it means: Extract properties or array elements into bindings.

Security: Extraction does not validate types or permissions.

Performance: Rest destructuring creates a new container; avoid copying large objects without a reason.

function readOptions(input) {
  if (!input || typeof input !== "object" || Array.isArray(input)) {
    throw new Error("Invalid options");
  }
  const { city, limit = 20 } = input;
  if (typeof city !== "string" || city.length > 100 ||
      !Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
    throw new Error("Invalid options");
  }
  return { city, limit };
}
console.log(JSON.stringify(readOptions({ city: "Goa" })));
Enter fullscreen mode Exit fullscreen mode

Output:

{"city":"Goa","limit":20}
Enter fullscreen mode Exit fullscreen mode

Why: Destructuring supplies a default, and explicit checks enforce the contract. Defaults apply to undefined, not null.

Production practice: Validate at API boundaries. Do not treat concise syntax as proof of speed.

10. Map, Filter, and Reduce

What it means: Transform, select, and combine array elements.

Security: Limit array size and validate elements before expensive processing.

Performance: Chaining operations can allocate intermediate arrays. A single loop may help for measured bottlenecks.

function availableIds(vehicles) {
  if (!Array.isArray(vehicles) || vehicles.length > 10000) {
    throw new Error("Invalid list");
  }
  const ids = [];
  for (const vehicle of vehicles) {
    if (vehicle?.available === true &&
        Number.isSafeInteger(vehicle.id) && vehicle.id > 0) {
      ids.push(vehicle.id);
    }
  }
  return ids;
}
console.log(JSON.stringify(availableIds([
  { id: 101, available: true },
  { id: 102, available: false }
])));
Enter fullscreen mode Exit fullscreen mode

Output:

[101]
Enter fullscreen mode Exit fullscreen mode

Why: One traversal selects and projects values without an intermediate filtered array.

Production practice: Readable filter().map() is often appropriate for small lists. Benchmark realistic sizes before rewriting it.

11. Debounce and Throttle

What it means: Debounce waits for a pause; throttle limits execution frequency.

Security: Client-side frequency controls can be bypassed. They cannot enforce API rate limits.

Performance: Reduce repeated search requests and event processing. Clean up pending timers.

function debounce(fn, delay) {
  let timer;
  function run(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  }
  run.cancel = () => clearTimeout(timer);
  return run;
}
const search = debounce(query => console.log(query), 30);
search("G");
search("Go");
search("Goa");
Enter fullscreen mode Exit fullscreen mode

Output after approximately 30 milliseconds or later:

Goa
Enter fullscreen mode Exit fullscreen mode

Why: Each call replaces the previous pending timer. A throttle instead permits execution at a limited frequency while events continue.

Production practice: Cancel pending work when a screen unmounts. Enforce server rate limits and cancel stale network requests separately.

12. Memory Management

What it means: Unreachable objects can be garbage-collected; live references keep objects reachable.

Security: Unbounded retention can exhaust resources. Avoid retaining sensitive data unnecessarily.

Performance: Timers, listeners, subscriptions, and caches need controlled lifetimes.

const cache = new Map();
let vehicle = { id: 101 };
cache.set("selected", vehicle);
vehicle = null;
console.log(cache.size);
cache.delete("selected");
console.log(cache.size);

const timer = setInterval(() => {}, 1000);
clearInterval(timer);
console.log("Timer stopped");
Enter fullscreen mode Exit fullscreen mode

Output:

1
0
Timer stopped
Enter fullscreen mode Exit fullscreen mode

Why: Clearing one variable did not remove the cache reference. Deleting it removes that retention path; the example does not prove collection timing.

Production practice: Inspect heap snapshots and retaining paths. Unreachable circular references can be collected.

13. Data Types

What it means: JavaScript distinguishes primitive types and objects.

Security: Do not coerce permission flags blindly: the string "false" is truthy.

Performance: Validate once at a boundary, then use consistent internal types.

function validBooking(input) {
  return input !== null && typeof input === "object" &&
    !Array.isArray(input) &&
    typeof input.confirmed === "boolean" &&
    Number.isSafeInteger(input.days) &&
    input.days >= 1 && input.days <= 30;
}
console.log(validBooking({ confirmed: false, days: 2 }));
console.log(validBooking({ confirmed: "false", days: 2 }));
Enter fullscreen mode Exit fullscreen mode

Output:

true
false
Enter fullscreen mode Exit fullscreen mode

Why: The validator checks the actual type, not how the value looks.

Production practice: Remember typeof null === "object"; use Array.isArray() for arrays. Repeat authoritative validation on the server.

14. null vs undefined

What it means: APIs often use absence for “unchanged” and null for “explicitly clear.” These meanings must be defined by contract.

Security: Confusing these cases can clear protected or required fields accidentally.

Performance: Mainly a correctness concern; neither is universally faster.

function patchPhone(profile, patch) {
  if (!Object.hasOwn(patch, "phone")) return profile;
  if (patch.phone === null) return { ...profile, phone: null };
  if (typeof patch.phone !== "string" || patch.phone.length > 30) {
    throw new Error("Invalid phone");
  }
  return { ...profile, phone: patch.phone };
}
console.log(JSON.stringify(patchPhone({ phone: "123" }, {})));
console.log(JSON.stringify(patchPhone({ phone: "123" }, { phone: null })));
Enter fullscreen mode Exit fullscreen mode

Output:

{"phone":"123"}
{"phone":null}
Enter fullscreen mode Exit fullscreen mode

Production practice: Backend update rules must specify whether the current user may clear each field.

15. Equality and Type Coercion

What it means: Loose equality may convert types; strict equality does not convert operands.

Security: Unexpected coercion can make validation logic accept unintended values.

Performance: Convert deliberately once instead of repeatedly during processing.

function parseId(raw) {
  if (typeof raw !== "string" || raw.length > 15 ||
      !/^[1-9][0-9]*$/.test(raw)) {
    throw new Error("Invalid ID");
  }
  const id = Number(raw);
  if (!Number.isSafeInteger(id)) throw new Error("Unsafe ID");
  return id;
}
console.log("0" == false);
console.log("0" === false);
console.log(parseId("101"));
Enter fullscreen mode Exit fullscreen mode

Output:

true
false
101
Enter fullscreen mode Exit fullscreen mode

Production practice: Prefer predictable comparisons. Validating an ID does not establish ownership of that record.

16. Truthy and Falsy Values

What it means: Conditions convert values to booleans.

Security: A nonempty string such as "false" is truthy and must not be accepted as a permission flag.

Performance: Explicit correctness matters more than speculative speed differences between conditions.

const response = { canDelete: "false" };
console.log(Boolean(response.canDelete));
console.log(response.canDelete === true);
console.log(Boolean([]));
console.log(Boolean(0));
Enter fullscreen mode Exit fullscreen mode

Output:

true
false
true
false
Enter fullscreen mode Exit fullscreen mode

Production practice: Require booleans where the contract specifies booleans. Hiding a button is not backend authorization.

17. NaN and Numeric Checks

What it means: Numbers may be invalid, infinite, fractional, or outside the safely representable integer range.

Security: Reject unreasonable amounts and quantities before calculation.

Performance: Early checks avoid unnecessary downstream work.

function estimate(ratePaise, days) {
  if (!Number.isSafeInteger(ratePaise) || ratePaise < 0 ||
      ratePaise > 100000000 || !Number.isSafeInteger(days) ||
      days < 1 || days > 30) {
    throw new Error("Invalid rate or duration");
  }
  return ratePaise * days;
}
console.log(estimate(50000, 2));
console.log(Number.isNaN(Number("hello")));
console.log(Number.isFinite(Infinity));
Enter fullscreen mode Exit fullscreen mode

Output:

100000
true
false
Enter fullscreen mode Exit fullscreen mode

Production practice: This is a UI estimate. The server retrieves authoritative rates and calculates charges. Define currency, rounding, and integer-range policies.

18. Value vs Reference

What it means: Assignments copy values; an object value refers to an object, so assignments may share it.

Security: Shared mutation can unexpectedly change previously checked configuration or state.

Performance: Sharing avoids allocation, but requires clear ownership.

const original = { price: 500 };
const shared = original;
shared.price = 1;
console.log(original.price);

const source = { price: 500 };
const independent = { ...source };
independent.price = 1;
console.log(source.price);
Enter fullscreen mode Exit fullscreen mode

Output:

1
500
Enter fullscreen mode Exit fullscreen mode

Production practice: Copy when isolated edits are needed. JavaScript passes arguments by value, including object-reference values.

19. Shallow vs Deep Copy

What it means: Shallow copying preserves nested references; a deep copy duplicates supported nested data.

Security: A clone is not sanitized or authorized data.

Performance: Deep copying large structures costs time and memory. Copy only the changed branches when possible.

const booking = {
  customer: { name: "Ashwani" },
  photos: ["a.jpg", "b.jpg"]
};
const updated = {
  ...booking,
  customer: { ...booking.customer, name: "Updated" }
};
console.log(booking.customer.name);
console.log(updated.customer.name);
console.log(updated.photos === booking.photos);
Enter fullscreen mode Exit fullscreen mode

Output:

Ashwani
Updated
true
Enter fullscreen mode Exit fullscreen mode

Production practice: structuredClone() can deeply clone supported values, but cannot clone everything, such as functions. Avoid describing JSON serialization as a universal deep copy.

20. Spread vs Rest

What it means: Spread expands values; rest collects remaining values.

Security: Broad copying can accidentally include fields such as roles or authoritative prices.

Performance: Repeatedly spreading a growing array can result in quadratic copying.

const payload = { displayName: "Ashwani", role: "admin" };
if (typeof payload.displayName !== "string" ||
    payload.displayName.length > 80) {
  throw new Error("Invalid name");
}
const update = { displayName: payload.displayName };
console.log(JSON.stringify(update));

// Avoid repeatedly building [...result, nextId] in a large loop.
const ids = [{ id: 1 }, { id: 2 }].map(row => row.id);
console.log(JSON.stringify(ids));
Enter fullscreen mode Exit fullscreen mode

Output:

{"displayName":"Ashwani"}
[1,2]
Enter fullscreen mode Exit fullscreen mode

Production practice: The backend must independently select allowed update fields. Spread is not inherently insecure; unsafe field selection is the problem.

21. Optional Chaining

What it means: ?. stops an access chain at null or undefined.

Security: It does not validate types or permissions.

Performance: Primarily improves clarity; it is not a general speed optimization.

function showAdminMenu(response) {
  return response?.permissions?.admin === true;
}
console.log(showAdminMenu(null));
console.log(showAdminMenu({ permissions: { admin: "yes" } }));
console.log(showAdminMenu({ permissions: { admin: true } }));
Enter fullscreen mode Exit fullscreen mode

Output:

false
false
true
Enter fullscreen mode Exit fullscreen mode

Production practice: Missing permission information should not grant access. Keep authoritative authorization on the server.

22. Nullish Coalescing

What it means: ?? supplies a fallback only for null or undefined.

Security: Missing security-sensitive values should have restrictive defaults, followed by type checks.

Performance: Mainly a correctness feature, with no universal performance advantage.

const settings = { discount: 0, canExport: undefined };
console.log(settings.discount ?? 10);
console.log(settings.discount || 10);
const canExport = (settings.canExport ?? false) === true;
console.log(canExport);
Enter fullscreen mode Exit fullscreen mode

Output:

0
10
false
Enter fullscreen mode Exit fullscreen mode

Production practice: Preserve valid zero and false values. A fallback alone does not validate an existing string or object.

23. Function Declarations, Expressions, and Arrows

What it means: JavaScript has several function forms with different declaration and this behavior.

Security: Do not execute user-supplied strings using eval() or new Function().

Performance: Avoid repeated unnecessary function construction in measured hot paths.

const operations = new Map([
  ["add", (a, b) => a + b],
  ["multiply", (a, b) => a * b]
]);
function calculate(operation, a, b) {
  if (!Number.isFinite(a) || !Number.isFinite(b)) {
    throw new Error("Invalid numbers");
  }
  const fn = operations.get(operation);
  if (!fn) throw new Error("Unsupported operation");
  return fn(a, b);
}
console.log(calculate("multiply", 5, 2));
Enter fullscreen mode Exit fullscreen mode

Output:

10
Enter fullscreen mode Exit fullscreen mode

Production practice: Select trusted operations rather than compiling arbitrary expressions. Arrow syntax is not automatically faster.

24. Higher-Order Functions

What it means: Functions accept other functions or return functions.

Security: Execute trusted callbacks; do not compile user input into callbacks.

Performance: Avoid repeated scans when a suitable lookup structure can be built once.

const allowedIds = new Set([101, 102, 103]);
const vehicles = [{ id: 101 }, { id: 999 }];
const visible = vehicles.filter(vehicle => allowedIds.has(vehicle.id));
console.log(JSON.stringify(visible));
Enter fullscreen mode Exit fullscreen mode

Output:

[{"id":101}]
Enter fullscreen mode Exit fullscreen mode

Why: The callback uses a Set for membership rather than repeatedly scanning an allowed-ID array.

Production practice: Include Set construction cost when measuring. Filtering visible records does not authorize backend access.

25. call, apply, and bind

What it means: Call a function with a chosen receiver, or create a bound function.

Security: Binding does not make a receiver trustworthy or isolate untrusted code.

Performance: Very large argument arrays passed through apply or spread can exceed engine limits.

function maximum(values) {
  if (!Array.isArray(values) || values.length === 0 ||
      values.length > 100000) throw new Error("Invalid list");
  let result = -Infinity;
  for (const value of values) {
    if (!Number.isFinite(value)) throw new Error("Invalid number");
    if (value > result) result = value;
  }
  return result;
}
console.log(maximum([5, 20, 3]));
Enter fullscreen mode Exit fullscreen mode

Output:

20
Enter fullscreen mode Exit fullscreen mode

Production practice: For large arrays, avoid Math.max.apply(null, values) and Math.max(...values). Reuse bound handlers where appropriate.

26. Currying

What it means: Express a multi-argument operation through successive functions.

Security: Captured configuration is not encrypted or secret.

Performance: Configuring a reusable function once can avoid repeated setup, but currying itself adds functions and closures.

const minimumValidator = minimum => value =>
  Number.isFinite(value) && value >= minimum;

const meetsMinimumAge = minimumValidator(18);
console.log(meetsMinimumAge(20));
console.log(meetsMinimumAge("20"));
console.log(meetsMinimumAge(16));
Enter fullscreen mode Exit fullscreen mode

Output:

true
false
false
Enter fullscreen mode Exit fullscreen mode

Production practice: This assumes the minimum is trusted developer configuration. Validate it too if configurable. Server eligibility checks remain required.

27. Event Propagation

What it means: DOM events follow capture, target, and, for bubbling events, bubble phases.

Security: stopPropagation() and preventDefault() do not protect an API endpoint.

Performance: Accidental duplicate handlers can repeat requests or expensive work.

Browser example:

const parent = document.createElement("div");
const button = document.createElement("button");
parent.append(button);
parent.addEventListener("click", () => console.log("Capture"), true);
button.addEventListener("click", event => {
  console.log("Target");
  event.stopPropagation();
});
parent.addEventListener("click", () => console.log("Bubble"));
button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
Enter fullscreen mode Exit fullscreen mode

Output:

Capture
Target
Enter fullscreen mode Exit fullscreen mode

Why: Stopping propagation at the target prevents the parent's bubble listener. It does not undo the earlier capture listener or stop other listeners on the same target.

Production practice: Bind submission logic once. Use server-side transactions and appropriate idempotency handling for duplicate business operations.

28. Event Delegation

What it means: A parent handles events originating from descendants.

Security: DOM IDs and data-* attributes are editable. Render untrusted names as text instead of HTML.

Performance: One delegated listener can serve many dynamic items.

Browser example:

const list = document.createElement("div");
const button = document.createElement("button");
button.dataset.id = "101";
const label = document.createElement("span");
label.textContent = "Book Bike";
button.append(label);
list.append(button);

list.addEventListener("click", event => {
  if (!(event.target instanceof Element)) return;
  const selected = event.target.closest("button[data-id]");
  if (!selected || !list.contains(selected)) return;
  const id = selected.dataset.id;
  if (!/^[1-9][0-9]*$/.test(id)) return;
  console.log(`Selected: ${id}`);
});
label.dispatchEvent(new MouseEvent("click", { bubbles: true }));
Enter fullscreen mode Exit fullscreen mode

Output:

Selected: 101
Enter fullscreen mode Exit fullscreen mode

Production practice: textContent prevents interpreting a name as HTML. For intentionally supported rich HTML, use an appropriate sanitizer; consider CSP and Trusted Types. The backend validates the ID and permissions.

29. Error Handling

What it means: Handle exceptions with try, catch, and finally.

Security: Avoid exposing tokens, personal information, internal configuration, or raw diagnostic details.

Performance: Bound retries and prevent repeated error-log floods.

async function loadProfile() {
  try {
    await Promise.reject(new Error("Internal diagnostic detail"));
  } catch {
    console.log("Unable to load profile. Please try again.");
    // Send only controlled, redacted diagnostics to monitoring.
  } finally {
    console.log("Hide loader");
  }
}
loadProfile();
Enter fullscreen mode Exit fullscreen mode

Output:

Unable to load profile. Please try again.
Hide loader
Enter fullscreen mode Exit fullscreen mode

Production practice: A synchronous try/catch cannot catch a later callback error merely because the callback was scheduled inside it. Do not convert a failed payment into apparent success.

30. Promise Combinators

What it means: all, allSettled, race, and any coordinate asynchronous results differently.

Security: Every operation still needs authorization. Partial success must be handled according to business rules.

Performance: Limit concurrency rather than launching an unbounded number of requests.

async function loadInBatches(ids, load) {
  if (!Array.isArray(ids) || ids.length > 100 ||
      !ids.every(id => Number.isSafeInteger(id) && id > 0)) {
    throw new Error("Invalid IDs");
  }
  const results = [];
  for (let i = 0; i < ids.length; i += 2) {
    const batch = await Promise.all(ids.slice(i, i + 2).map(load));
    results.push(...batch);
  }
  return results;
}
loadInBatches([101, 102, 103], async id => `Vehicle ${id}`)
  .then(results => console.log(JSON.stringify(results)));
Enter fullscreen mode Exit fullscreen mode

Output:

["Vehicle 101","Vehicle 102","Vehicle 103"]
Enter fullscreen mode Exit fullscreen mode

Production practice: At most two loader calls run concurrently here. A pool can use capacity better. race() and early rejection do not automatically cancel other work. Handle failures at the call site.

31. Modules: import and export

What it means: Modules expose and consume bindings across files.

Security: Module scope does not conceal frontend secrets. Review imported dependencies and keep import destinations controlled.

Performance: Dynamic imports can defer code loading. Bundler configuration determines the generated chunks.

Two-file example:

// pricing.mjs
export function total(rate, days) {
  if (!Number.isSafeInteger(rate) || rate < 0 || rate > 100000000 ||
      !Number.isSafeInteger(days) || days < 1 || days > 30) {
    throw new Error("Invalid input");
  }
  return rate * days;
}
Enter fullscreen mode Exit fullscreen mode
// app.mjs — run: node app.mjs
async function main() {
  const { total } = await import("./pricing.mjs");
  console.log(total(500, 2));
}
main().catch(error => console.error(error.message));
Enter fullscreen mode Exit fullscreen mode

Output:

1000
Enter fullscreen mode Exit fullscreen mode

Production practice: Import optional report or editor code when needed. Measure initial bundle size and loading waterfalls; dynamic imports can add latency when the feature is first opened.

32. Array Mutation

What it means: Some array methods modify the receiver; others produce a new array.

Security: Shared mutation can unexpectedly change data another operation relies on.

Performance: Copies cost memory and time; mutation is appropriate for locally owned temporary data.

const sharedPrices = [500, 200, 800];
const sorted = [...sharedPrices].sort((a, b) => a - b);
console.log(JSON.stringify(sharedPrices));
console.log(JSON.stringify(sorted));

function localSort() {
  const local = [3, 1, 2];
  local.sort((a, b) => a - b);
  return local;
}
console.log(JSON.stringify(localSort()));
Enter fullscreen mode Exit fullscreen mode

Output:

[500,200,800]
[200,500,800]
[1,2,3]
Enter fullscreen mode Exit fullscreen mode

Production practice: Define ownership. Avoid mutating shared UI state accidentally; avoid unnecessary full-array copies in intensive local processing.

33. Map, Set, and Weak Collections

What it means: Use Map for keyed values, Set for uniqueness, and weak collections for metadata that should not retain eligible keys by itself.

Security: An attacker-influenced cache can grow without limits.

Performance: Bound caches. Weak collections do not replace timer or listener cleanup.

const cache = new Map();
const MAX_ENTRIES = 2;
function put(key, value) {
  cache.delete(key);
  cache.set(key, value);
  if (cache.size > MAX_ENTRIES) {
    cache.delete(cache.keys().next().value);
  }
}
put(101, "Bike");
put(102, "Car");
put(103, "Scooter");
console.log(cache.has(101));
console.log(cache.size);
console.log(JSON.stringify([...new Set([1, 1, 2])]));
Enter fullscreen mode Exit fullscreen mode

Output:

false
2
[1,2]
Enter fullscreen mode Exit fullscreen mode

Production practice: This is a bounded insertion-order cache, not a complete LRU cache. Entry-count limits do not limit bytes; control value sizes and expiration too.

34. Property Ownership

What it means: Distinguish own properties from inherited properties.

Security: An inherited field should not satisfy a contract requiring an explicitly supplied field.

Performance: Iterate the fields needed; Object.keys() returns enumerable own string keys.

const user = Object.create({ role: "admin" });
user.name = "Ashwani";
console.log("role" in user);
console.log(Object.hasOwn(user, "role"));
console.log(Object.hasOwn(user, "name"));
console.log(JSON.stringify(Object.keys(user)));
Enter fullscreen mode Exit fullscreen mode

Output:

true
false
true
["name"]
Enter fullscreen mode Exit fullscreen mode

Production practice: Object.hasOwn() avoids a missing or shadowed hasOwnProperty method. Own fields still require validation and authorization.

35. Classes and Inheritance

What it means: Classes define instances; inheritance shares and extends behavior.

Security: Private fields protect language-level invariants, not secrets against a person controlling the client.

Performance: Ordinary methods normally live on the prototype and are shared across instances.

class BookingDraft {
  #days = 1;
  setDays(days) {
    if (!Number.isSafeInteger(days) || days < 1 || days > 30) {
      throw new Error("Invalid duration");
    }
    this.#days = days;
  }
  getDays() { return this.#days; }
}
const first = new BookingDraft();
const second = new BookingDraft();
first.setDays(3);
console.log(first.getDays());
console.log(second.getDays());
console.log(first.getDays === second.getDays);
Enter fullscreen mode Exit fullscreen mode

Output:

3
1
true
Enter fullscreen mode Exit fullscreen mode

Production practice: Per-instance arrow methods may be useful for binding, but allocate separate functions. Backend rules must validate the submitted draft.

36. Iterators and Generators

What it means: Produce values incrementally through iteration; generators pause at yield.

Security: Bound sequence consumption. Potentially infinite or expensive sequences can exhaust resources.

Performance: Lazy processing can avoid large intermediate arrays. Spreading a generator materializes its values again.

function* validatedIds(values, maxItems = 1000) {
  let count = 0;
  for (const value of values) {
    if (count >= maxItems) throw new Error("Too many items");
    if (!Number.isSafeInteger(value) || value <= 0) {
      throw new Error("Invalid ID");
    }
    count++;
    yield value;
  }
}
for (const id of validatedIds([101, 102, 103])) {
  console.log(id);
}
Enter fullscreen mode Exit fullscreen mode

Output:

101
102
103
Enter fullscreen mode Exit fullscreen mode

Production practice: This assumes a trusted iterable implementation and a trusted positive limit. Item limits cannot prevent an individual iterator step from performing expensive work. Streaming validation may fail after earlier items were processed; use appropriate transaction or prevalidation strategies for business writes.

Summary: Security and Performance Checklist for All 36 Concepts

# Concept Main security consideration Main performance consideration
1 Scope Local scope is not secret storage Avoid unnecessary global retention
2 Closures Encapsulation is not encryption Inspect captured-object retention
3 Hoisting and TDZ Initialize permission logic clearly No universal declaration-speed rule
4 Event loop Bound attacker-influenced computation Avoid long tasks and starvation
5 Promises Validate fulfilled data; handle rejection Promises do not parallelize CPU work
6 Async / await Check responses and failure paths Overlap independent work; cancel stale work
7 this Receiver is not identity Reuse bound handlers
8 Prototypes Avoid unsafe dynamic merges Share methods; avoid prototype churn
9 Destructuring Extraction is not validation Avoid unnecessary rest copies
10 Map/filter/reduce Bound and validate collections Measure intermediate allocations
11 Debounce/throttle Enforce rate limits on the backend Reduce repeated work; clear timers
12 Memory management Prevent unbounded retention Clean up listeners, timers, and caches
13 Data types Require explicit types Normalize at boundaries
14 null/undefined Define update and clearing semantics Mainly correctness
15 Equality/coercion Avoid unexpected coercion Convert deliberately once
16 Truthy/falsy Strings are not permission booleans Mainly correctness
17 Numeric checks Validate finite values and bounds Reject invalid work early
18 Value/reference Control shared mutation Balance sharing and copying
19 Copy depth Copying is not sanitization Copy only required branches
20 Spread/rest Select allowed fields Avoid repeated growing copies
21 Optional chaining Null safety is not authorization Mainly readability
22 Nullish coalescing Restrictive defaults plus type checks Mainly correctness
23 Function forms Avoid compiling untrusted strings Avoid unnecessary function construction
24 Higher-order functions Use trusted callbacks Choose suitable lookup structures
25 call/apply/bind Binding is not isolation Avoid enormous argument lists
26 Currying Captured values are not secrets Reuse configuration where useful
27 Propagation Event controls do not secure endpoints Avoid duplicate work
28 Delegation Render text safely; validate IDs server-side Reduce listener count when appropriate
29 Errors Redact sensitive diagnostics Bound retries and log volume
30 Promise combinators Handle partial business failures Bound concurrency; cancel separately
31 Modules Review dependencies; no frontend secrets Measure splitting and lazy-loading trade-offs
32 Mutation Define data ownership Avoid unnecessary copies
33 Collections Bound untrusted cache growth Limit count, bytes, and lifetime
34 Ownership Own properties are still untrusted Iterate required own fields
35 Classes Private fields are not a server boundary Share methods when appropriate
36 Generators Bound consumption and partial effects Preserve laziness when useful

Additional Production Topics to Learn

These 36 concepts provide foundations, but a production guide should also cover:

  • DOM XSS: dangerous sinks, context-sensitive output handling, sanitization, CSP, and Trusted Types.
  • Authentication and sessions: XSS exposure, secure cookies, CSRF protection, and token lifecycle decisions.
  • Backend enforcement: per-request authorization, ownership checks, trusted pricing, transactions, rate limits, and idempotency.
  • Prototype pollution and ReDoS: safe object handling, regular-expression design, and input-size limits.
  • Safe URLs and messaging: approved URL schemes and destinations, postMessage origin validation, and message-schema validation.
  • Dependency security: package review, lockfiles, maintained versions, and third-party script control.
  • Rendering and delivery: layout thrashing, animation scheduling, virtualization, pagination, bundle size, caching, and third-party script costs.

How to Verify Security and Performance

Security verification

Test missing, malformed, oversized, inherited, and unexpected values. Attempt API requests directly with unauthorized IDs and changed roles or prices. Confirm that the backend rejects the operation even when browser checks are removed. Use dedicated test accounts and authorized test environments.

Performance verification

Record a baseline on representative hardware and data. Use browser Performance traces to inspect long tasks and rendering work, heap snapshots to inspect retention, and Network traces to inspect duplicate requests and bundle loading. Compare the same workload before and after the change, including memory and failure behavior. Do not infer improvement from code length or a single tiny timing run.

References and Further Reading

A reliable application combines clear JavaScript, explicit trust boundaries, controlled resource use, and measured improvements. Every feature should have understandable failure behavior and a clear owner for its business rules.

Top comments (0)