JavaScript Fundamentals · Complete Notes & Examples
Each topic below gives a quick explanation and at least one example. Copy/paste and run in your browser DevTools console (F12 or Ctrl/⌘+Shift+I).
Beginner → Intermediate · ES2023-friendly
PART 1 · An Introduction to JavaScript
JavaScript was initially created to “make web pages alive”.
The programs in this language are called scripts. They can be written right in a web page’s HTML and run
automatically as the page loads.
Scripts are provided and executed as plain text. They don’t need special preparation or compilation to run.
JavaScript is able to:
- Add new HTML to the page, change the existing content, modify styles.
- React to user actions, run on mouse clicks, pointer movements, key presses.
- Send requests over the network to remote servers, download and upload files (so-called AJAX
- Get and set cookies, ask questions to the visitor, show messages.
- Remember the data on the client-side (“local storage”).
What makes JavaScript unique?
- Full integration with HTML/CSS.
- Simple things are done simply.
- Supported by all major browsers and enabled by default.
Hello, world!
Use a <script> tag to run JavaScript on a page, or open the DevTools console and type code
directly.
<script type="text/javascript" >
alert('Hello, world!');
</script>
.js files for caching and reuse.<script type="text/javascript" src="script.js"></script>
Code structure: statements, semicolons, comments
Write one statement per line; semicolons are optional but recommended. Use // or
/* ... */ for comments.
// Two statements:
const msg = 'JS!';
alert(msg); // shows "JS!"
The modern mode: "use strict" (Should we use it?)
"use strict" enables safer, modern semantics (e.g., forbids accidental globals). Yes—use it at
the top of files.
"use strict";
x = 1; // ReferenceError in strict mode
Why using "use strict"?
Enables stricter error checking, improves performance, and prepares for future language features.
It’s a good practice to use "use strict" in all your scripts.
Variables
Use let for reassignable bindings, const for constants. Prefer clear, descriptive
names.
const TAX_RATE = 0.18;
let subtotal = 100;
let total = subtotal * (1 + TAX_RATE);
Variable naming
- Use
camelCasefor multi-word names (e.g.,myVariable). - Start with a letter, $, or _. Avoid numbers at the beginning.
- Be descriptive but concise (e.g.,
itemCountinstead ofic).
Data types
A value in JavaScript is always of a certain type. For example, a string or a number.
Primitives: number, string, boolean, null, undefined, bigint, symbol · Reference: object.
typeof 42 // "number"
typeof 'hi' // "string"
typeof null // "object" (quirk)
typeof {} // "object"
Interaction: alert, prompt, confirm
Use alert to show a message.
alert('Heads up!');
Use prompt to ask for input.
const name = prompt('Your name?','');
alert('Hi, ' + name + '!');
Use confirm to ask for a yes/no answer.
const ok = confirm('Continue?');
alert(ok ? 'Proceeding' : 'Stopped');
Type Conversions
Convert explicitly with Number(x), String(x), Boolean(x). Beware:
+ concatenates if either side is a string.
Number(' 42 ') // 42
'4' + 2 // "42" (concat)
'4' - 2 // 2 (numeric)
Basic operators & maths
Standard arithmetic, assignment, and exponentiation **. The unary + coerces to
number.
+true // 1
2 ** 3 // 8 //exponentiation
let x = 2; x *= 3 + 1; // 8
Arithmetic operators
- Addition
+, - Subtraction
-, - Multiplication
*, - Division
/, - Remainder
%, - Exponentiation
**.
Comparisons
Use ===/!== for strict checks (no type coercion). Strings compare
lexicographically.
'Z' > 'A' // true
0 == false // true
0 === false // false (strict)
Conditional branching: if, ?
The if(...) statement evaluates a condition in parentheses and, if the result is true, executes a block of code
For example:
if(age >= 18) {
alert('adult');
} else {
alert('minor');
}
You can use the ? operator for a shorter way to write an if statement. For example:
const age = 20;
const label = age >= 18 ? 'adult' : 'minor';
Logical operators: OR / AND / NOT
|| returns the first truthy; && returns the first falsy; !
negates.
'' || 'fallback' // "fallback"
'hi' && 123 // 123
!0 // true
Nullish coalescing: ??
Return the right side only when the left is null or undefined (unlike ||,
which treats many values as “falsy”).
Historically, the OR || operator was there first. It’s been there since the beginning of JavaScript, so developers were using it for such purposes for a long time. On the other hand, the nullish coalescing operator ?? was added to JavaScript only recently, and the reason for that was that people weren’t quite happy with ||. The important difference between them is that:
||returns the first truthy value.??returns the first defined value (ignores onlynullandundefined).
let height = 0;
alert(height || 100); // 100
alert(height ?? 100); // 0
- The nullish coalescing operator
??provides a short way to choose the first “defined” value from a list. - It’s used to assign default values to variables.
- The operator
??has a very low precedence, only a bit higher than?and=, so consider adding parentheses when using it in an expression. - It’s forbidden to use it with
||or&&without explicit parentheses.
Loops: while, for
Loops are a way to repeat the same code multiple times.
while (condition) { // code // so-called "loop body" }do { // loop body } while (condition);
Examples
let i = 0;
while (i < 3) { // shows 0, then 1, then 2
alert( i );
i++;
}
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
alert(i);
}
Breaking the loop
Normally, a loop exits when its condition becomes falsy. But we can force the exit at any time using the special break directive.
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
Continue to the next iteration
The continue directive is a “lighter version” of break. It doesn’t stop the whole loop. Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows).
for (let i = 0; i < 10; i++) {
// if true, skip the remaining part of the body
if (i % 2 == 0) continue;
alert(i); // 1, then 3, 5, 7, 9
}
The switch statement
A switch statement can replace multiple if checks. It gives a more descriptive way to compare a value with multiple variants.
const role = 'admin';
switch (role) {
case 'user': /* ... */ break;
case 'admin': /* ... */ break;
default: /* ... */
}
Example
let arg = prompt("Enter a value?");
switch (arg) {
case '0':
alert( 'Zero' );
case '1':
alert( 'One' );
break;
case '2':
alert( 'Two' );
break;
case '3':
alert( 'Three' );
break;
default:
alert( 'An unknown value' );
}
Functions (declaration, local/outer vars, params, defaults)
Functions are the main “building blocks” of the program. They allow the code to be called many times without repetition. We’ve already seen examples of built-in functions, like alert(message), prompt(message, default) and confirm(question). But we can create functions of our own as well.
Function Declaration
To create a function we can use a function declaration.
It looks like this:
function showMessage() {
alert( 'Hello everyone!' );
}
showMessage()
The function keyword goes first, then goes the name of the function, then a list of parameters between the parentheses (comma-separated, empty in the example above, we’ll see examples later) and finally the code of the function, also named “the function body”, between curly braces.
Parameters
function showMessage(name) {
let message = "Hello " + name + ", I'm JavaScript!"; // local variable
alert( message );
}
showMessage('Saeed'); // Hello, I'm JavaScript!
Default values
We can specify the so-called “default” (to use if omitted) value for a parameter in the function declaration, using =:
function showMessage(name = "stranger") {
let message = "Hello " + name + ", I'm JavaScript!"; // local variable
alert( message );
}
showMessage("Layla");
showMessage();
Returning a value
A function can return a value back into the calling code as the result. The simplest example would be a function that sums two values:
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
Function expressions
In JavaScript, a function is not a “magical language structure”, but a special kind of value. The syntax that we used before is called a Function Declaration:
function sayHi() {
alert( "Hello" );
}
sayHi(); // Hello
There is another syntax for creating a function that is called a Function Expression. It allows us to create a new function in the middle of any expression.
For example:let sayHi = function() {
alert( "Hello" );
};
sayHi(); // Hello
Here we can see a variable sayHi getting a value, the new function, created as function() { alert("Hello"); }.
Callback function
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
function showOk() {
alert( "You agreed." );
}
function showCancel() {
alert( "You canceled the execution." );
}
// usage: functions showOk, showCancel are passed as arguments to ask
ask("Do you agree?", showOk, showCancel);
Arrow functions
There’s another very simple and concise syntax for creating functions, that’s often better than Function Expressions. It’s called “arrow functions”, because it looks like this:
let func = (arg1, arg2, ..., argN) => expression;
This creates a function func that accepts arguments arg1..argN, then evaluates the expression on the right side with their use and returns its result.
let func = function(arg1, arg2, ..., argN) {
return expression;
};
function func(arg1, arg2, ..., argN) {
return expression;
};
Example:
let sum = (a, b) => a + b;
/* This arrow function is a shorter form of:
let sum = function(a, b) {
return a + b;
};
*/
alert( sum(1, 2) ); // 3
let age = prompt("What is your age?", 18);
let welcome = (age < 18) ?
() => alert('Hello!') :
() => alert("Greetings!");
welcome();
Multiline arrow functions
Sometimes we need a more complex function, with multiple expressions and statements. In that case, we can enclose them in curly braces. The major difference is that curly braces require a return within them to return a value (just like a regular function does).
let sum = (a, b) => { // the curly brace opens a multiline function
let result = a + b;
return result; // if we use curly braces, then we need an explicit "return"
};
alert( sum(1, 2) ); // 3
PART 2 · Code Quality
Code quality in JavaScript refers to the overall standard and effectiveness of the codebase, encompassing its readability, maintainability, reliability, security, and performance. High-quality code is easier to understand, debug, modify, and extend, leading to more efficient development and reduced technical debt.
Debugging in the browser
Debugging is the process of finding and fixing errors within a script. All modern browsers and most other environments support debugging tools – a special UI in developer tools that makes debugging much easier. It also allows to trace the code step by step to see what exactly is going on. We’ll be using Chrome here, because it has enough features, most other browsers have a similar process.
Open DevTools, use breakpoints, console.log, and the Sources panel to step through code.
console.log({value, state});
debugger; // triggers a breakpoint when DevTools is open
Coding Style & Best Practices
Writing clean, readable, and maintainable code is crucial. A consistent style helps prevent bugs and makes collaboration easier.
Variables and Naming
Use const by default to prevent accidental reassignment. Use let only when you know
a variable needs to change. Name variables descriptively.
// Good: Clear and immutable where possible
const userProfile = await fetchUserProfile(userId);
let retryCount = 0;
// Avoid: Vague, mutable when it doesn't need to be
var data = ...; // 'var' is legacy, prefer let/const
var i = 0; // 'i' is not descriptive
Functions
Functions should be small and do one thing well (Single Responsibility Principle). Avoid deep nesting, which makes code hard to follow.
// Good: Clear, single purpose
function isUserAdmin(user) {
return user.role === 'admin';
}
// Avoid: Too many nested levels
function processData(data) {
if (data) {
if (data.items) {
// ... more nesting
}
}
}
Tooling for Consistency
Automated tools can format your code and catch common errors, ensuring consistency across your project.
PART 3 · Advanced Topics
Array Methods
Arrays come with a host of built-in methods to make common operations like iteration, transformation, and filtering concise and readable.
Iteration: .forEach()
Executes a provided function once for each array element. It's a modern replacement for a simple
for loop.
const fruits = ['apple', 'banana', 'cherry'];
fruits.forEach(fruit => {
alert(`I love ${fruit}s!`);
});
// Logs: "I love apples!", "I love bananas!", "I love cherrys!"
Transformation: .map()
Creates a new array populated with the results of calling a provided function on every element in the calling array.
const numbers = [1, 4, 9, 16];
const roots = numbers.map(num => Math.sqrt(num));
// roots is now [1, 2, 3, 4]
alert(roots);
// numbers is still [1, 4, 9, 16]
alert(numbers);
Filtering: .filter()
Creates a new array with all elements that pass the test implemented by the provided function.
const ages = [32, 33, 16, 40];
const adults = ages.filter(age => age >= 18);
alert(adults);
// adults is now [32, 33, 40]
Aggregation: .reduce()
Executes a "reducer" function on each element of the array, resulting in a single output value (e.g., a sum or a flattened array).
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0); // 0 is the initial value for the accumulator
alert(sum);
// sum is 10
Finding Elements: .find() & .some()
.find() returns the first element that satisfies a condition, while .some() returns
a boolean if at least one element satisfies it.
const inventory = [ { name: 'apples', quantity: 2 }, { name: 'bananas', quantity: 0 }];
const appleItem = inventory.find(item => item.name === 'apples'); // { name: 'apples', quantity: 2 }
const hasOutOfStock = inventory.some(item => item.quantity === 0); // true
alert(hasOutOfStock);
Map and Set
Map and Set are modern, specialized data structures that offer more flexibility and
performance for certain tasks compared to plain Objects and Arrays.
Map
A Map is a collection of keyed data items, much like an Object. However, the main
difference is that Map allows keys of any type and maintains the insertion order.
const userRoles = new Map();
// Set key-value pairs
const john = { name: 'John' };
userRoles.set(john, 'Admin');
userRoles.set('guest', 'Limited Access');
// Get a value by key
alert(userRoles.get(john)); // "Admin"
// Check for existence
alert(userRoles.has('guest')); // true
alert(userRoles.size); // 2
Set
A Set is a special type of collection that only stores unique values. If you
add a value that already exists, it will be ignored.
const uniqueNumbers = new Set();
uniqueNumbers.add(1);
uniqueNumbers.add(5);
uniqueNumbers.add(5); // This duplicate is ignored
uniqueNumbers.add(2);
// A common use case: remove duplicates from an array
const numbers = [2, 3, 4, 4, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7];
const unique = [...new Set(numbers)]; // [2, 3, 4, 5, 6, 7];
alert(unique);
alert(unique.length); // 6
Objects
Objects store keyed collections of data and more complex entities. Unlike primitives (single values), objects are mutable containers of properties (key–value pairs). Keys are strings or symbols; values can be anything.
Create objects
let user1 = new Object(); // constructor syntax
let user2 = {}; // literal syntax (common)
Literals & properties
let user = {
name: "John",
age: 30
};
// read
alert(user.name); // "John"
alert(user.age); // 30
// add
user.isAdmin = true;
// delete
delete user.age;
Multiword keys & bracket notation
Dot notation needs valid identifiers. Use brackets for any string key or when key comes from a variable.
let user = {};
user["likes birds"] = true;
alert(user["likes birds"]); // true
let key = "likes birds";
alert(user[key]); // true
user.name = "John";
// Dot notation cannot use a variable name as a key:
let k = "name";
alert(user.k); // undefined (looks for literal 'k')
alert(user[k]);
Computed properties
Build keys dynamically inside object literals.
let fruit = "apple";
let bag = {
[fruit]: 5, // key becomes "apple"
[fruit + "Computers"]: 2 // key becomes "appleComputers"
};
alert(bag.apple); // 5
alert(bag.appleComputers); // 2
Property value shorthand
function makeUser(name, age){
return { name, age }; // same as { name: name, age: age }
}
const u = makeUser("John", 30);
alert(u.name); // "John"
Property name rules
Object property names can be (almost) any string—even reserved words or numeric-looking strings.
let obj = { for: 1, let: 2, return: 3, 0: "zero" };
alert(obj.for + obj.let + obj.return); // 6
alert(obj[0]); // "zero" "zero"
alert(obj["0"]); // "zero" "zero"
__proto__ is special; assigning non-objects to it won’t behave like normal
props.Check property existence
Reading a missing property returns undefined. Use "key" in obj when
undefined could be a valid value.
let user = {};
alert(user.missing === undefined); // true
let obj2 = { test: undefined };
alert("test" in obj2); // true (property exists)
Iterate properties: for...in
let user = { name: "John", age: 30, isAdmin: true };
for (let key in user) {
alert(key + ' ' + user[key]);
}
Property order
Integer-like keys iterate in ascending numeric order; other keys iterate in creation order.
let codes = { "49": "Germany", "41": "Switzerland", "1": "USA" };
for (let code in codes) alert(code); // 1, 41, 49
let user3 = { name: "John", surname: "Smith" };
user3.age = 25;
for (let k in user3) alert(k); // name, surname, age
Summary
- Objects = key–value stores; keys are strings/symbols, values are any type.
- Access via dot (
obj.key) or brackets (obj["key"]); brackets allow variables & any string. - Manage props:
obj.key = v,delete obj.key, test with"key" in obj. - Iterate with
for...in. Integer-like keys are ordered numerically; others by insertion order.
Object.keys / values
These methods are used to retrieve different aspects of an object's properties.
const person = {
name: 'Alice',
age: 30,
city: 'New York'
};
// Object.keys returns an array of keys
const keys = Object.keys(person);
alert(keys); // ['name', 'age', 'city']
// Object.values returns an array of values
const values = Object.values(person);
alert(values); // ['Alice', 30, 'New York']
Date & Time (Summary)
JavaScript represents date/time with the built-in Date object. A Date always has
both date and time. Months are 0–11 (Jan=0). getDay() returns 0–6 (Sun=0).
Dates “auto-correct” when you set out-of-range parts (useful for adding days/months).
Create dates
// Current date/time
let now = new Date();
alert(now);
// From timestamp (ms since Jan 1, 1970 UTC)
let jan01_1970 = new Date(0);
alert(jan01_1970);
let jan02_1970 = new Date(24 * 3600 * 1000);
alert(jan02_1970);
// Before 1970: negative timestamps
let dec31_1969 = new Date(-24 * 3600 * 1000);
alert(dec31_1969);
// From ISO-like string (parsed similar to Date.parse)
let d1 = new Date("2017-01-26");
alert(d1);
// From parts (year, month(0-11), date, hours, minutes, seconds, ms)
let d2 = new Date(2011, 0, 1, 2, 3, 4, 567);
alert(d2);
Read components (local time)
let d = new Date();
alert(d.getFullYear()); // e.g., 2025
alert(d.getMonth()); // 0..11
alert(d.getDate()); // 1..31
alert(d.getDay()); // 0..6 (Sun..Sat)
alert(d.getHours()); // 0..23
alert(d.getMinutes()); // 0..59
alert(d.getSeconds()); // 0..59
alert(d.getMilliseconds());// 0..999
// UTC variants: getUTCFullYear(), getUTCMonth(), getUTCHours()...
alert(d.getUTCHours()); // hour in UTC
Set components (auto-correct applies)
let today = new Date();
// Change just the hour
today.setHours(0);
alert(today);
// Set exact time to 00:00:00.000
today.setHours(0, 0, 0, 0);
alert(today);
Autocorrection (handy for adding time)
let date = new Date(2013, 0, 32); // Jan 32, 2013 ?!
alert(date); // auto-corrects to Feb 1, 2013
let leap = new Date(2016, 1, 28); // Feb 28, 2016
leap.setDate(leap.getDate() + 2); // add 2 days
alert(leap); // Mar 1, 2016
Timestamps & differences
let start = new Date();
// do some work
for (let i = 0; i < 100000; i++) { let x = i * i * i; }
let end = new Date();
alert("The loop took " + (end - start) + " ms"); // subtracting dates = ms diff
alert(+end); // number conversion → timestamp (same as end.getTime())
Fast now (no Date object)
let t1 = Date.now(); // ms since Jan 1, 1970
for (let i = 0; i < 100000; i++) { let x = i * i * i; }
let t2 = Date.now();
alert("The loop took " + (t2 - t1) + " ms");
Parsing strings
// ISO-like format: YYYY-MM-DDTHH:mm:ss.sssZ (Z or ±hh:mm)
let ms = Date.parse("2012-01-26T13:51:50.417-07:00");
alert(ms); // timestamp (ms)
let parsed = new Date(ms); // make Date from it
alert(parsed);
Key points
Datealways includes both date and time.- Months are zero-based;
getDay(): Sun=0..Sat=6. - Setting out-of-range parts auto-adjusts the date.
- Subtract Dates (or use
Date.now()) to measure durations.
JSON methods & toJSON (Summary)
JSON is a data format for exchanging structured data. In JS, use
JSON.stringify to serialize values → string, and JSON.parse to read
them back. JSON supports objects, arrays, strings, numbers, booleans, and null.
(Functions, undefined, and symbol properties are skipped.)
Stringify basics
let student = {
name: "John",
age: 30,
isAdmin: false,
courses: ["html","css","js"],
spouse: null
};
let json = JSON.stringify(student); // → string
alert(typeof json); // "string"
alert(json);
/* Example output:
{"name":"John","age":30,"isAdmin":false,"courses":["html","css","js"],"spouse":null}
*/
Primitives also stringify
alert(JSON.stringify(1)); // 1
alert(JSON.stringify("test")); // "test"
alert(JSON.stringify(true)); // true
alert(JSON.stringify([1,2,3])); // [1,2,3]
Skipped in JSON
let user = {
sayHi(){ alert("Hello"); }, // function → skipped
[Symbol("id")]: 123, // symbol → skipped
something: undefined // undefined → skipped
};
alert(JSON.stringify(user)); // "{}"
Nested objects (ok) & circular refs (error)
let room = { number: 23 };
let meetup = { title: "Conference", place: room };
room.occupiedBy = meetup; // circular link
try {
JSON.stringify(meetup);
} catch (e) {
alert("Error: " + e.message); // Converting circular structure to JSON
}
Filtering/transforming: replacer
Use an array of allowed keys, or a function (key, value) => newValue.
let room2 = { number: 23 };
let meetup2 = {
title: "Conference",
participants: [{name:"John"},{name:"Alice"}],
place: room2
};
room2.occupiedBy = meetup2;
// Keep only selected keys everywhere:
alert(JSON.stringify(meetup2, ["title","participants","place","name","number"]));
/*
{"title":"Conference","participants":[{"name":"John"},{"name":"Alice"}],"place":{"number":23}}
*/
// Or use a replacer function to drop circular ref:
alert(JSON.stringify(meetup2, function replacer(key, value){
if (key === "occupiedBy") return undefined;
return value;
}));
Pretty printing: space
let userPretty = { name:"John", age:25, roles:{ isAdmin:false, isEditor:true } };
let pretty = JSON.stringify(userPretty, null, 2); // 2-space indent
alert(pretty);
/*
{
"name": "John",
"age": 25,
"roles": {
"isAdmin": false,
"isEditor": true
}
}
*/
Custom toJSON
let room3 = {
number: 23,
toJSON(){ return this.number; } // define your own JSON form
};
let meetup3 = { title: "Conference", room: room3 };
alert(JSON.stringify(room3)); // 23
alert(JSON.stringify(meetup3)); // {"title":"Conference","room":23}
Parsing strings: JSON.parse
let numbers = "[0,1,2,3]";
let arr = JSON.parse(numbers);
alert(arr[1]); // 1
let userData = '{"name":"John","age":35,"isAdmin":false,"friends":[0,1,2,3]}';
let userObj = JSON.parse(userData);
alert(userObj.friends[1]); // 1
Key points
JSON.stringify(value, replacer?, space?)→ string.JSON.parse(str, reviver?)→ value; use reviver to restore types likeDate.- If an object has
toJSON, it’s used automatically during stringify.
Scheduling (Summary): setTimeout & setInterval
Schedule code to run later. setTimeout(fn, ms) runs once after ms.
setInterval(fn, ms) repeats every ms until canceled.
Delays are approximate (CPU load, background tabs, battery saver, etc.).
Run once: setTimeout
let tId = setTimeout(() => alert("John"), 1000); // 1s later → "Hi, John!"
Repeat: setInterval (and stop)
let ticks = 0;
let iId = setInterval(() => {
alert("tick " + (++ticks));
if (ticks === 3) { clearInterval(iId); alert("stop"); }
}, 1500); // every 1.5s
“ASAP” after current script
setTimeout(() => alert("World")); // queued
alert("Hello"); // shows first
"alert('x')"); pass functions.
Function Binding (Short Summary)
When we pass object methods (e.g. to setTimeout),
this can be lost — it no longer refers to the original object.
bind() fixes that by locking the function’s this value.
Lost this
let user = {
firstName: "John",
sayHi() { alert("Hi, " + this.firstName); }
};
setTimeout(user.sayHi, 1000); // ❌ "Hi, undefined"
Fix 1: wrapper function
let user = {
firstName: "John",
sayHi() { alert("Hi, " + this.firstName); }
};
setTimeout(() => user.sayHi(), 1000); // ✅ "Hi, John"
Fix 2: bind()
let user = {
firstName: "John"
};
function func() {
alert(this.firstName);
}
let funcUser = func.bind(user);
funcUser(); // John
bind(thisArg)fixes the value ofthis.- Use
bindwhen passing methods as callbacks.
Error handling (Summary): try...catch
Use try...catch to prevent crashes on runtime errors and handle them gracefully.
Optional finally runs whether an error happened or not.
Basic pattern
try {
alert("Start");
lalala; // runtime error
alert("Never runs");
} catch (err) {
alert(err.name + ": " + err.message); // e.g., "ReferenceError: lalala is not defined"
} finally {
alert("Cleanup always runs");
}
Throw your own errors
let json = '{ "age": 30 }';
try {
let user = JSON.parse(json);
if (!user.name) throw new SyntaxError("Incomplete data: no name");
alert("User: " + user.name);
} catch (err) {
alert("JSON Error: " + err.message);
}
Promises (Short Summary)
A Promise links slow “producing” code to “consuming” code.
It starts pending and settles once: fulfilled via resolve or rejected via reject.
Use .then for success, .catch for errors, and .finally for cleanup.
Create
let p = new Promise((resolve, reject) => {
// async work...
setTimeout(() => resolve("done"), 500); // or: reject(new Error("oops"))
});
Consume
p.then(result => alert(result)) // "done"
.catch(err => alert(err)) // "Error: oops"
.finally(() => alert("finished")); // always runs
One result only
new Promise(r => { r("A"); r("B"); }) // "B" is ignored
Handlers can be added anytime
let ready = Promise.resolve("OK");
ready.then(v => alert(v)); // alerts immediately
Mini example: load script
function loadScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement("script");
s.src = src;
s.onload = () => resolve(s);
s.onerror = () => reject(new Error("Load error: " + src));
document.head.append(s);
});
}
loadScript("https://example.com/app.js")
.then(s => alert(s.src + " loaded"))
.catch(e => alert(e.message))
.finally(() => alert("done"));
loadScript("https://portal.almasar101.com/wp-includes/js/jquery/ui/core.min.js?ver=1.13.3")
.then(s => alert(s.src + " loaded"))
.catch(e => alert(e.message))
.finally(() => alert("done"));
Watch Promise Video Explanation Arabic
Watch Promise Video Explanation English
Promise chaining
Chaining promises allows you to perform sequential asynchronous operations. Each .then handler receives the result of the previous one, and you can return a new value or a promise to pass it to the next handler.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
}).then(function(result) { // (***)
alert(result); // 2
return result * 2;
}).then(function(result) {
alert(result); // 4
return result * 2;
});
Error Handling with Promises (Short Summary)
.catch handles any rejection or error in a promise chain — even those thrown in .then handlers.
You can place one .catch at the end to handle all errors above.
Basic example
fetch('https://no-such-server.example')
.then(r => r.json())
.catch(err => alert("Error: " + err.message));
Implicit try...catch
new Promise(() => { throw new Error("Whoops!"); })
.catch(err => alert(err)); // works same as reject()
Rethrowing
new Promise(() => { throw new Error("Whoops!"); })
.catch(e => {
if (e instanceof URIError) alert("Handled");
else throw e; // pass to next .catch
})
.catch(e => alert("Final catch: " + e));
Unhandled rejections
window.addEventListener("unhandledrejection", e => {
alert("Unhandled: " + e.reason);
});
new Promise(() => { throw new Error("Whoops!"); });
.catchcatches rejections or thrown errors.- Use
throwinside.catchto rethrow unhandled ones. - Browsers trigger
unhandledrejectionif a rejected promise has no.catch.
Promise API (Short Summary)
The Promise class has 6 static methods to combine or create promises.
They help run tasks in parallel, handle multiple results, or settle early.
1️⃣ Promise.all
Waits for all promises to resolve → returns array of results. Rejects immediately if any fail.
Promise.all([
fetch("https://api.github.com/users/iliakan"), fetch("https://api.github.com/users/zeiadhabbab")
]).then(all => alert("All done"))
.catch(err => alert("Error: " + err));
2️⃣ Promise.allSettled
Waits for all to finish (fulfilled or rejected). Always resolves with an array of status objects.
Promise.allSettled([
fetch("https://api.github.com/users/zeiadhabbab"), fetch("bad.json")
]).then(r => alert(JSON.stringify(r)));
3️⃣ Promise.race
Resolves/rejects with the first settled promise (winner of the “race”).
Promise.race([
new Promise(r => setTimeout(() => r("fast"), 500)),
new Promise(r => setTimeout(() => r("slow"), 1500))
]).then(alert); // "fast"
4️⃣ Promise.any
Returns the first fulfilled result. If all fail → rejects with AggregateError.
Promise.any([
Promise.reject("fail"),
Promise.resolve("ok")
]).then(alert); // "ok"
5️⃣ Promise.resolve
Creates a resolved promise with a given value.
Promise.resolve("ready").then(alert);
6️⃣ Promise.reject
Creates a rejected promise with an error.
Promise.reject(new Error("Oops")).catch(e => alert(e.message));
Promise.all – run tasks in parallel and wait for all results.
Async / Await (Short Summary)
async and await make working with promises simpler and more readable.
async makes a function return a promise.
await pauses inside that function until the promise settles.
Async functions
async function f() {
return 1; // returns Promise.resolve(1)
}
f().then(alert); // 1
Await keyword
async function f() {
let promise = new Promise(r => setTimeout(() => r("done!"), 1000));
let result = await promise; // waits 1s
alert(result); // "done!"
}
f();
Error handling
async function f() {
try {
let r = await fetch("https://no-site.example");
} catch (err) {
alert("Error: " + err.message);
}
}
f();
Await + Promise.all
async function loadAll() {
let [a, b] = await Promise.all([
fetch("https://api.github.com/users/zeiadhabbab"),
fetch("https://api.github.com/users/zeiadhabbab")
]);
alert("Both loaded!");
}
loadAll();
async→ always returns a Promise.await→ waits for the promise and gives its result.- Use
try...catchto handle errors inside async functions. - Combine with
Promise.all()for parallel tasks.
await = “pause until ready”,
async = “function that always returns a promise”.
PART 4 · DOM
The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page structure as a tree of objects, allowing JavaScript to dynamically access and update content, structure, and styles.
This section covers the basics of DOM manipulation, including traversing the DOM tree, searching for elements, modifying content and attributes, handling styles and classes, and working with events.
Browser environment & specs
The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms. A platform may be a browser, or a web-server or another host, or even a “smart” coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a host environment. A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on.
There’s a “root” object called window. It has two roles:
function sayHi() {
alert("Hello");
}
// global functions are methods of the global object:
window.sayHi();
alert(window.innerHeight); // inner window height
DOM (Document Object Model)
The Document Object Model, or DOM for short, represents all page content as objects that can be modified. The document object is the main “entry point” to the page. We can change or create anything on the page using it.
// change the background color to red
document.body.style.background = "red";
// change it back after 1 second
setTimeout(() => document.body.style.background = "", 1000);
BOM (Browser Object Model)
The Browser Object Model (BOM) is a collection of objects that provide browser-specific features. It includes objects like window, navigator, location, history, and screen.
alert(location.href); // shows current URL
if (confirm("Go to Wikipedia?")) {
location.href = "https://wikipedia.org"; // redirect the browser to another URL
}
DOM tree
The DOM is a tree of nodes representing the document structure.
<html>
<head>
<title>My title</title>
</head>
<body>
<a href="#">My link</a>
<h1>My header</h1>
</body>
</html>
Walking the DOM
To navigate the DOM tree, we can use properties like parentElement, children, firstElementChild, lastElementChild, nextElementSibling, and previousElementSibling.
const list = document.getElementById('list');
list.firstElementChild.textContent; // "A"
<html> = document.documentElement
<body> = document.body
<head> = document.head
Example
<html>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
</ul>
<div>End</div>
<script>
for (let i = 0; i < document.body.childNodes.length; i++) {
alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT
}
</script>
...more stuff...
</body>
</html>
Difference between Node object and Element object?
Node object: represents any part of the document tree, like elements, text, comments, etc.
Element object: specifically represents HTML elements and provides methods and properties for manipulating them.
Searching: getElement*, querySelector*
To search for elements in the DOM, we can use methods like getElementById, getElementsByTagName, getElementsByClassName, and querySelector (and its plural querySelectorAll).
<div id="elem">
<div id="elem-content">Element</div>
</div>
<script>
// get the element
let elem = document.getElementById('elem');
// make its background red
elem.style.background = 'red';
</script>
Also, there’s a global variable named by id that references the element:
<div id="myDiv">Hello</div>
<script>
alert(myDiv.textContent); // Hello
</script>
querySelectorAll
To select multiple elements, we can use querySelectorAll. It returns a collection of elements that match the given selector.
<ul>
<li>The</li>
<li>test</li>
</ul>
<ul>
<li>has</li>
<li>passed</li>
</ul>
<script>
let elements = document.querySelectorAll('ul > li:last-child');
for (let elem of elements) {
alert(elem.innerHTML); // "test", "passed"
}
</script>
querySelector
To select a single element, we can use querySelector. It returns the first element that matches the given selector.
<div class="note">First note
</div>
<div class="note">Second note
</div>
<script>
let firstNote = document.querySelector('.note');
alert(firstNote.innerHTML); // "First note"
</script>
Node properties: type, tag, contents
Every element, text, or comment in a web page is a DOM node. Each node belongs to a class in the DOM hierarchy that defines its properties and methods.
1. DOM Node Classes
The DOM is built on a class hierarchy:
EventTarget– Base class that enables events.Node– Adds tree structure features (parentNode,childNodes).Element– Adds element-level features likechildrenandquerySelector().HTMLElement– Base class for all HTML tags (<div>,<a>, etc.).- Specific classes like
HTMLInputElement,HTMLBodyElement, etc., add tag-specific features.
Example:
let input = document.querySelector('input');
alert(input.constructor.name); // HTMLInputElement
2. Identifying Node Type
The nodeType property tells what kind of node you’re working with:
1→ Element3→ Text9→ Document
alert(document.body.nodeType); // 1
alert(document.body.firstChild.nodeType); // 3
3. Tag Name
You can get a node’s tag using tagName or nodeName:
alert(document.body.tagName); // BODY
alert(document.body.nodeName); // BODY
tagName works only for elements, while nodeName also works for text or comment nodes.
4. innerHTML and outerHTML
innerHTML lets you read or replace the HTML inside an element.
document.body.innerHTML = "<h1>Welcome!</h1>";
outerHTML includes the element itself. Writing to it replaces the element in the DOM.
div.outerHTML = "<p>Replaced!</p>";
5. textContent and nodeValue
Use textContent to safely get or set plain text (no HTML tags).
Use nodeValue or data to access the content of text or comment nodes.
elem.textContent = "<b>Safe text</b>"; // shows literally, no bold
6. Hidden Property
The hidden attribute quickly hides elements, like display:none in CSS.
elem.hidden = true;
7. Common Node Properties
value→ for inputshref→ for linksid→ for any element
Summary
- Each DOM node belongs to a class and inherits properties from its hierarchy.
nodeType,tagName, andnodeNamehelp identify nodes.innerHTMLandouterHTMLhandle HTML markup.textContentanddatahandle plain text safely.hiddenquickly toggles visibility.
textContent to safely insert text,
and console.dir(element) to inspect all its DOM properties.
const p = document.createElement('p');
p.nodeType; // 1 = ELEMENT_NODE
p.tagName; // "P"
Attributes & properties
When the browser parses HTML, most standard attributes on elements become JavaScript DOM properties. But they aren’t always identical, and not every attribute becomes a property.
1) DOM Properties (JS side)
- Live on the element object (regular JS object).
- Typed values (e.g.,
checkedis boolean,styleis an object). - Case-sensitive names: use
elem.nodeType, notelem.NoDeTyPe. - You can add your own:
document.body.myData = {name:'Caesar'}.
// Custom property
document.body.myData = { name: 'Caesar', title: 'Imperator' };
console.log(document.body.myData.title); // Imperator
// Prototype extension (affects all elements)
Element.prototype.sayHi = function () {
console.log(`Hello, I'm <${this.tagName.toLowerCase()}>`);
};
document.body.sayHi(); // Hello, I'm <body>
2) HTML Attributes (markup side)
- What you write in HTML.
- Names are case-insensitive; values are always strings.
- Some attributes are standard only for certain tags (e.g.,
typeis standard on<input>, not on<body>).
<div id="card" about="Elephant"></div>
<script>
const el = document.getElementById('card');
console.log(el.getAttribute('About')); // "Elephant" (case-insensitive)
el.setAttribute('data-note', 123);
for (const a of el.attributes) {
console.log(a.name, '=', a.value);
}
</script>
3) Working with Attributes (raw HTML values)
elem.hasAttribute(name)elem.getAttribute(name)elem.setAttribute(name, value)elem.removeAttribute(name)elem.attributes→ iterable list of all attributes
4) Sync Rules (Attribute ⇄ Property)
Standard attributes usually sync with their properties, but not always both ways:
- Two-way (usually):
idinput.setAttribute('id', 'x'); console.log(input.id); // "x" input.id = 'y'; console.log(input.getAttribute('id')); // "y" - One-way:
value(attribute → property only)input.setAttribute('value', 'text'); console.log(input.value); // "text" input.value = 'new'; console.log(input.getAttribute('value')); // "text" (unchanged original) - Typed differences:
checkedis boolean vs attribute is string/empty<input type="checkbox" checked id="c"> c.getAttribute('checked'); // "" (string) c.checked; // true (boolean) - Normalized properties:
a.hrefis always absolute<a id="link" href="#hash"> link.getAttribute('href'); // "#hash" link.href; // "https://example.com/page#hash" - Objects:
styleattribute is string;elem.styleis CSS objectdiv.getAttribute('style'); // "color:red;font-size:120%" div.style.color; // "red"
5) Custom Data: data-* & dataset
Use data-* for safe, conflict-free custom attributes. Access them via elem.dataset
(camelCase for multi-word).
<div id="order" class="order" data-order-state="new">A new order</div>
<script>
console.log(order.dataset.orderState); // "new"
order.dataset.orderState = 'pending'; // updates attribute and can drive CSS
</script>
/* CSS */
.order[data-order-state="pending"] { color: blue; }
6) Quick Guidance
- Prefer properties for day-to-day JS (typed, live state).
- Use attributes when you need the exact HTML value (
getAttribute) or non-standard/custom data. - For custom data, use
data-*+dataset. - Remember special sync cases (e.g.,
value).
Modifying the Document (DOM)
Create elements, insert them in precise positions, clone or remove nodes, and safely inject HTML or text.
1) Create Nodes
document.createElement(tag)→ element nodedocument.createTextNode(text)→ text node
// Build: <div class="alert"><strong>Hi!</strong> You’ve got a message.</div>
const box = document.createElement('div');
box.className = 'alert';
box.innerHTML = '<strong>Hi!</strong> You’ve got a message.';
2) Insert Nodes (Modern)
parent.append(...nodes|strings)– end of parentparent.prepend(...)– start of parentnode.before(...)/node.after(...)– around nodenode.replaceWith(...)– replace node
document.body.prepend(box); // show at top
box.after(' (inline text)', document.createElement('hr'));
Strings are inserted as text (escaped), not HTML.
3) Insert as HTML
Use elem.insertAdjacentHTML(where, html) for literal HTML.
"beforebegin"|"afterbegin"|"beforeend"|"afterend"
box.insertAdjacentHTML('beforeend', '<em> Read this.</em>');
4) Remove / Move
node.remove()– delete node- Re-inserting a node moves it automatically (no manual remove needed)
setTimeout(() => box.remove(), 1500);
5) Clone
elem.cloneNode(true)– deep clone (with children)elem.cloneNode(false)– shallow clone
const copy = box.cloneNode(true);
copy.querySelector('strong').textContent = 'Bye!';
box.after(copy);
6) Batching
Build lists off-DOM, then append once. Either:
DocumentFragment or just collect nodes in an array and spread.
// Array approach (simple, fast)
const items = Array.from({length: 3}, (_, i) => {
const li = document.createElement('li');
li.textContent = i + 1;
return li;
});
document.querySelector('#list').append(...items);
7) Legacy Methods (know them, don’t start with them)
appendChild, insertBefore, replaceChild, removeChild exist for older codebases.
8) ⚠️ Avoid document.write
Only works during initial parsing; calling later wipes the page. Prefer the modern APIs above.
createElement → insert with append/prepend/before/after →
HTML string? use insertAdjacentHTML → batch many nodes → clone with cloneNode → remove with remove().
Styles & Classes
Prefer CSS classes for presentation. Use inline style from JS only when values are dynamic (e.g., computed positions).
1) Prefer Classes over Inline Styles
/* CSS */
.btn { padding: .5rem 1rem; background: #0a7; color: #fff; }
/* JS: add the class instead of manual inline styling */
elem.classList.add('btn');
2) className vs classList
elem.className→ full class string (overwrites all).elem.classList→ add/remove/toggle one class at a time.
// className (replace all)
elem.className = 'card card--primary';
// classList (surgical)
elem.classList.add('is-active');
elem.classList.remove('is-hidden');
elem.classList.toggle('dark');
elem.classList.contains('dark'); // true/false
// Iterate classes
for (const c of elem.classList) console.log(c);
3) Inline Styles (when you must)
Use camelCase for multi-word properties. Remember units (px, %, etc.).
elem.style.backgroundColor = 'gold';
elem.style.zIndex = '10';
elem.style.left = leftPx + 'px';
elem.style.top = topPx + 'px';
Reset or Remove an Inline Style
// reset one property
elem.style.display = ''; // restores CSS/UA default
elem.style.removeProperty('background'); // remove explicitly-set inline style
// set many at once (replaces all inline styles!)
elem.style.cssText = `
color: red !important;
background-color: #ffe;
padding: 8px;
`;
classList →
inline style only for dynamic numbers/positions → reset with '' or
removeProperty → read final values with getComputedStyle.
Introduction to Browser Events
An event signals that something happened (click, key press, form submit, etc.). Handlers are functions that run in response.
Common Event Types
- Mouse:
click,contextmenu,mouseover/mouseout,mousedown/mouseup,mousemove - Keyboard:
keydown,keyup - Form:
submit,focus - Document:
DOMContentLoaded - CSS:
transitionend
Ways to Attach Handlers
-
HTML attribute (quick demos; avoid for real apps):
<input type="button" value="Click" onclick="sayHi()"> -
DOM property (one handler per event):
button.onclick = function () { alert('Thanks'); }; button.onclick = null; // remove -
addEventListener (multiple handlers + options):
Use for events likefunction handler(e){ console.log(e.type); } button.addEventListener('click', handler, { once: true }); button.removeEventListener('click', handler);DOMContentLoaded(not available viaonDOMContentLoaded).
The Event Object
button.addEventListener('click', function (event) {
console.log(event.type); // "click"
console.log(event.currentTarget === this); // true (except in arrow functions)
console.log(event.clientX, event.clientY); // mouse coords
});
In HTML attributes, event is also available: onclick="alert(event.type)".
Gotchas & Tips
- Assign the function, don’t call it:
btn.onclick = sayHi(✅), notsayHi()(❌). removeEventListenerneeds the same function reference used to add.- Prefer
addEventListenerfor composability and special events. - Use options:
{ once: true }auto-removes,{ passive: true }for scroll/touch (nopreventDefault()),{ capture: true }to handle in capture phase.
addEventListener → keep a named handler for removal →
read details from event → avoid inline HTML handlers in production.
PART 5 · Network Requests
Network requests allow web apps to fetch data from servers.
Commonly used for fetching JSON data from APIs.
Fetch API is modern, Promise-based; supports JSON, FormData, Blobs, etc.
Fetch
fetch() lets JavaScript make network requests without reloading the page
(a.k.a. “AJAX”). Use it to submit forms, load user data, or grab live updates.
Quick start
// Basic GET (2-stage: headers → body)
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json(); // or .text() / .blob() / .arrayBuffer() / .formData()
.json(), .text(), .blob(), etc.
Response essentials
res.status→ HTTP status (e.g. 200, 404)res.ok→truefor 200–299res.headers→ Map-like headers (get(), iterable)
console.log(res.headers.get('content-type'));
for (const [k, v] of res.headers) console.log(k, v);
Sending requests
JSON POST
const payload = { name: 'John', surname: 'Smith' };
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json;charset=utf-8' },
body: JSON.stringify(payload)
});
const result = await res.json();
Binary / FormData
// Blob (e.g., from canvas.toBlob or a file input)
const res = await fetch('/upload', { method: 'POST', body: myBlob });
// FormData auto-sets headers (don’t set Content-Type manually)
const fd = new FormData();
fd.append('avatar', fileInput.files[0]);
const res2 = await fetch('/upload', { method: 'POST', body: fd });
Error handling (robust pattern)
async function httpJson(url, opts = {}) {
const res = await fetch(url, opts);
// Network errors throw; non-2xx do NOT → check .ok yourself
if (!res.ok) {
const msg = await res.text().catch(() => '');
throw new Error(`HTTP ${res.status} ${res.statusText} – ${msg}`);
}
const ct = res.headers.get('content-type') || '';
return ct.includes('application/json') ? res.json() : res.text();
}
try {
const data = await httpJson('https://api.github.com/users/iliakan');
console.log(data);
} catch (err) {
console.error(err);
}
Headers
Set request headers via headers. Some are forbidden (e.g., Content-Length, Origin, Referer, Cookie, Sec-*), the browser manages those.
await fetch('/secure', { headers: { Authorization: 'Bearer <token>' } });
Common pitfalls
- Non-2xx statuses don’t throw → always check
res.ok. - Read the body once; choose a single method (
.json(),.text(), …). - CORS is enforced by the browser—server must allow your origin/method/headers.
- Don’t manually set
Content-TypeforFormDataor mostBlobs.
Cheat sheet
// GET
await fetch(url).then(r => r.json());
// POST JSON
await fetch(url, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(data) });
// Text / Blob
const txt = await (await fetch(url)).text();
const blob = await (await fetch(url)).blob();
// Headers
const res = await fetch(url);
res.headers.get('content-type');
FormData
FormData is a built-in JavaScript object used to collect and send form data — including files —
to a server using fetch() or other network methods. It automatically encodes data as
multipart/form-data, just like a regular form submission.
Creating FormData
// Create from an existing form
const formData = new FormData(document.querySelector('#myForm'));
// Or create manually
const fd = new FormData();
fd.append('name', 'John');
fd.append('surname', 'Smith');
Sending a Simple Form
<form id="userForm">
<input type="text" name="name" value="John">
<input type="text" name="surname" value="Smith">
<input type="submit">
</form>
<script>
userForm.onsubmit = async (e) => {
e.preventDefault();
const res = await fetch('/api/users', {
method: 'POST',
body: new FormData(userForm)
});
const result = await res.json();
alert(result.message);
};
</script>
FormData Methods
append(name, value)– Add a new field.append(name, blob, fileName)– Add a file field (like<input type="file">).set(name, value)– Replace existing field(s) with a new one.delete(name)– Remove a field.get(name)– Read a field’s value.has(name)– Check if a field exists.
Note: append() adds another field with the same name;
set() replaces all previous ones.
Example – Iterating through fields
const fd = new FormData();
fd.append('key1', 'value1');
fd.append('key2', 'value2');
for (const [name, value] of fd) {
console.log(name, value);
}
Summary
new FormData(form)captures all fields of an HTML form.- Use
append()andset()to add fields or files.
PART 6 · Storing Data in the Browser
Web storage APIs let you store data locally in the browser for later use.
Common options are localStorage (persistent) and sessionStorage (per-tab).
LocalStorage
localStorage is a built-in web storage object that lets you store
key–value pairs in the browser permanently (until manually deleted).
Data in localStorage:
- Persists after page refresh or browser restart.
- Is shared across all tabs and windows from the same origin (domain + protocol + port).
- Is not sent to the server with requests (unlike cookies).
- Can typically store 5–10 MB of data (depending on browser).
Basic API
setItem(key, value)– store a value.getItem(key)– retrieve a value.removeItem(key)– delete a specific key.clear()– remove all keys.key(index)– get the key name by index.length– number of stored entries.
Example
localStorage.setItem('user', 'John');
console.log(localStorage.getItem('user')); // "John"
localStorage.removeItem('user');
localStorage.clear(); // remove everything
Object-like access (not recommended)
localStorage.test = 123;
console.log(localStorage.test); // 123
delete localStorage.test;
This works, but is not safe if the key overlaps with built-in names (e.g. length, toString).
Always prefer getItem()/setItem().
Looping over keys
// by index
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
console.log(key, localStorage.getItem(key));
}
// or with Object.keys
for (const key of Object.keys(localStorage)) {
console.log(key, localStorage.getItem(key));
}
Strings only
Both key and value must be strings.
To store objects, use JSON.stringify() and JSON.parse().
// store object
const user = { name: 'John', age: 25 };
localStorage.setItem('user', JSON.stringify(user));
// read object
const stored = JSON.parse(localStorage.getItem('user'));
console.log(stored.name); // John
Storage Event (multi-tab sync)
When localStorage changes in one tab,
other tabs (on the same origin) receive a storage event.
window.addEventListener('storage', e => {
console.log('Changed:', e.key, e.oldValue, '→', e.newValue, 'from', e.url);
});
// in another tab
localStorage.setItem('theme', 'dark');
This enables simple communication between browser tabs.
Practical Uses
- Save user preferences (e.g., theme, language).
- Cache small JSON data or API responses for faster reloads.
- Remember form input or app state between sessions.
Summary
| Feature | localStorage |
|---|---|
| Scope | All tabs/windows on the same origin |
| Lifetime | Until manually cleared |
| Size limit | ≈ 5 MB per origin |
| Data type | String (convert objects using JSON) |
| Event | storage (syncs across tabs) |
localStorage for small, persistent data.
Use sessionStorage for temporary, tab-specific data.