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);
Output:
1000000
undefined
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();
Output:
101
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);
Output:
undefined
ReferenceError
false
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");
Output:
Started
60
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));
Output:
Invalid vehicle
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();
Output:
2 2
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);
Output:
101
true
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));
Output:
{"name":"Bike"}
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" })));
Output:
{"city":"Goa","limit":20}
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 }
])));
Output:
[101]
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");
Output after approximately 30 milliseconds or later:
Goa
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");
Output:
1
0
Timer stopped
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 }));
Output:
true
false
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 })));
Output:
{"phone":"123"}
{"phone":null}
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"));
Output:
true
false
101
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));
Output:
true
false
true
false
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));
Output:
100000
true
false
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);
Output:
1
500
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);
Output:
Ashwani
Updated
true
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));
Output:
{"displayName":"Ashwani"}
[1,2]
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 } }));
Output:
false
false
true
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);
Output:
0
10
false
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));
Output:
10
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));
Output:
[{"id":101}]
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]));
Output:
20
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));
Output:
true
false
false
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 }));
Output:
Capture
Target
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 }));
Output:
Selected: 101
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();
Output:
Unable to load profile. Please try again.
Hide loader
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)));
Output:
["Vehicle 101","Vehicle 102","Vehicle 103"]
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;
}
// 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));
Output:
1000
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()));
Output:
[500,200,800]
[200,500,800]
[1,2,3]
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])]));
Output:
false
2
[1,2]
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)));
Output:
true
false
true
["name"]
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);
Output:
3
1
true
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);
}
Output:
101
102
103
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,
postMessageorigin 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
- MDN JavaScript Guide
- MDN Closures
- MDN JavaScript Execution Model
- MDN Memory Management
- MDN JavaScript Performance Optimization
- MDN innerHTML Security Considerations
- OWASP Web Frontend Security Cheat Sheet
- OWASP JavaScript and TypeScript Security Cheat Sheet
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)