Week 1 · JavaScript basics: values, functions and the DOM
The one-minute story
JavaScript is the waiter in a restaurant. HTML is the menu (what exists), CSS is the decoration (how it looks), and JavaScript is the waiter who actually does things: takes your order when you click a button, brings a new dish without rebuilding the restaurant, and remembers what each table asked for.
The waiter is fast but has odd habits. Ask him to add the text "5" and the number 3 and he does not complain, he hands you "53". Ask him for a name he was never given and he says undefined instead of crashing. Most of this week is learning those habits (types, coercion, hoisting, this) so you can predict exactly what he will do. The exam is full of "predict the output" questions, and they are all about these habits.
By the end of this week you can:
- explain where JavaScript runs and run it in the browser console and in Node;
- predict the output of code using
var,let,const, hoisting,==and===; - write functions (declaration, expression, arrow) and use
map,filter,reduce; - use objects, destructuring, the spread operator and JSON;
- read and write a small DOM program with events.
Words you need
- JavaScript (JS) - the programming language of the web; runs in every browser and also outside it (Node).
- Node.js - a program that runs JavaScript on your computer or server, without a browser.
- Console - the place where
console.logprints (browser dev tools, or the terminal in Node). - Scope - the region of code where a name is visible.
- Hoisting - declarations are registered before the code runs, so some names exist "early".
- Temporal dead zone (TDZ) - the stretch between the start of a block and the line of a
let/const, where using the name is an error. - Type coercion - JavaScript silently converting a value to another type (
"5" * 2becomes 10). - Truthy / falsy - how a value behaves inside an
iftest. - Arrow function - short function syntax
(x) => x * 2, with its own rules forthis. - Callback - a function you hand to another function to be called later.
- Destructuring - unpacking array items or object properties into variables.
- JSON - a text format for data that looks like a JS object;
JSON.stringifyandJSON.parseconvert. - DOM - the page as a tree of objects that JavaScript can read and change.
Idea 1: What JavaScript is and where it runs
JavaScript was created for the browser, but the language itself is just a language. Two places run it:
| Where | What you get | Typical use |
|---|---|---|
| Browser | the language plus document, window, fetch, localStorage, events | making pages interactive; your Vue app |
| Node.js | the language plus files, network servers, require/import | tools, build steps (npm), servers |
Both use an engine (Chrome and Node use V8) that reads your code and runs it. JavaScript is single-threaded: one thing at a time (week 2 shows how it still waits for the network without freezing).
Put JavaScript in a page with a script tag. It runs when the browser reaches it.
<!DOCTYPE html>
<html>
<body>
<h1 id="title">Hello</h1>
<script src="app.js"></script> <!-- put at the END of body, so the h1 exists -->
</body>
</html>
// app.js
console.log("page loaded"); // goes to the browser console (F12)
document.getElementById("title").textContent = "Hi from JS";
console.log is your best friend: it prints any values, separated by spaces. Use console.log(a, b) to see two things at once. A script tag with type="module" (week 2) is deferred automatically; a normal one blocks the page while it loads, which is why it goes at the end of body (or gets the defer attribute).
(a) Can document.getElementById run in Node? (b) Can console.log run in the browser? (c) Which runs a script file named tool.js from the terminal?
Need a hint?
Node has no page, so no document.
Show the full solution
(a) No, there is no page in Node (document is not defined). (b) Yes, it prints in the dev-tools console. (c) node tool.js.
Idea 2: Variables, scope and hoisting
Three ways to declare a variable:
var | let | const | |
|---|---|---|---|
| Scope | function (ignores blocks) | block { } | block { } |
| Re-assign | yes | yes | no |
| Re-declare in same scope | yes | error | error |
| Hoisted | yes, starts as undefined | yes, but in the TDZ | yes, but in the TDZ |
Use const by default, let when the value must change, and avoid var. const stops re-assignment of the name; it does not freeze the object inside (you can still push into a const array).
{ var v = 1; let l = 2; }
console.log(v); // 1 var leaks out of the block
console.log(typeof l); // undefined l is not visible here
console.log(a); // undefined (var is hoisted, value not yet set)
var a = 5;
try { console.log(b); } catch (e) { console.log(e.name); } // ReferenceError (TDZ)
let b = 5;
const list = [1, 2];
list.push(3); // allowed
console.log(list); // [ 1, 2, 3 ]
try { list = []; } catch (e) { console.log(e.name); } // TypeError
Hoisting in one sentence: before running, JavaScript scans for declarations. var x is created and set to undefined; a function declaration is created with its whole body; let/const are created but locked until their line (TDZ).
console.log(add(2, 3)); // 5 declaration hoisted with its body
function add(a, b) { return a + b; }
try { sub(5, 2); } catch (e) { console.log(e.name + ": " + e.message); }
var sub = function (a, b) { return a - b; }; // only `var sub` is hoisted (as undefined)
// prints: TypeError: sub is not a function
The most asked scope puzzle is var in a loop with a delayed callback:
for (var i = 0; i < 3; i++) setTimeout(() => console.log("var", i), 0);
for (let j = 0; j < 3; j++) setTimeout(() => console.log("let", j), 0);
// var 3, var 3, var 3, let 0, let 1, let 2
There is one i shared by all three callbacks, and it is 3 when the loop ends. let makes a fresh j for each round.
typeofof an undeclared name is"undefined", buttypeofof aletin its TDZ throws.varinsideif/foris visible outside it.letis not.const obj = {}; obj.x = 1;is fine.obj = {}is the error.- Assigning to a name that was never declared (
total = 5) quietly makes a global variable in sloppy mode. Do not.
Idea 3: Types, coercion and equality
There are seven primitive types and one object type:
| Type | Example | typeof |
|---|---|---|
| number | 42, 3.14, NaN, Infinity | "number" |
| string | "hi", 'hi' | "string" |
| boolean | true, false | "boolean" |
| undefined | a variable never given a value | "undefined" |
| null | "deliberately empty" | "object" (an old bug) |
| symbol | Symbol("id") (a unique key) | "symbol" |
| bigint | 12345678901234567890n | "bigint" |
| object | {}, [], functions, dates | "object" (functions: "function") |
There is one number type (no int/float split), and 0.1 + 0.2 is 0.30000000000000004.
console.log(typeof 5, typeof "5", typeof null, typeof undefined, typeof [], typeof NaN, typeof (() => 1));
// number string object undefined object number function
console.log(Array.isArray([])); // true (the right way to test for an array)
Coercion: + joins, everything else calculates
console.log("5" + 3); // 53 one side is a string, so + joins
console.log("5" - 3); // 2 - always means numbers
console.log("5" * "2"); // 10
console.log(true + 1); // 2 true becomes 1
console.log(null + 1); // 1 null becomes 0
console.log(undefined + 1);// NaN undefined becomes NaN
console.log("abc" * 2); // NaN "not a number"
console.log(1 + 2 + "3"); // 33 left to right: 3, then "33"
console.log("1" + 2 + 3); // 123
console.log(parseInt("42px"), Number("42px"), Number(""), parseInt(undefined));
// 42 NaN 0 NaN
== converts types, === does not. Always use === unless you know why.
console.log(0 == "", "0" == false, null == undefined); // true true true
console.log(0 === "", null === undefined); // false false
console.log(NaN == NaN, NaN === NaN, Number.isNaN(NaN)); // false false true
console.log([1] == 1, [] == false); // true true
Truthy and falsy
There are exactly eight falsy values: false, 0, -0, 0n, "", null, undefined, NaN. Everything else is truthy, including "0", "false", [] and {}.
console.log(Boolean([]), Boolean(""), Boolean("0"), !!{}, !!NaN);
// true false true true false
typeof nullis"object";typeof NaNis"number";typeof []is"object"."5" + 3is"53"but"5" - 3is2.NaNis not equal to itself. Test withNumber.isNaN(x).[]and{}are truthy;"0"is truthy;0is falsy.parseInt("12abc")is 12 (reads digits until it fails);Number("12abc")isNaN.
console.log(1 + "2" + 3, 1 + 2 + "3", "10" / 2, "a" - 1, [] + [], typeof typeof 1);Need a hint?
Go left to right. typeof always returns a string.
Show the full solution
"123", "33", 5, NaN, "" (empty string, two empty arrays become empty strings), "string". Printed: 123 33 5 NaN string (there is an empty string between the NaN and "string").
Idea 4: Operators, template literals, control flow and loops
- Arithmetic:
+ - * / % **(2 ** 3is 8). Increments:i++,i += 2. - Compare:
< > <= >= === !==. - Logic:
&&,||,!. They return one of the operands, not just true/false.||returns the first truthy operand (or the last),&&returns the first falsy operand (or the last). The exam loves this. ??(nullish coalescing) falls back only fornullorundefined:0 ?? 5is0, but0 || 5is5.- Ternary:
cond ? a : b.
console.log(2 && 4, 0 && 2, 2 || 0, 0 || "", "" || "dflt"); // 4 0 2 "" dflt
console.log(0 || 5, 0 ?? 5, null ?? 5, undefined ?? "x"); // 5 0 5 x
const age = 17;
console.log(age >= 18 ? "adult" : "minor"); // minor
Template literals use backticks, allow ${expression} and span lines:
const name = "Asha", n = 3;
console.log(`Hello ${name}, you have ${n * 2} items.`); // Hello Asha, you have 6 items.
Control flow and loops
const marks = 72;
if (marks >= 90) console.log("A");
else if (marks >= 60) console.log("B"); // B
else console.log("C");
switch (marks > 50) { // switch uses ===, and needs break
case true: console.log("pass"); break;
default: console.log("fail");
}
for (let i = 0; i < 3; i++) console.log(i); // 0 1 2
let k = 0; while (k < 2) { k++; } // k is 2
for (const x of ["a", "b"]) console.log(x); // a b values
for (const key in { p: 1, q: 2 }) console.log(key); // p q keys (strings)
for (const idx in ["a", "b"]) console.log(idx, typeof idx); // 0 string / 1 string
for...of walks the values of something iterable (array, string). for...in walks the keys (property names) of an object, always as strings. On an array for...in gives indexes as strings, so prefer for...of for arrays. break leaves the loop, continue skips to the next round.
Print the numbers 1 to 15, but write "Fizz" for multiples of 3 and "Buzz" for multiples of 5 (and "FizzBuzz" for both).
Need a hint?
Test the "both" case first, using %.
Show the full solution
for (let i = 1; i <= 15; i++) {
if (i % 15 === 0) console.log("FizzBuzz");
else if (i % 3 === 0) console.log("Fizz");
else if (i % 5 === 0) console.log("Buzz");
else console.log(i);
}
It prints 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz.
Idea 5: Functions
Functions are values (first-class): you can store them in variables, pass them to other functions and return them.
function square(x) { return x * x; } // declaration: hoisted
const cube = function (x) { return x ** 3; }; // expression: not usable before this line
const double = (x) => x * 2; // arrow, one expression: implicit return
const make = (a, b) => ({ sum: a + b }); // returning an object literal needs ( )
const greet = (who = "friend") => `Hi ${who}`; // default parameter
console.log(square(3), cube(2), double(4), make(1, 2), greet(), greet("Ravi"));
// 9 8 8 { sum: 3 } Hi friend Hi Ravi
console.log((function () {})()); // undefined no return means undefined
// first-class: pass a function, return a function
const apply = (f, v) => f(v);
console.log(apply(square, 5)); // 25
const adder = (n) => (x) => x + n;
console.log(adder(10)(5)); // 15
const nums = (...all) => all.length; // rest parameters collect the extras
console.log(nums(1, 2, 3)); // 3
this: a first look
Inside a normal function, this means "the object before the dot at the call site". An arrow function has no this of its own: it uses the this of the code around it. Week 2 gives the full rules (call, bind, apply).
const user = { nick: "Asha", hi() { return "Hi " + this.nick; } };
console.log(user.hi()); // Hi Asha object before the dot is user
const detached = user.hi;
try { console.log(detached()); } catch (e) { console.log("lost this"); }
// Hi undefined (sloppy script) or "lost this" (strict mode/module): the dot is gone
- A function declaration works before its line; a function expression (even
const f = () => {}) does not. x => { value }with braces needs an explicitreturn;x => valuereturns it implicitly.(x) => ({ a: x }): parentheses so the braces are an object, not a function body.- Extracting a method (
const f = obj.method) losesthis.
Rewrite as an arrow function: function area(w, h = 1) { return w * h; }. What do area(3) and area(3, 4) give?
Need a hint?
Parameters stay the same, body becomes an expression.
Show the full solution
const area = (w, h = 1) => w * h; gives 3 and 12.
Idea 6: Arrays and their methods
const a = [10, 20, 30];
console.log(a.push(40), a); // 4 [ 10, 20, 30, 40 ] push returns the NEW LENGTH
console.log(a.pop(), a); // 40 [ 10, 20, 30 ] pop returns the removed item
console.log(a.length, a[0], a[5]); // 3 10 undefined
console.log(a.slice(1, 3), a); // [ 20, 30 ] [ 10, 20, 30 ] slice COPIES, original unchanged
console.log(a.splice(1, 1), a); // [ 20 ] [ 10, 30 ] splice CHANGES the original
const b = [...a, 99]; // spread: copy into a new array
console.log(b, [..."hi"]); // [ 10, 30, 99 ] [ 'h', 'i' ]
The "big four" take a callback and do not change the original array:
| Method | Returns | Callback does |
|---|---|---|
map(f) | new array, same length | transform each item |
filter(f) | new array, shorter | keep items where f is truthy |
reduce(f, start) | one value | combine: f(accumulator, item) |
forEach(f) | undefined | just do something per item |
find(f) | first matching item, or undefined |
const n = [1, 2, 3, 4, 5];
console.log(n.map(x => x * 2)); // [ 2, 4, 6, 8, 10 ]
console.log(n.filter(x => x % 2 === 1)); // [ 1, 3, 5 ]
console.log(n.reduce((acc, x) => acc + x, 0)); // 15
console.log(n.find(x => x > 3), n.find(x => x > 9)); // 4 undefined
console.log(n.indexOf(3), n.includes(7)); // 2 false
console.log(n.forEach(x => x)); // undefined
console.log([10, 9, 1].sort()); // [ 1, 10, 9 ] default sort compares AS TEXT
console.log([10, 9, 1].sort((p, q) => p - q)); // [ 1, 9, 10 ]
console.log(["1", "2", "3"].map(parseInt)); // [ 1, NaN, NaN ] map also passes the index!
const data = [
{ name: "Alpha", score: 40 }, { name: "Beta", score: 30 },
{ name: "Charlie", score: 50 }, { name: "Delta" },
];
const total = data
.map(p => parseInt(p.score) || 0)
.filter(score => score >= 40)
.reduce((acc, score) => acc + score, 0);
console.log("Total Score:", total);
Step by step: map turns each score into a number: 40, 30, 50, and for Delta parseInt(undefined) is NaN, which is falsy, so NaN || 0 is 0: [40, 30, 50, 0]. filter keeps >= 40: [40, 50]. reduce adds from 0: 90. Output: Total Score: 90.
pushreturns the length,popreturns the item,forEachreturnsundefined,splicemutates,slicedoes not.- Arrays are objects:
b = acopies the reference;b = [...a]copies the items (a shallow copy). ["1","2","3"].map(parseInt)is[1, NaN, NaN].sort()without a function sorts as strings.
[3, 8, 1, 9]: keep numbers above 2, double them, then add them up.
Need a hint?
filter, then map, then reduce.
Show the full solution
[3,8,1,9].filter(x => x > 2).map(x => x * 2).reduce((a, x) => a + x, 0): filter gives [3,8,9], map [6,16,18], reduce 40.
Idea 7: Objects, destructuring and JSON
An object is a bag of key: value pairs. A function stored as a value is a method.
const person = { name: "Asha", age: 20, skills: ["js", "py"], greet() { return "Hi " + this.name; } };
console.log(person.name, person["age"], person.greet()); // Asha 20 Hi Asha
person.city = "Chennai"; delete person.age; // add, delete
console.log("city" in person, person.age, person.zip); // true undefined undefined
console.log(Object.keys({ a: 1, b: 2 }), Object.entries({ a: 1 }));
// [ 'a', 'b' ] [ [ 'a', 1 ] ]
Destructuring and spread are everywhere in Vue code:
const [first, , third = 0] = [1, 2]; // positions; third gets its default
const { name, skills: [s1] } = person; // names must match; nested allowed
console.log(first, third, name, s1); // 1 0 Asha js
const base = { x: 1, y: 2 };
const copy = { ...base, y: 20, z: 3 }; // copy, later keys win
console.log(copy, base); // { x: 1, y: 20, z: 3 } { x: 1, y: 2 }
const extra = { ...(false && { secret: 1 }) }; // spreading false adds nothing
console.log(extra); // {}
function isValidAge(age) { return !(age > 18); }
const validAgeParams = { voter_id: 123, constituency: "New Delhi" };
let some_value; // undefined
console.log(isValidAge(some_value) ? { name: "ABC", ...(!isValidAge(19) && validAgeParams) }
: { name: "XYZ" });
undefined > 18 is false, so isValidAge(undefined) is true: take the first branch. isValidAge(19) is !(true) = false, so !isValidAge(19) is true, and true && validAgeParams is validAgeParams, which is spread in. Output: { name: 'ABC', voter_id: 123, constituency: 'New Delhi' }.
JSON
JSON is text. Keys and strings need double quotes; no functions, no comments, no trailing commas.
const text = JSON.stringify({ a: 1, b: undefined, c: () => 1, d: [1, undefined], e: "hi" });
console.log(text); // {"a":1,"d":[1,null],"e":"hi"}
const back = JSON.parse('{"id":7,"tags":["x","y"]}');
console.log(back.id, back.tags[1]); // 7 y
stringify drops undefined and function values in objects (and turns them into null inside arrays). Browser storage and fetch bodies are text, so you will convert with these two calls all the time.
const {a, b: {c}} = {a: 1, b: {c: 2, d: 3}}; What are a and c? Is b defined?
Need a hint?
Only names written on the left become variables.
Show the full solution
a is 1, c is 2. b is not defined (it is only a path to reach c), and neither is d.
Idea 8: The DOM and events
The browser turns your HTML into a tree of objects called the DOM. document is the root. JavaScript finds elements, reads or changes them, and reacts to events (click, input, submit, keydown).
// browser only
const out = document.querySelector("#out"); // first match of a CSS selector
const all = document.querySelectorAll(".item"); // list of every match
out.textContent = "changed"; // set text (safe)
out.style.color = "red";
out.classList.add("big");
document.querySelector("button").addEventListener("click", () => { out.textContent = "clicked"; });
input.value is always a string, even for type="number", so convert before calculating. That is the classic calculator bug.
<input id="a" type="number"> <select id="op"><option>+</option><option>-</option>
<option>*</option><option>/</option></select> <input id="b" type="number">
<button id="go">=</button> <span id="out"></span>
<script src="calc.js"></script>
// calc.js
function calculate(a, op, b) {
switch (op) {
case "+": return a + b;
case "-": return a - b;
case "*": return a * b;
case "/": return b === 0 ? "cannot divide by 0" : a / b;
default: return "?";
}
}
document.querySelector("#go").addEventListener("click", () => {
const a = Number(document.querySelector("#a").value); // value is a string: convert!
const b = Number(document.querySelector("#b").value);
const op = document.querySelector("#op").value;
document.querySelector("#out").textContent = calculate(a, op, b);
});
The pure function calculate can be tested in Node: calculate(6, "*", 7) is 42. Without Number(...) the plus would join text: "2" + "3" is "23". Frameworks like Vue (later weeks) remove this manual querySelector plumbing by tying page and data together.
Examiners ask "what is the difference between let and var" and "why ===" even in a Vue project viva. Keep const/let only in your code and use === everywhere, so you can say it is a deliberate habit.
Exam radar
The keyword scan for this week matches about 423 distinct graded questions (about 570 of 848 in Quiz 1, 320 of 608 in Quiz 2 and 623 of 1471 in the End Term, by keyword, so approximate). Honest caution: "JavaScript" words appear in nearly every code question, and a large share of them are Vue questions whose answer is still decided by plain JavaScript rules. Pure-JavaScript "predict the output" questions mostly come in Quiz 2 and the End Term and test:
- Array chains with
map/filter/reduceandparseInt(...) || 0(the score-total question above). - Operators returning operands (
2 && 4,2 || 0,0 && 2). - Object spread with
&&to add keys conditionally. thiswithcalland arrow functions (week 2) andvarbecoming a global.- Storage values being strings (
"10" + 5), covered in week 3.
Typical questions: "what is logged?", "which statement about let/const is false?", "what does x.sort() return for numbers?".
- Strings:
+joins and*calculates. A value read frominputor storage is a string. &&/||return an operand;??ignores0and"".parseInt(""),parseInt(undefined)areNaN, andNaN || 0is0.forEachreturnsundefined;mapreturns an array.
Quick quiz
What does this print? console.log("5" + 3, "5" - 3)
- 8 2
- 53 53
- 53 2
- 8 8
+ with a string joins text ("53"); - always does arithmetic (2).What is the output? console.log(2 && 4, 0 || 7, 0 ?? 7)
- 2 7 7
- 4 7 0
- true true true
- 4 0 7
&& returns 4 (last truthy), || skips the falsy 0 and returns 7, ?? keeps 0 because it is not null/undefined.What is logged? for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 0)
- 0 1 2
- 3 3 3
- 2 2 2
- Nothing
var i, and the loop ends with i = 3 before any callback runs. With let it would be 0 1 2.Select all TRUE statements. (select all that apply)
typeof nullis "object".const arr = []; arr.push(1)is allowed.NaN === NaNis true.letvariables are function scoped.
NaN is never equal to itself, and let is block scoped.[10, 9, 1].sort() returns...
- [1, 9, 10]
- [1, 10, 9]
- [10, 9, 1]
- an error
Which statement about slice and splice is correct?
slicereturns a copy and leaves the array unchanged;splicechanges the array.- Both change the array.
slicechanges the array;splicedoes not.- Both return a new array and leave the original unchanged.
splice removes/inserts in place; slice only copies a part.Viva: say it out loud
What is the difference between var, let and const?
var is function scoped and hoisted as undefined; let and const are block scoped and sit in the temporal dead zone until their line. const cannot be re-assigned, but an object held in a const can still be changed. I use const by default and let only when needed.
What is hoisting?
Before the code runs, JavaScript registers the declarations. A function declaration is available with its body, a var exists as undefined, and let/const exist but cannot be used before their line.
Why prefer === over ==?
== converts types first, so 0 == "" and "0" == false are true, which hides bugs. === compares type and value with no conversion.
What is a callback and where did you use one?
A callback is a function passed to another function, to be called later. Examples are array.map(fn) and an event handler passed to addEventListener.
What is the difference between map, filter and reduce?
map makes a new array of the same length by transforming each item, filter keeps only items that pass a test, and reduce folds the whole array into one value. None of them changes the original array.
What is JSON and how do you use it in JavaScript?
JSON is a text format for data. JSON.stringify turns an object into text (for fetch bodies or storage), and JSON.parse turns text back into an object.
What is the DOM?
It is the page represented as a tree of objects. JavaScript reads and changes it with querySelector, textContent and addEventListener.
Recap
- JS runs in the browser (with
document,fetch, storage) and in Node (with files and servers); it is single-threaded. constby default,letif it changes, novar.varignores blocks;let/consthave a TDZ; function declarations hoist whole.+joins when a string is involved; other operators convert to numbers. Use===. Eight falsy values;[]and{}are truthy.&&/||return operands;??only skipsnull/undefined.- Functions are values. Arrow functions are short and take
thisfrom outside. map/filter/reducereturn new values;push/pop/splicechange the array;forEachreturnsundefined.- Spread
...copies; destructuring unpacks; JSON is text. input.valueis a string; convert before calculating.
typeof null-> "object";typeof NaN-> "number";Array.isArray(x)- Falsy:
false 0 -0 0n "" null undefined NaN "5" + 3-> "53";"5" - 3-> 2;1 + 2 + "3"-> "33"for (const v of arr)values;for (const k in obj)keys (strings)arr.reduce((acc, x) => acc + x, 0);arr.map(f);arr.filter(f)const { a, b: { c } } = obj;const [x, y] = arr;{ ...obj, k: v }[...arr, v]JSON.stringify(obj)/JSON.parse(text)document.querySelector("#id"),el.addEventListener("click", fn),el.textContent
- MAD1 week 8 - The frontend: JavaScript, the DOM and fetch: The MAD1 frontend week (JavaScript, DOM, fetch) is the doorway to MAD2's JavaScript weeks.
Practise with real exam questions
These come straight from past papers (with the real figures and the official answer key). Open 60 real questions on this week All Quiz 1 papers