Introduction to JavaScript
What is JavaScript?
JavaScript (JS) is a high‑level, interpreted, multi‑paradigm language that powers the web. It was created in 1995 by Brendan Eich at Netscape and has since become the de‑facto language for client‑side scripting, server‑side development (Node.js), desktop apps (Electron), and much more.
- 1995 – First appeared as “LiveScript” in Netscape Navigator.
- 1997 – Standardised as
ECMAScript 1. - 2009 –
Node.jsbrings JS to the server. - 2015 –
ES6 (ES2015)introduces classes, modules, arrow functions, etc. - 2023 –
ES2023continues to evolve the language.
Hello World – Your First JS Program
console.log("Hello, World!");
Run it with Node.js (or open the file in a browser console):
# Run
node hello.js
Hello, World!
How JavaScript Executes
- Engine (V8, SpiderMonkey, JavaScriptCore) parses source code into an AST.
- Just‑In‑Time (JIT) compilation turns hot code paths into native machine code.
- Event‑loop handles asynchronous callbacks and promises.
Install Node.js, create hello.js with the snippet above, run it, and verify the output.
Environment Setup
Running JavaScript in the Browser
All modern browsers have a built‑in JavaScript engine. Open the developer console (F12 → “Console”) and type code directly.
console.log('Hello from the browser!');
Node.js – JavaScript on the Server
Node.js bundles the V8 engine with a set of core modules (file system, networking, etc.). After
installing from nodejs.org you can run any
.js file with the node command.
# Install (macOS/Linux)
brew install node
# Verify version
node -v
npm -v
Package Management with npm
npm (Node Package Manager) lets you install third‑party libraries.
# Initialise a new project
npm init -y
# Install a library (e.g., lodash)
npm install lodash
# Run a script defined in package.json
npm run start
Run package binaries without installing them globally: npx create-react-app my-app.
Editor Recommendations
- VS Code + ESLint + Prettier – great for JS.
- WebStorm – full‑featured IDE, paid.
- Vim/Neovim – lightweight, with
coc.nvim.
Initialise a new npm project, install lodash, and write a script index.js
that imports lodash and logs _.shuffle([1,2,3,4,5]) to the console.
Variables & Data Types
Declaring Variables
Three main keywords:
var– function scoped, hoisted.let– block scoped, no hoisting.const– block scoped, read‑only binding.
var legacy = 10; let mutable = 20; const immutable = 30; console.log(legacy, mutable, immutable);
Primitive Types
- Number – double‑precision floating point (
42,3.14). - BigInt – arbitrary‑length integers (
123n). - String – textual data (
"Hello"). - Boolean –
true/false. - Symbol – unique identifiers.
- undefined – declared but uninitialised.
- null – intentional empty value.
Type Checking
let val = '123'; console.log(typeof val); // "string" val = 123; console.log(typeof val); // "number" val = null; console.log(typeof val); // "object" (quirk)
Constants and Immutability
Only the binding of a const cannot change. For objects/arrays the contents are still mutable
unless you freeze them:
const arr = [1, 2]; arr.push(3); // works Object.freeze(arr); arr.push(4); // TypeError: Cannot add property 3, object is not extensible
Create a script that declares variables of each primitive type, prints their type using
typeof, then attempts to reassign a const and observes the error.
Operators & Expressions
Arithmetic Operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ |
Addition / concatenation | 5 + 3 |
8 |
- |
Subtraction | 5 - 3 |
2 |
* |
Multiplication | 5 * 3 |
15 |
/ |
Division | 7 / 2 |
3.5 |
% |
Remainder | 7 % 2 |
1 |
String Concatenation & Template Literals
let a = "Hello"; let b = "World"; console.log(a + ", " + b); // Hello, World console.log(`\${a}, \${b}!`); // Hello, World!
Comparison & Logical Operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
=== |
Strict equality (no coercion) | 5 === "5" |
false |
!== |
Strict inequality | 5 !== 5 |
false |
== |
Loose equality (type coercion) | 5 == "5" |
true |
!= |
Loose inequality | 5 != "5" |
false |
&& |
Logical AND | true && false |
false |
|| |
Logical OR | true || false |
true |
! |
Logical NOT | !true |
false |
Assignment Operators
let x = 5; x += 3; // x = 8 x *= 2; // x = 16 x %= 5; // x = 1
Type Coercion Gotchas
console.log(1 + 2 + "3"); // "33" console.log("3" - 2); // 1 (string converted to number)
Write a program that reads two numbers with prompt() (or readline-sync on
Node) and prints the sum, difference, product, quotient, remainder and a truthy/falsy test of the
result.
Control Flow
If / else if / else
let score = 85; if (score >= 90) { console.log("Grade A"); } else if (score >= 80) { console.log("Grade B"); } else { console.log("Needs improvement"); }
Switch Statement
let day = 'Monday'; switch (day) { case 'Monday': console.log('Start of the week'); break; case 'Friday': console.log('Almost weekend'); break; default: console.log('Just another day'); }
Loops
for loop
for (let i = 1; i <= 5; i++) { console.log(i); }
while loop
let n = 1; while (n <= 100) { console.log(n); n *= 2; }
do…while loop
let password; do { password = prompt('Enter password (type 1234 to quit):'); } while (password !== '1234');
break & continue
breakexits the nearest loop immediately.continueskips the remainder of the current iteration.
Write a program that prints the multiplication table of a number entered by the user (1‑12). Then
create a do…while loop that repeatedly asks for a secret word until the user enters
open-sesame.
Functions
Function Declarations & Expressions
Two common syntaxes:
- Function Declaration (hoisted):
function add(a, b) { … } - Function Expression (not hoisted):
const add = function(a, b) { … };
function add(a, b) { return a + b; } const multiply = (a, b) => a * b; // arrow function console.log(add(3, 4)); // 7 console.log(multiply(5, 6)); // 30
Default Parameters & Rest Parameters
function greet(name = 'World') { console.log(`Hello, \${name}!`); } greet(); // Hello, World! greet('Alice'); // Hello, Alice! function sumAll(...nums) { return nums.reduce((a, b) => a + b, 0); } console.log(sumAll(1, 2, 3)); // 6
Higher‑Order Functions & Callbacks
A function can receive another function as an argument or return one.
function fetchData(url, callback) { setTimeout(() => { const data = `Data from \${url}`; callback(data); }, 1000); } fetchData('/api', result => { console.log(result); });
this from the surrounding scope, while regular functions have their
own this binding depending on how they are called.
Implement a recursive factorial(n) function (using both a function declaration and an
arrow function). Then write a higher‑order function applyTwice(fn, x) that returns
fn(fn(x)) and test it with the factorial function.
Objects & Prototypes
Object Literals
Key/value pairs enclosed in {}. Property names can be strings or identifiers.
const user = { name: "Bob", age: 30, isAdmin: true, greet() { console.log(`Hi, I'm \${this.name}`); } }; console.log(user.name); // Bob user.greet(); // Hi, I’m Bob
Constructor Functions & Classes
Before ES6, objects were created via constructor functions and the new keyword. ES6
introduced class syntax which is syntactic sugar over the prototype chain.
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`\${this.name} says hello!`); } } const alice = new Person('Alice', 28); alice.greet(); // Alice says hello!
Prototypal Inheritance
Every object has an internal [[Prototype]] (accessible via
Object.getPrototypeOf or __proto__). Adding properties to the prototype makes
them shared.
function Animal(type) { this.type = type; } Animal.prototype.speak = function() { console.log(`\${this.type} makes a sound`); }; const dog = new Animal('Dog'); dog.speak(); // Dog makes a sound
Create a Vehicle class with make, model and a method
info(). Extend it with an ElectricVehicle subclass that adds a
batteryLevel property and overrides info() to also show the battery.
Arrays & Higher‑Order Functions
Array Basics
Arrays are ordered collections, created with [] or new Array().
const numbers = [1, 2, 3, 4, 5]; console.log(numbers.length); // 5 console.log(numbers[0]); // 1
Common Array Methods
push / pop– add/remove at end.shift / unshift– add/remove at start.forEach– iterate (no return).map– transform to new array.filter– select elements.reduce– accumulate a single value.find / findIndex– locate element.
const nums = [1, 2, 3, 4, 5]; // map – double each number const doubled = nums.map(n => n * 2); console.log(doubled); // [2,4,6,8,10] // filter – keep evens const evens = nums.filter(n => n % 2 === 0); console.log(evens); // [2,4] // reduce – sum const sum = nums.reduce((acc, cur) => acc + cur, 0); console.log(sum); // 15
Spread & Rest Operators
Use ... to expand or collect elements.
const a = [1, 2]; const b = [3, 4]; const combined = [...a, ...b]; // [1,2,3,4] function logAll(...values) { console.log(values); } logAll(5, 6, 7); // [5,6,7]
Write a function unique(arr) that returns a new array with duplicate values removed (use
Set or filter). Then write a groupBy(arr, keyFn) that groups
objects by a derived key and returns an object of arrays.
Asynchronous JavaScript
Callbacks
Functions passed to async APIs that run once the operation completes.
function delayedLog(msg, delay, cb) { setTimeout(() => { console.log(msg); if (cb) cb(); }, delay); } delayedLog('First', 1000, () => { delayedLog('Second', 500); });
Promises
Represent a value that may become available in the future. They can be chained and handle errors with
catch.
function fetchNumber() { return new Promise((resolve, reject) => { setTimeout(() => { const n = 42; if (n) resolve(n); else reject('No number'); }, 800); }); } fetchNumber() .then(value => console.log('Got', value)) .catch(err => console.error(err));
Async / Await
Syntactic sugar over promises; makes asynchronous code look synchronous.
async getData() { try { const res = await fetch('https://api.github.com/users/octocat'); const data = await res.json(); console.log(data); } catch (e) { console.error(e); } } getData();
Write a function fetchJson(url) that returns a promise resolving to parsed JSON (use
fetch). Then consume it with both .then/.catch and with
async/await to display the result.
DOM Manipulation & Events
The Document Object Model
The DOM is a tree‑like representation of the HTML page. You can query and modify nodes with the
document API.
Selecting Elements
const header = document.querySelector('h1'); const items = document.querySelectorAll('.item'); const byId = document.getElementById('main');
Modifying Content
header.textContent = 'New Title'; header.style.color = 'rebeccapurple'; const newDiv = document.createElement('div'); newDiv.classList.add('card'); newDiv.innerHTML = `<p>Hello</p>`; document.body.appendChild(newDiv);
Event Handling
Attach listeners with addEventListener. Event objects provide details (type, target, etc.).
document.querySelector('button').addEventListener('click', e => { alert(`Clicked! X: \${e.clientX}`); }); window.addEventListener('resize', () => { console.log('Window resized. New size:', window.innerWidth); });
Build a simple counter app: an HTML page with a number display and two buttons “+” and “‑”. Clicking
the buttons updates the number and stores the current value in localStorage so the
count persists across reloads.
Modules & Build Tools
ES6 Modules
Use export and import. Native support in browsers (with
type="module") and in Node.js (with .mjs or "type":"module" in
package.json).
export function add(a, b) { return a + b; } export const PI = 3.1415926535;
import { add, PI } from './math.mjs'; console.log(add(2, 3)); // 5 console.log(PI); // 3.14159...
CommonJS (Node.js legacy)
Uses module.exports and require(). Still works alongside ES6 modules.
function multiply(a, b) { return a * b; } module.exports = { multiply };
Bundlers (Webpack, Vite, Parcel)
Combine multiple modules, transpile with Babel, and generate production‑ready assets.
# Example with Vite (fast dev server)
npm create vite@latest my-app -- --template vanilla
cd my-app
npm install
npm run dev
Transpiling Modern JS (Babel)
Convert ES2023+ syntax to older JavaScript that runs on older browsers.
# Install Babel
npm install --save-dev @babel/core @babel/cli @babel/preset-env
# .babelrc
{
"presets": ["@babel/preset-env"]
}
Set up a tiny project with Vite (or Webpack) using ES modules. Create two modules
(utils.js exporting a helper, and app.js importing it) and run the
development server to see the live reload.
Capstone Project – Interactive To‑Do List
In this project you will build a **single‑page** JavaScript application that lets the user manage a to‑do list. The app will demonstrate everything you’ve learned: modules, DOM manipulation, event handling, local storage, async operations, and modern JavaScript syntax.
Feature Checklist
- Persist tasks in
localStorage(load on start, save on change). - Task object:
{id, text, done, createdAt}. - Add, toggle (complete/incomplete), delete, and edit tasks.
- Filter view: All / Active / Completed.
- Responsive UI with minimal CSS.
- Split code into ES modules:
store.js,ui.js,app.js. - Optional: use the Fetch API to load a remote JSON of sample tasks (demonstrates async).
Project Skeleton
todo-app/ ├── index.html ├── styles.css ├── src/ │ ├── app.js /* entry point */ │ ├── store.js /* localStorage handling */ │ └── ui.js /* DOM rendering & events */ └── package.json /* optional – for build tools */
Starter Code – index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="app">
<h1>Todo List</h1>
<input id="new-task" type="text" placeholder="What needs to be done?">
<ul id="task-list"></ul>
<div class="filters">
<button data-filter="all">All</button>
<button data-filter="active">Active</button>
<button data-filter="completed">Completed</button>
</div>
</div>
<script type="module" src="src/app.js"></script>
</body>
</html>
Basic UI Module (ui.js)
import { getTasks, addTask, toggleTask, deleteTask, setFilter } from './store.js'; const taskList = document.getElementById('task-list'); const newTaskInput = document.getElementById('new-task'); const filterButtons = document.querySelectorAll('.filters button'); function render() { const tasks = getTasks(); taskList.innerHTML = ''; tasks.forEach(t => { const li = document.createElement('li'); li.className = t.done ? 'done' : ''; li.innerHTML = `\${t.text} <button class="toggle">\${t.done ? '↩' : '✓'}</button> <button class="del">✕</button>`; li.querySelector('.toggle').addEventListener('click', () => { toggleTask(t.id); render(); }); li.querySelector('.del').addEventListener('click', () => { deleteTask(t.id); render(); }); taskList.appendChild(li); }); } newTaskInput.addEventListener('keydown', e => { if (e.key === 'Enter' && newTaskInput.value.trim()) { addTask(newTaskInput.value.trim()); newTaskInput.value = ''; render(); } }); filterButtons.forEach(btn => { btn.addEventListener('click', () => { setFilter(btn.dataset.filter); render(); }); }); render();
Store Module (store.js) – persists in localStorage
let tasks = JSON.parse(localStorage.getItem('tasks')) || []; let filter = 'all'; function save() { localStorage.setItem('tasks', JSON.stringify(tasks)); } export function getTasks() { if (filter === 'active') return tasks.filter(t => !t.done); if (filter === 'completed') return tasks.filter(t => t.done); return tasks; } export function addTask(text) { const id = Date.now(); tasks.push({id, text, done: false, createdAt: new Date()}); save(); } export function toggleTask(id) { const t = tasks.find(x => x.id === id); if (t) t.done = !t.done; save(); } export function deleteTask(id) { tasks = tasks.filter(x => x.id !== id); save(); } export function setFilter(mode) { filter = mode; }
Application Entry (app.js)
import './ui.js'; // UI module runs immediately and wires everything up
- Add a simple backend (Node Express) that stores tasks in a JSON file and syncs via HTTP.
- Use a CSS framework (Tailwind, Bulma) for a prettier UI.
- Write unit tests for
store.jsusing Jest.
Extend the app with a “Due Date” field on each task. Show a visual indicator (e.g., red background)
for tasks that are overdue. Store the date in ISO format, and format it nicely using
Intl.DateTimeFormat.