Unit 4: JavaScript Fundamentals

CSE326 — Internet Programming 8 min read

I. Introduction to JavaScript

JavaScript was written by Brendan Eich at Netscape in May 1995 (prototyped in ten days, shipped as "LiveScript", renamed for marketing reasons) to add behaviour to static HTML pages. It was standardised as ECMAScript by Ecma International (ECMA-262, 1st edition, June 1997); the modern language dates from ES6 / ES2015 (June 2015), which introduced let, const, arrow functions, classes and template literals, after which releases became annual (ES2016, ES2017, …).

Defining characteristics assumed by every later section:

  • Interpreted and JIT-compiled: source is executed by an engine (V8 in Chrome/Node.js, SpiderMonkey in Firefox) with no separate compile step; syntax errors surface only when the script is parsed.
  • Dynamically typed: a variable has no declared type; the value carries the type, so let x = 5; x = "five"; is legal.
  • Weakly typed: the engine coerces types automatically, so "5" * 2 yields the number 10.
  • Multi-paradigm: supports imperative, functional (functions are first-class values) and prototype-based object-oriented styles.
  • Single-threaded and event-driven: one call stack plus an event loop; long computations block the page.
  • Host-embedded: the language core (ECMAScript) has no I/O. Browsers add the DOM and window; Node.js adds fs and require.
  • Placement in a page: <script src="app.js" defer></script> before </head>, or a <script> block before </body>, so the DOM exists before the code runs.
  • Strict mode: "use strict"; at the top of a file or function bans undeclared variables and silent errors; ES modules are strict automatically.

II. Bindings and Types — how values are named and classified

A binding associates an identifier with a value in memory; a type determines the operations legal on that value.

A. Variables and Constants

A variable is a named, re-assignable binding; a constant is a binding that cannot be re-assigned after initialisation.

  • var (pre-ES6): function-scoped, hoisted and initialised to undefined, re-declarable. console.log(a); var a = 1; prints undefined rather than throwing.
  • let: block-scoped, re-assignable, not re-declarable in the same block; in the temporal dead zone from block entry until its declaration, so early access throws ReferenceError.
  • const: block-scoped and must be initialised at declaration (const PI = 3.14159;). Re-assignment throws TypeError: Assignment to constant variable.
    • Constant binding ≠ immutable value: const arr = [1,2]; arr.push(3); is legal — only the reference is frozen. Object.freeze(obj) freezes contents.
  • Naming rules: must begin with a letter, _ or $; case-sensitive (total ≠ Total); reserved words such as class and return are forbidden. Convention: camelCase for variables, UPPER_SNAKE_CASE for fixed constants.
  • Practice: default to const, use let only when re-assignment is needed, avoid var.

B. Data Types

JavaScript has seven primitive types plus objects; primitives are immutable and copied by value, objects are copied by reference.

Type Example typeof result
number 42, 3.14 "number"
string "hi" "string"
boolean true "boolean"
undefined declared, unassigned "undefined"
null intentional empty value "object" (historic bug)
symbol (ES6) Symbol("id") "symbol"
bigint (ES2020) 9007199254740993n "bigint"
object / array / function {}, [], f(){} "object" / "object" / "function"
  • Value vs reference: let a = {n:1}; let b = a; b.n = 2; leaves a.n === 2, whereas the same with numbers leaves the original unchanged.
  • Detecting arrays: typeof [] gives "object", so use Array.isArray([])true.
  • null vs undefined: undefined is the engine's "no value assigned"; null is the programmer's deliberate "empty".

C. Numbers and Boolean Values

All ordinary numbers are IEEE 754 64-bit doubles — there is no separate integer type.

  • Precision limit: 53 bits of mantissa, so Number.MAX_SAFE_INTEGER === 9007199254740991 (2⁵³−1) and 0.1 + 0.2 === 0.30000000000000004. Compare with a tolerance: Math.abs(x - y) < 1e-9.
  • Special values: Infinity (1/0), -Infinity, and NaN from invalid arithmetic ("abc" * 2). NaN !== NaN; test with Number.isNaN(x).
  • Conversion: Number("42")42; parseInt("42px", 10)42; parseFloat("3.5rem")3.5; (3.14159).toFixed(2)"3.14" (a string).
  • Math helpers: Math.round(4.5)=5, Math.floor(-1.2)=−2, Math.trunc(-1.2)=−1, Math.random() ∈ [0,1).
  • Booleans: only true / false. The eight falsy values are false, 0, -0, 0n, "", null, undefined, NaN; every other value, including [] and {}, is truthy. Boolean(x) or !!x forces conversion.

D. Strings and String Methods

A string is an immutable ordered sequence of UTF-16 code units, written with '…', "…" or backticks.

  • Template literals: `Total: ${price * qty}` interpolates expressions and spans lines directly.
  • Immutability: s[0] = "H" silently fails; every method returns a new string.
  • Inspection: .length, s[2], .charAt(2), .indexOf("a") (−1 if absent), .includes("ab"), .startsWith, .endsWith.
  • Extraction: .slice(2, 5) (negative indices count from the end), .substring(2, 5) (clamps negatives to 0).
  • Transformation: .toUpperCase(), .toLowerCase(), .trim(), .replace("a","b") (first match), .replaceAll, .repeat(3), .padStart(5,"0").
  • Splitting and joining: "a,b,c".split(",")["a","b","c"]; the inverse is arr.join(",").
  • Escapes: \n newline, \t tab, \\ backslash, \" quote.

III. Expressions and Operations

A. Operators and Expressions

An expression is any fragment that produces a value; operators combine operands into expressions.

  • Arithmetic: + - * / % ** ++ --. % is remainder, keeping the dividend's sign (-7 % 3-1); ** is right-associative (2 ** 3 ** 2 = 512).
  • Assignment: =, and compounds +=, -=, *=, /=, %=, **=.
  • Comparison:
    1. == (loose): coerces before comparing — "5" == 5 is true, null == undefined is true.
    2. === (strict): compares type and value — "5" === 5 is false. Always prefer ===.
  • Logical: &&, ||, !. && and || short-circuit and return an operand, not a boolean: "" || "default""default".
  • Nullish coalescing ??: returns the right side only for null/undefined, so 0 ?? 100 while 0 || 1010.
  • Ternary: const fee = age < 18 ? 0 : 100;
  • Overloaded +: if either operand is a string, + concatenates: 1 + "2""12", but 1 - "2"-1.
  • Precedence: grouping () > unary !/++ > ** > * / % > + - > comparison > && > ||/?? > =.

B. Conditional Statements

Conditional statements select which block executes by evaluating a condition to a truthy or falsy value.

  • if / else if / else: conditions are tested top-down and the first truthy branch runs.
  • switch: compares with strict equality against each case; break is mandatory or execution falls through.
JS
switch (grade) {
  case "A":
  case "B": message = "Pass"; break;   // deliberate fall-through
  default:  message = "Fail";
}
  • Guard clauses: if (!user) return; early exits flatten nesting.
  • Common pitfall: if (x = 5) assigns and is always truthy; the intended test is if (x === 5).

IV. Functions and Scope

A. Functions

A function is a reusable, parameterised block that is itself a first-class value — assignable, passable and returnable.

  • Declaration: function add(a, b) { return a + b; } — hoisted entirely, so it may be called before its definition.
  • Expression: const add = function (a, b) { return a + b; }; — bound by const, so it is not callable earlier.
  • Arrow function (ES6): const add = (a, b) => a + b; — implicit return when the body is a single expression; has no own this, arguments, and cannot be a constructor.
  • Parameters: missing arguments become undefined; defaults function greet(name = "guest"); rest function sum(...nums) collects extras into a real array.
  • Return: a function without return yields undefined; return immediately terminates execution.
  • Callbacks: functions passed as arguments, e.g. [1,2,3].map(n => n * 2)[2,4,6].

B. Scope

Scope is the region of code in which a binding is visible; JavaScript resolves names lexically, by where code is written, not where it is called.

  • Global scope: declared outside any function; attached to window in browsers. Pollution risks name collisions.
  • Function scope: var and parameters live for the whole function body.
  • Block scope: let/const are confined to the nearest { }, including loop bodies and if blocks.
  • Scope chain: an inner scope reads outer bindings, never the reverse; lookup walks outward until the global scope, then throws ReferenceError.
  • Hoisting: declarations move to the top of their scope — var initialised to undefined, let/const left in the temporal dead zone.
  • Closure: an inner function retains access to its defining scope after the outer call returns.
JS
function counter() {
  let n = 0;                 // private to the closure
  return () => ++n;
}
const next = counter();
next(); next();              // 1, then 2

V. Structured Data

A. Arrays and Array Operations

An array is an ordered, zero-indexed, dynamically sized object whose length is one more than its highest index.

  • Creation and access: const a = [10, 20, 30]; a[0]10; a[-1] is undefined, so use a.at(-1) or a[a.length-1].
  • Mutating operations: push/pop at the end, unshift/shift at the front, splice(1, 2, "x") removes 2 items from index 1 and inserts "x", sort, reverse.
  • Non-mutating: slice(1,3), concat, join("-"), indexOf, includes.
  • Iteration methods: forEach (side effects), map (transform, same length), filter (subset), find (first match), reduce (fold to one value), some/every (boolean tests).
JS
const cart = [{p:100}, {p:250}];
const total = cart.reduce((sum, item) => sum + item.p, 0);  // 350
  • Sorting caveat: sort() compares as strings, so [10,9,1].sort()[1,10,9]; supply a comparator sort((a,b) => a - b).
  • Copying: [...a] or a.slice() creates a shallow copy; const b = a merely aliases the same array.

B. Objects and Object Manipulation

An object is an unordered collection of key–value pairs, where keys are strings or symbols and values may be any type, including functions (methods).

  • Literal and access: const u = { name: "Asha", age: 21 }; — dot notation u.name; bracket notation u["age"] is required for dynamic or non-identifier keys (u[key]).
  • Mutation: add or update with u.city = "Pune"; remove with delete u.age; test with "name" in u or u.hasOwnProperty("name").
  • Methods and this: inside greet() { return "Hi " + this.name; }, this refers to the object the method is called on.
  • Enumeration: Object.keys(u), Object.values(u), Object.entries(u) return arrays; for (const k in u) iterates keys including inherited ones.
  • Copying and merging: { ...defaults, ...options } and Object.assign({}, a, b) — both shallow, so nested objects stay shared.
  • Destructuring: const { name, city = "N/A" } = u; extracts with a fallback; shorthand { name, age } builds an object from like-named variables.
  • Safe access: u.address?.pin returns undefined instead of throwing when address is absent.
  • Prototypes: every object links to a prototype object, from which it inherits properties — the basis of JavaScript's object model, with class syntax as sugar over it.