You may copy JavaScript syntax yet struggle to predict why "40" + 2 becomes "402", why a closure retains a value, or why a zero-delay timer does not run first. All three follow rules you can trace by hand: + switches to string concatenation the moment one operand is a string, a returned function keeps the variable environment it was created in, and setTimeout(fn, 0) queues a task that waits for the call stack to empty. Trace those rules on real values and output prediction stops being guesswork. Programming Languages collects the other language tutorials on the blog.
JavaScript basics: language, runtime and statement execution
JavaScript is standardised as ECMAScript. Variables, functions, arrays, objects and Promises are core language features. Browser or server runtimes supply host features such as the DOM, fetch and timers. The split matters the moment you move code: Array.prototype.map works in every runtime, while document.querySelector exists only where a DOM does.
Trace in source order:
const learner = "Asha";
let attempts = 2;
attempts += 1;The final bindings are learner === "Asha" and attempts === 3. const prevents rebinding, while let permits reassignment. Both are block-scoped; declare them before use. The assignment statement attempts += 1; updates the binding, after which the expression attempts * 4 evaluates to 12. Engines may compile internally, so JavaScript is not interpreted-only.
JavaScript data types, coercion and equality with exact outputs
Primitive examples are string "JavaScript", number 42, bigint 42n, boolean true, undefined, null and Symbol("id"). In contrast, { topic: "JS" }, [8, 6, 9] and a function are object or callable values. Despite the historical result typeof null === "object", null is primitive. Array.isArray([8, 6, 9]) is true.
Trace const rawScore = "40"; const bonus = 2;:
Expression | Result | Result type |
|---|---|---|
|
| string |
|
| number |
|
| number |
|
| boolean |
|
| boolean |
Use explicit conversion and strict equality. Also, 0.1 + 0.2 evaluates to 0.30000000000000004; Floating point representation: encode and add in IEEE 754 explains why. Finally, 0 || 25 gives 25, but 0 ?? 25 gives 0 because nullish coalescing treats only null and undefined as missing.
JavaScript operators, conditions and loops as a control-flow trace
Operators have jobs: arithmetic uses +, -, *, /, %; comparison uses <, >=, ===, !==; logical work uses &&, ||, !; assignment uses = and +=. With const completed = true; const attempts = 3;, completed && attempts <= 3 is true, so the ternary returns "on-track":
completed && attempts <= 3 ? "on-track" : "retry"For a loop, start with evenTotal = 0 and inspect 1, 2, 3, 4. Only 2 and 4 enter the branch, so the accumulator moves 0 -> 2 -> 6 and finishes at 6.
let evenTotal = 0;
for (let n = 1; n <= 4; n += 1) {
if (n % 2 === 0) evenTotal += n;
}Use if/else for branches, a ternary to select one value, switch for several discrete cases, for...of for explicit iteration, and array methods for transformations. Do not hide several side effects inside a ternary. The falsy values are false, 0, -0, 0n, "", null, undefined and NaN.
JavaScript functions, parameters, scope and closures
In function add(a, b) { return a + b; }, a and b are parameters, 4 and 6 are arguments, and add(4, 6) returns 10. With const multiply = (a, b) => a * b;, multiply(4, 6) returns 24. Arrow and ordinary functions are not interchangeable because they handle this differently.
A closure retains access to its lexical environment:
function makeCounter(start) {
let count = start;
return () => {
count += 1;
return count;
};
}
const next = makeCounter(5);The environment created by makeCounter(5) holds count. The first next() returns 6, the second returns 7, and count is not global. After if (true) { let inside = 7; }, console.log(inside) raises ReferenceError. Function declarations are callable before their textual declaration, but let and const cannot be accessed before initialisation. Declare before use instead of relying on hoisting tricks. Closures in JavaScript: Lexical Scope and the var Loop Trap carries the same mechanism into the var-in-a-loop trap.
JavaScript arrays and objects: model and transform three records
Three progress records model one learner's week:
const attempts = [{ topic: "Types", solved: 8, tasks: 10 }, { topic: "Functions", solved: 6, tasks: 8 }, { topic: "Arrays", solved: 9, tasks: 12 }];attempts.length is 3, attempts[1].topic is "Functions", and valid top-level indexes are 0, 1, 2. Each object names one record's fields. Filtering with item => item.solved >= 8 keeps Types and Arrays. Mapping with item => item.topic produces ["Types", "Arrays"]. Solved reduction moves 0 -> 8 -> 14 -> 23; task reduction moves 0 -> 10 -> 18 -> 30.
Copying avoids an unwanted top-level mutation:
const learner = { name: "Asha", level: 1 };
const promoted = { ...learner, level: 2 };The results are learner.level === 1 and promoted.level === 2. Spread makes a shallow copy, so nested objects still need deliberate handling.
JavaScript worked example: compute a complete learning summary
This function converts the bonus before arithmetic and rejects invalid input:
function summarise(records, bonusText) {
const solved = records.reduce((total, item) => total + item.solved, 0);
const tasks = records.reduce((total, item) => total + item.tasks, 0);
const bonus = Number(bonusText);
if (!Number.isFinite(bonus)) throw new TypeError("Invalid bonus");
const adjustedSolved = Math.min(solved + bonus, tasks);
const percent = adjustedSolved / tasks * 100;
return {
solved,
tasks,
bonus,
adjustedSolved,
percent: percent.toFixed(2),
status: percent >= 75 ? "ready" : "revise"
};
}record | solved accumulator | task accumulator |
|---|---|---|
Types | 8 | 10 |
Functions | 14 | 18 |
Arrays | 23 | 30 |
For bonusText = "2", conversion gives 2; Math.min(23 + 2, 30) gives 25; and 25 / 30 * 100 gives 83.333.... The branch percent >= 75 ? "ready" : "revise" selects "ready", while percent.toFixed(2) gives the string "83.33". The returned value is { solved: 23, tasks: 30, bonus: 2, adjustedSolved: 25, percent: "83.33", status: "ready" }.
With bonusText = "quiz", Number(bonusText) is NaN, so Number.isFinite(bonus) rejects it. With bonusText = "10", the cap makes adjusted solved 30, the displayed percent is "100.00", and status remains "ready".

JavaScript async output, common traps and how questions test them
Trace this program exactly:
console.log("start");
setTimeout(() => console.log("timer"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("end");The output is start, end, promise, timer. Synchronous code finishes first. The resolved Promise reaction runs as a job before the timer task, and a delay of 0 does not mean "run now". Ecma International's ECMA-262, 16th edition, June 2025 is where the coercion, equality, scope and Promise-job rules are specified exactly.
Common traps become manageable when cause and fix stay together:
Form-like strings plus
+make"40" + 2become"402"; validate and convert first.Two fresh objects make
[] === []false; compare the contents required by the task.NaN === NaNis false; useNumber.isNaN.const settings = { mode: "light" }; settings.mode = "dark";is allowed, butsettings = {}raisesTypeError; mutation is not rebinding.
Assessments can ask you to predict output, repair coercion, trace a closure, transform records, or order synchronous and queued work. More than 40 JavaScript Basics questions are already open for practice, such as the UP LT Grade JavaScript Basics previous-year questions. In interviews, explain the trace before coding. For GATE, what transfers is the habit rather than the language: predicting every intermediate value before running the code is the skill programming questions actually test.

JavaScript basics: the short version and the next runnable step
Keep this recall chain: values have types; const prevents rebinding rather than freezing objects; explicit conversion beats accidental coercion; conditions choose paths; functions return values and closures retain lexical state; arrays and objects organise data; queued callbacks run only after current synchronous work.
Now rerun two variations. Change Types from 8/10 to 10/10, keeping the other records unchanged: solved becomes 25, bonus 2 makes adjusted solved 27, 27 / 30 * 100 gives "90.00", and status is "ready". Then restore the original records and change the threshold from 75 to 90: 83.333... >= 90 is false, so status changes from "ready" to "revise".
The Complete JavaScript Course is a structured next step for regular coding practice. Rerun examples, predict results, and explain every conversion or queue transition.




