0% found this document useful (0 votes)
32 views28 pages

Javascript Notes 1

JavaScript is a high-level, interpreted programming language essential for web development, covering both front-end and back-end. The document outlines fundamental concepts such as variables, data types, operators, control flow, loops, and functions, as well as intermediate topics like arrays, objects, prototypes, and the 'this' keyword. It serves as a comprehensive guide from basic to advanced JavaScript concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views28 pages

Javascript Notes 1

JavaScript is a high-level, interpreted programming language essential for web development, covering both front-end and back-end. The document outlines fundamental concepts such as variables, data types, operators, control flow, loops, and functions, as well as intermediate topics like arrays, objects, prototypes, and the 'this' keyword. It serves as a comprehensive guide from basic to advanced JavaScript concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Comprehensive JavaScript Notes: From

Basics to Advanced
JavaScript is a versatile, high-level, and interpreted programming language primarily used for
creating interactive web pages. It's an essential technology for front-end web development, and
increasingly, with Node.js, for back-end development as well.

1. Basic Concepts
1.1 Variables
Variables are containers for storing data values. JavaScript has three keywords for declaring
variables: var, let, and const.
●​ var: Oldest way to declare variables. Function-scoped and can be re-declared and
re-assigned.​
var greeting = "Hello";​
var greeting = "Hi"; // Re-declaration is allowed​
greeting = "Hey"; // Re-assignment is allowed​
console.log(greeting); // Output: Hey​

●​ let: Block-scoped. Can be re-assigned but not re-declared within the same scope.​
let name = "Alice";​
// let name = "Bob"; // Error: Cannot re-declare block-scoped
variable 'name'​
name = "Charlie"; // Re-assignment is allowed​
console.log(name); // Output: Charlie​

if (true) {​
let blockScoped = "I'm inside a block";​
console.log(blockScoped); // Output: I'm inside a block​
}​
// console.log(blockScoped); // Error: blockScoped is not defined​

●​ const: Block-scoped. Cannot be re-assigned or re-declared. Must be initialized at


declaration.​
const PI = 3.14159;​
// PI = 3.14; // Error: Assignment to constant variable.​
// const GRAVITY; // Error: Missing initializer in const
declaration​

const person = { name: "David" };​
person.name = "Eve"; // Allowed: Modifying properties of an object
declared with const is fine​
// person = { name: "Frank" }; // Error: Assignment to constant
variable.​
console.log(person); // Output: { name: "Eve" }​

1.2 Data Types


JavaScript has two main categories of data types: Primitive and Non-Primitive (Reference).

Primitive Data Types:

1.​ String: Represents text.​


let message = "Hello, JavaScript!";​
console.log(typeof message); // Output: string​

2.​ Number: Represents both integers and floating-point numbers.​


let age = 30;​
let price = 99.99;​
console.log(typeof age); // Output: number​
console.log(typeof price); // Output: number​

3.​ Boolean: Represents true or false.​


let isActive = true;​
let hasPermission = false;​
console.log(typeof isActive); // Output: boolean​

4.​ Undefined: A variable that has been declared but not yet assigned a value.​
let undefinedVar;​
console.log(undefinedVar); // Output: undefined​
console.log(typeof undefinedVar); // Output: undefined​

5.​ Null: Represents the intentional absence of any object value. It's a primitive value.​
let emptyValue = null;​
console.log(emptyValue); // Output: null​
console.log(typeof emptyValue); // Output: object (This is a
long-standing bug in JavaScript)​

6.​ Symbol (ES6): Represents a unique identifier.​


const id1 = Symbol('id');​
const id2 = Symbol('id');​
console.log(id1 === id2); // Output: false​
console.log(typeof id1); // Output: symbol​

7.​ BigInt (ES2020): Represents whole numbers larger than 2^{53} - 1.​
const bigNumber = 1234567890123456789012345678901234567890n;​
console.log(typeof bigNumber); // Output: bigint​
Non-Primitive (Reference) Data Types:

1.​ Object: A collection of key-value pairs.​


let person = {​
firstName: "John",​
lastName: "Doe",​
age: 30​
};​
console.log(typeof person); // Output: object​

2.​ Array: A special type of object used to store ordered collections of data.​
let colors = ["red", "green", "blue"];​
console.log(typeof colors); // Output: object (Arrays are objects
in JS)​

3.​ Function: A block of code designed to perform a particular task. Functions are also
objects.​
function greet() {​
console.log("Hello!");​
}​
console.log(typeof greet); // Output: function (though technically
an object)​

1.3 Operators
Operators perform operations on values and variables.
●​ Arithmetic Operators: +, -, *, /, % (modulus), ** (exponentiation), ++ (increment), --
(decrement)​
let a = 10, b = 3;​
console.log(a + b); // 13​
console.log(a - b); // 7​
console.log(a * b); // 30​
console.log(a / b); // 3.333...​
console.log(a % b); // 1​
console.log(a ** b); // 1000 (10 to the power of 3)​
a++; console.log(a); // 11​
b--; console.log(b); // 2​

●​ Assignment Operators: =, +=, -=, *=, /=, %=, **=​


let x = 5;​
x += 3; // x = x + 3; // x is now 8​
console.log(x); // 8​

●​ Comparison Operators: == (loose equality), === (strict equality), !=, !==, >, <, >=, <=​
console.log(5 == '5'); // true (loose equality, type coercion)​
console.log(5 === '5'); // false (strict equality, no type
coercion)​
console.log(10 > 5); // true​

●​ Logical Operators: && (AND), || (OR), ! (NOT)​


let isAdult = true;​
let hasLicense = false;​
console.log(isAdult && hasLicense); // false​
console.log(isAdult || hasLicense); // true​
console.log(!isAdult); // false​

●​ Ternary Operator: condition ? expr1 : expr2​


let age = 18;​
let status = (age >= 18) ? "Adult" : "Minor";​
console.log(status); // Output: Adult​

1.4 Control Flow


Control flow statements allow you to execute different blocks of code based on certain
conditions.
●​ if, else if, else:​
let temperature = 25;​
if (temperature > 30) {​
console.log("It's hot outside!");​
} else if (temperature > 20) {​
console.log("It's pleasant.");​
} else {​
console.log("It's cold.");​
}​
// Output: It's pleasant.​

●​ switch:​
let day = "Monday";​
switch (day) {​
case "Monday":​
console.log("Start of the week.");​
break;​
case "Friday":​
console.log("End of the work week!");​
break;​
default:​
console.log("Just another day.");​
}​
// Output: Start of the week.​

1.5 Loops
Loops are used to execute a block of code repeatedly.
●​ for loop:​
for (let i = 0; i < 5; i++) {​
console.log("Iteration " + i);​
}​
/*​
Output:​
Iteration 0​
Iteration 1​
Iteration 2​
Iteration 3​
Iteration 4​
*/​

●​ while loop:​
let count = 0;​
while (count < 3) {​
console.log("Count: " + count);​
count++;​
}​
/*​
Output:​
Count: 0​
Count: 1​
Count: 2​
*/​

●​ do...while loop: Executes the block once, then checks the condition.​
let i = 0;​
do {​
console.log("Do-while iteration: " + i);​
i++;​
} while (i < 0); // Condition is false, but runs once.​
// Output: Do-while iteration: 0​

●​ for...in loop: Iterates over enumerable properties of an object.​


const car = { brand: "Toyota", model: "Camry", year: 2020 };​
for (let key in car) {​
console.log(`${key}: ${car[key]}`);​
}​
/*​
Output:​
brand: Toyota​
model: Camry​
year: 2020​
*/​

●​ for...of loop (ES6): Iterates over iterable objects (like Arrays, Strings, Maps, Sets, etc.).​
const colors = ["red", "green", "blue"];​
for (let color of colors) {​
console.log(color);​
}​
/*​
Output:​
red​
green​
blue​
*/​

1.6 Functions
Functions are reusable blocks of code that perform a specific task.
●​ Function Declaration:​
function greet(name) {​
return "Hello, " + name + "!";​
}​
console.log(greet("Alice")); // Output: Hello, Alice!​

●​ Function Expression:​
const sayHi = function(name) {​
return "Hi, " + name + "!";​
};​
console.log(sayHi("Bob")); // Output: Hi, Bob!​

●​ Arrow Functions (ES6): Shorter syntax, no this binding, cannot be used as constructors.​
const add = (a, b) => a + b;​
console.log(add(5, 3)); // Output: 8​

const multiply = (a, b) => {​
// Multi-line arrow function needs return​
return a * b;​
};​
console.log(multiply(4, 2)); // Output: 8​

●​ Parameters and Arguments:​


function calculateSum(num1, num2) { // num1, num2 are parameters​
return num1 + num2;​
}​
console.log(calculateSum(10, 20)); // 10, 20 are arguments​

●​ Default Parameters (ES6):​


function welcome(name = "Guest") {​
console.log(`Welcome, ${name}!`);​
}​
welcome("Charlie"); // Output: Welcome, Charlie!​
welcome(); // Output: Welcome, Guest!​

2. Intermediate Concepts
2.1 Arrays
Arrays are ordered lists of values.
●​ Creating Arrays:​
let fruits = ["Apple", "Banana", "Cherry"];​
let emptyArray = [];​
let mixedArray = [1, "hello", true, { key: "value" }];​

●​ Accessing Elements:​
console.log(fruits[0]); // Output: Apple​
console.log(fruits[fruits.length - 1]); // Output: Cherry​

●​ Modifying Elements:​
fruits[1] = "Blueberry";​
console.log(fruits); // Output: ["Apple", "Blueberry", "Cherry"]​

●​ Common Array Methods:


○​ push(): Adds element to the end.
○​ pop(): Removes element from the end.
○​ unshift(): Adds element to the beginning.
○​ shift(): Removes element from the beginning.
○​ splice(start, deleteCount, ...items): Adds/removes elements at any position.
○​ slice(start, end): Returns a shallow copy of a portion of an array.
○​ concat(): Joins arrays.
○​ indexOf(), lastIndexOf(), includes(): Search for elements.
○​ forEach(), map(), filter(), reduce(): Iteration and transformation.
○​ find(), findIndex(): Find elements based on a condition.
<!-- end list -->let numbers = [1, 2, 3, 4, 5];​
numbers.push(6); // [1, 2, 3, 4, 5, 6]​
numbers.pop(); // [1, 2, 3, 4, 5]​
numbers.unshift(0); // [0, 1, 2, 3, 4, 5]​
numbers.shift(); // [1, 2, 3, 4, 5]​
numbers.splice(2, 1, 10); // Removes 3, adds 10: [1, 2, 10, 4, 5]​
let subArray = numbers.slice(1, 3); // [2, 10]​

numbers.forEach(num => console.log(num * 2)); // Prints 2, 4, 20, 8,
10​
let doubled = numbers.map(num => num * 2); // [2, 4, 20, 8, 10]​
let evens = numbers.filter(num => num % 2 === 0); // [2, 10, 4]​
let sum = numbers.reduce((acc, curr) => acc + curr, 0); // 22​
2.2 Objects
Objects are collections of key-value pairs.
●​ Creating Objects:​
let user = {​
name: "Jane Doe",​
age: 25,​
email: "jane@example.com",​
isAdmin: false,​
address: {​
street: "123 Main St",​
city: "Anytown"​
},​
greet: function() {​
console.log(`Hello, my name is ${this.name}`);​
}​
};​

●​ Accessing Properties:​
console.log(user.name); // Dot notation: Jane Doe​
console.log(user['age']); // Bracket notation: 25​
console.log(user.address.city); // Anytown​
user.greet(); // Hello, my name is Jane Doe​

●​ Modifying/Adding Properties:​
user.age = 26;​
user.phone = "555-1234";​
console.log(user);​

●​ Deleting Properties:​
delete user.isAdmin;​
console.log(user);​

●​ Object Destructuring (ES6):​


const { name, age } = user;​
console.log(name, age); // Jane Doe 26​

const { city } = user.address;​
console.log(city); // Anytown​

●​ Spread Operator (...) for Objects (ES2018):​


const defaults = { color: "red", size: "M" };​
const userPrefs = { size: "L", theme: "dark" };​
const combined = { ...defaults, ...userPrefs };​
console.log(combined); // { color: "red", size: "L", theme: "dark"
}​
2.3 Prototypes and Prototypal Inheritance
JavaScript uses prototypal inheritance. Every object has a prototype, which is another object
that it inherits properties and methods from.
const animal = {​
eats: true,​
walk() {​
console.log("Animal walks.");​
}​
};​

const rabbit = {​
jumps: true,​
__proto__: animal // rabbit inherits from animal​
};​

console.log(rabbit.eats); // Output: true (inherited from animal)​
rabbit.walk(); // Output: Animal walks. (inherited from
animal)​

// Using Object.create()​
const dog = Object.create(animal);​
dog.barks = true;​
console.log(dog.eats); // true​

2.4 this keyword


The this keyword refers to the object it belongs to. Its value depends on how the function is
called.
●​ Global context: this refers to the global object (window in browsers, global in Node.js).
●​ Method context: this refers to the object the method belongs to.
●​ Function context: In non-strict mode, this refers to the global object. In strict mode, this
is undefined.
●​ Event handlers: this refers to the element that received the event.
●​ Arrow functions: this is lexically scoped (it inherits this from the parent scope).
●​ call(), apply(), bind(): Explicitly set this.
<!-- end list -->
const myObject = {​
value: 42,​
getValue: function() {​
console.log(this.value); // `this` refers to myObject​
}​
};​
myObject.getValue(); // Output: 42​

const anotherObject = {​
value: 100​
};​
// Using call to set `this` explicitly​
myObject.getValue.call(anotherObject); // Output: 100​

// Arrow function example​
const arrowObject = {​
name: "Arrow Guy",​
sayName: function() {​
// `this` here refers to arrowObject​
const innerArrow = () => {​
console.log(this.name); // `this` here also refers to
arrowObject (lexical scope)​
};​
innerArrow();​
}​
};​
arrowObject.sayName(); // Output: Arrow Guy​

2.5 Closures
A closure is the combination of a function and the lexical environment within which that function
was declared. It allows a function to access variables from its outer scope even after the outer
function has finished executing.
function createCounter() {​
let count = 0; // This variable is "closed over" by the returned
functions​
return {​
increment: function() {​
count++;​
console.log(count);​
},​
decrement: function() {​
count--;​
console.log(count);​
},​
getCount: function() {​
return count;​
}​
};​
}​

const counter1 = createCounter();​
counter1.increment(); // Output: 1​
counter1.increment(); // Output: 2​
console.log(counter1.getCount()); // Output: 2​

const counter2 = createCounter(); // New independent counter​
counter2.increment(); // Output: 1​

2.6 Higher-Order Functions


Functions that take other functions as arguments or return functions as their result.
// Function that takes a function as an argument​
function operateOnNumbers(a, b, operation) {​
return operation(a, b);​
}​

function add(x, y) { return x + y; }​
function subtract(x, y) { return x - y; }​

console.log(operateOnNumbers(10, 5, add)); // Output: 15​
console.log(operateOnNumbers(10, 5, subtract)); // Output: 5​

// Function that returns a function​
function multiplier(factor) {​
return function(number) {​
return number * factor;​
};​
}​

const multiplyBy5 = multiplier(5);​
console.log(multiplyBy5(10)); // Output: 50​

2.7 Event Loop and Asynchronous JavaScript


JavaScript is single-threaded, but it handles asynchronous operations (like network requests,
timers) using the Event Loop.
●​ Callbacks: Functions passed as arguments to be executed later.​
console.log("Start");​
setTimeout(function() {​
console.log("Inside setTimeout (after 2 seconds)");​
}, 2000);​
console.log("End");​
// Output: Start -> End -> Inside setTimeout (after 2 seconds)​

●​ Promises (ES6): Objects representing the eventual completion or failure of an


asynchronous operation.​
const fetchData = new Promise((resolve, reject) => {​
// Simulate an async operation​
setTimeout(() => {​
const success = true;​
if (success) {​
resolve("Data fetched successfully!");​
} else {​
reject("Failed to fetch data.");​
}​
}, 1500);​
});​

fetchData​
.then(data => console.log(data)) // Output: Data fetched
successfully!​
.catch(error => console.error(error))​
.finally(() => console.log("Promise finished."));​

●​ Async/Await (ES2017): Syntactic sugar built on Promises, making asynchronous code


look synchronous.​
async function getUserData() {​
try {​
console.log("Fetching user data...");​
const response = await
fetch('https://jsonplaceholder.typicode.com/users/1');​
const data = await response.json();​
console.log("User data:", data);​
} catch (error) {​
console.error("Error fetching user:", error);​
}​
}​
getUserData();​
Note: fetch API is a Web API, not part of core JavaScript, but commonly used with
async/await.

2.8 Error Handling


The try...catch...finally statement is used to handle errors gracefully.
function divide(a, b) {​
try {​
if (b === 0) {​
throw new Error("Cannot divide by zero!");​
}​
return a / b;​
} catch (error) {​
console.error("An error occurred:", error.message);​
return null; // Or re-throw, or return a default value​
} finally {​
console.log("Division attempt finished.");​
}​
}​

console.log(divide(10, 2)); // Output: Division attempt finished. \n 5​
console.log(divide(10, 0)); // Output: An error occurred: Cannot
divide by zero! \n Division attempt finished. \n null​

3. Advanced Concepts
3.1 ES6+ Features (Modern JavaScript)
●​ Classes: Syntactic sugar over JavaScript's existing prototypal inheritance.​
class Animal {​
constructor(name) {​
this.name = name;​
}​
speak() {​
console.log(`${this.name} makes a sound.`);​
}​
}​

class Dog extends Animal {​
constructor(name, breed) {​
super(name); // Call parent constructor​
this.breed = breed;​
}​
speak() {​
console.log(`${this.name} (${this.breed}) barks.`);​
}​
}​

const myDog = new Dog("Buddy", "Golden Retriever");​
myDog.speak(); // Output: Buddy (Golden Retriever) barks.​

●​ Modules (import/export): Organize code into separate files for better maintainability and
reusability.
○​ math.js​
// math.js​
export const PI = 3.14159;​
export function add(a, b) {​
return a + b;​
}​
export default function subtract(a, b) {​
return a - b;​
}​

○​ app.js​
// app.js​
import { PI, add } from './math.js';​
import subtractNumbers from './math.js'; // Default import​

console.log(PI); // 3.14159​
console.log(add(2, 3)); // 5​
console.log(subtractNumbers(5, 2)); // 3​

○​ Note: Modules require a server environment or specific browser configuration (e.g.,


<script type="module">).
●​ Spread (...) and Rest (...) Operators:
○​ Spread: Expands an iterable (like an array or string) into individual elements.​
const arr1 = [1, 2];​
const arr2 = [3, 4];​
const combinedArray = [...arr1, ...arr2, 5]; // [1, 2, 3, 4,
5]​

const obj1 = { a: 1, b: 2 };​
const obj2 = { c: 3, d: 4 };​
const combinedObject = { ...obj1, ...obj2 }; // { a: 1, b: 2,
c: 3, d: 4 }​

function sum(x, y, z) { return x + y + z; }​
const numbers = [1, 2, 3];​
console.log(sum(...numbers)); // 6​

○​ Rest: Collects remaining arguments into an array.​


function collectArgs(first, ...rest) {​
console.log(first); // 1​
console.log(rest); // [2, 3, 4, 5]​
}​
collectArgs(1, 2, 3, 4, 5);​

●​ Template Literals (Template Strings): Backticks (`) for embedded expressions and
multi-line strings.​
const name = "World";​
const greeting = `Hello, ${name}!​
This is a multi-line string.`;​
console.log(greeting);​

●​ Destructuring Assignment: Extract values from arrays or properties from objects into
distinct variables.​
// Array Destructuring​
const [first, second, ...restOfArray] = [10, 20, 30, 40, 50];​
console.log(first); // 10​
console.log(second); // 20​
console.log(restOfArray); // [30, 40, 50]​

// Object Destructuring (already covered)​

●​ Default Parameters: (Already covered in Functions)


3.2 Generators
Functions that can be paused and resumed, yielding (returning) multiple values over time. They
are defined with function*.
function* idGenerator() {​
let id = 1;​
while (true) {​
yield id++; // Pause execution and return id, resume on next()​
}​
}​

const gen = idGenerator();​
console.log(gen.next().value); // 1​
console.log(gen.next().value); // 2​
console.log(gen.next().value); // 3​

3.3 Proxies and Reflect (ES6)


●​ Proxies: Objects that wrap another object or function and can intercept fundamental
operations (e.g., property lookup, assignment, function invocation).​
const target = {​
message1: "hello",​
message2: "world"​
};​

const handler = {​
get: function(target, property, receiver) {​
if (property === 'message2') {​
return 'Intercepted message2!';​
}​
return Reflect.get(target, property, receiver);​
},​
set: function(target, property, value, receiver) {​
if (property === 'message1' && typeof value !== 'string')
{​
throw new TypeError('message1 must be a string');​
}​
return Reflect.set(target, property, value, receiver);​
}​
};​

const proxy = new Proxy(target, handler);​

console.log(proxy.message1); // hello​
console.log(proxy.message2); // Intercepted message2!​

proxy.message1 = "new hello";​
console.log(proxy.message1); // new hello​

// proxy.message1 = 123; // Throws TypeError​

●​ Reflect: A built-in object that provides methods for interceptable JavaScript operations.
Often used with Proxies.

3.4 Web APIs (Browser Environment)


These are not part of core JavaScript but are crucial for web development.
●​ DOM Manipulation (Document Object Model): Interface for HTML and XML documents.​
// Select elements​
const myDiv = document.getElementById('myDiv');​
const paragraphs = document.querySelectorAll('.my-paragraph');​

// Create elements​
const newElement = document.createElement('p');​
newElement.textContent = "This is a new paragraph.";​
newElement.classList.add('dynamic-text');​

// Append elements​
document.body.appendChild(newElement);​

// Modify attributes/styles​
myDiv.style.backgroundColor = 'lightblue';​
myDiv.setAttribute('data-info', 'important');​

// Event Listeners​
const myButton = document.getElementById('myButton');​
myButton.addEventListener('click', () => {​
// In a real app, use a custom modal or message box instead of
alert()​
// For this example, we'll use a placeholder for brevity.​
console.log('Button clicked!');​
});​

●​ Fetch API: For making network requests (e.g., to APIs).​


// Already demonstrated in Async/Await section.​
// fetch(url, options) returns a Promise.​

●​ Local Storage and Session Storage: Store data in the browser.


○​ localStorage: Data persists even after browser close.
○​ sessionStorage: Data cleared when the browser tab/window is closed. <!-- end list
-->
// Local Storage​
localStorage.setItem('username', 'Alice');​
const username = localStorage.getItem('username');​
console.log(username); // Alice​
localStorage.removeItem('username');​
// localStorage.clear(); // Clears all items​

// Session Storage​
sessionStorage.setItem('tempData', JSON.stringify({ id: 1, value:
'temporary' }));​
const tempData = JSON.parse(sessionStorage.getItem('tempData'));​
console.log(tempData); // { id: 1, value: 'temporary' }​

3.5 Design Patterns


Common solutions to recurring problems in software design.
●​ Module Pattern: Encapsulate private state and public methods.​
const ShoppingCart = (function() {​
let items = []; // Private variable​

function addItem(item) {​
items.push(item);​
console.log(`${item} added.`);​
}​

function removeItem(item) {​
const index = items.indexOf(item);​
if (index > -1) {​
items.splice(index, 1);​
console.log(`${item} removed.`);​
}​
}​

function getItems() {​
return [...items]; // Return a copy to prevent external
modification​
}​

return {​
add: addItem,​
remove: removeItem,​
list: getItems​
};​
})();​

ShoppingCart.add("Laptop");​
ShoppingCart.add("Mouse");​
console.log(ShoppingCart.list()); // ["Laptop", "Mouse"]​
// console.log(ShoppingCart.items); // undefined (private)​
●​ Revealing Module Pattern: A variation of the module pattern where you explicitly return
an object with pointers to the private functions you want to expose.​
const Calculator = (function() {​
let result = 0;​

function add(num) {​
result += num;​
}​

function subtract(num) {​
result -= num;​
}​

function getResult() {​
return result;​
}​

return {​
add: add,​
subtract: subtract,​
getResult: getResult​
};​
})();​

Calculator.add(10);​
Calculator.subtract(3);​
console.log(Calculator.getResult()); // 7​

●​ Singleton Pattern: Ensures a class has only one instance and provides a global point of
access to it.​
const Logger = (function() {​
let instance;​

function init() {​
let logs = [];​
function addLog(message) {​
const timestamp = new Date().toISOString();​
logs.push(`[${timestamp}] ${message}`);​
}​
function getLogs() {​
return logs;​
}​
return {​
addLog: addLog,​
getLogs: getLogs​
};​
}​

return {​
getInstance: function() {​
if (!instance) {​
instance = init();​
}​
return instance;​
}​
};​
})();​

const logger1 = Logger.getInstance();​
logger1.addLog("First message.");​

const logger2 = Logger.getInstance();​
logger2.addLog("Second message.");​

console.log(logger1.getLogs()); // Both messages are present, as
it's the same instance.​
console.log(logger2.getLogs());​

●​ Observer Pattern (Publish/Subscribe): Defines a one-to-many dependency between


objects so that when one object changes state, all its dependents are notified and
updated automatically.​
class Subject {​
constructor() {​
this.observers = [];​
}​

addObserver(observer) {​
this.observers.push(observer);​
}​

removeObserver(observer) {​
this.observers = this.observers.filter(obs => obs !==
observer);​
}​

notify(data) {​
this.observers.forEach(observer => observer.update(data));​
}​
}​

class Observer {​
constructor(name) {​
this.name = name;​
}​

update(data) {​
console.log(`${this.name} received update: ${data}`);​
}​
}​

const newsFeed = new Subject();​
const subscriber1 = new Observer("Subscriber A");​
const subscriber2 = new Observer("Subscriber B");​

newsFeed.addObserver(subscriber1);​
newsFeed.addObserver(subscriber2);​

newsFeed.notify("Breaking News: JavaScript is awesome!");​
// Output:​
// Subscriber A received update: Breaking News: JavaScript is
awesome!​
// Subscriber B received update: Breaking News: JavaScript is
awesome!​

newsFeed.removeObserver(subscriber1);​
newsFeed.notify("Another update!");​
// Output:​
// Subscriber B received update: Another update!​

3.6 Web Workers


Allows running JavaScript in the background thread, separate from the main execution thread,
preventing UI blocking.
// main.js (or script in HTML)​
// This part would typically be in your main HTML script​
/*​
if (window.Worker) {​
const myWorker = new Worker('worker.js'); // Assuming worker.js is
a separate file​

myWorker.postMessage({ command: 'startCalculation', data:
1000000000 });​

myWorker.onmessage = function(e) {​
console.log('Message received from worker:', e.data);​
};​

myWorker.onerror = function(error) {​
console.error('Worker error:', error);​
};​

console.log('Main thread continues...');​
} else {​
console.log('Web Workers are not supported in this browser.');​
}​
*/​

// worker.js content (this would be in a separate file named
worker.js)​
/*​
onmessage = function(e) {​
if (e.data.command === 'startCalculation') {​
let sum = 0;​
for (let i = 0; i < e.data.data; i++) {​
sum += i;​
}​
postMessage(sum); // Send result back to main thread​
}​
};​
*/​

Note: Web Workers cannot directly access the DOM. The above code snippets for Web
Workers are illustrative and would typically reside in separate files (main.js and worker.js) in a
real project.

4. Real-World Project: Simple To-Do List Application


This project will demonstrate several concepts:
●​ DOM Manipulation
●​ Event Handling
●​ Local Storage for persistence
●​ Basic Array methods
●​ Functions
The application will allow users to add, delete, and mark tasks as complete, with tasks persisting
across browser sessions.
<!DOCTYPE html>​
<html lang="en">​
<head>​
<meta charset="UTF-8">​
<meta name="viewport" content="width=device-width,
initial-scale=1.0">​
<title>Simple To-Do List</title>​
<!-- Tailwind CSS CDN for easy styling -->​
<script src="https://cdn.tailwindcss.com"></script>​
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&
display=swap" rel="stylesheet">​
<style>​
body {​
font-family: 'Inter', sans-serif;​
background-color: #f0f2f5;​
display: flex;​
justify-content: center;​
align-items: flex-start; /* Align to top for better
content flow */​
min-height: 100vh;​
padding: 20px;​
box-sizing: border-box;​
}​
.container {​
background-color: #ffffff;​
border-radius: 1rem; /* Rounded corners */​
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);​
padding: 2rem;​
width: 100%;​
max-width: 500px;​
margin-top: 2rem; /* Add some margin from the top */​
}​
.task-item {​
display: flex;​
align-items: center;​
justify-content: space-between;​
padding: 0.75rem 0;​
border-bottom: 1px solid #e5e7eb;​
}​
.task-item:last-child {​
border-bottom: none;​
}​
.task-text {​
flex-grow: 1;​
margin-right: 1rem;​
word-break: break-word; /* Ensure long words wrap */​
}​
.task-text.completed {​
text-decoration: line-through;​
color: #6b7280;​
}​
.action-buttons button {​
padding: 0.4rem 0.8rem;​
border-radius: 0.5rem;​
font-size: 0.875rem;​
font-weight: 600;​
transition: background-color 0.2s ease-in-out;​
}​
.action-buttons .complete-btn {​
background-color: #d1fae5; /* Light green */​
color: #065f46; /* Dark green text */​
}​
.action-buttons .complete-btn:hover {​
background-color: #a7f3d0;​
}​
.action-buttons .delete-btn {​
background-color: #fee2e2; /* Light red */​
color: #991b1b; /* Dark red text */​
margin-left: 0.5rem;​
}​
.action-buttons .delete-btn:hover {​
background-color: #fecaca;​
}​
.input-group {​
display: flex;​
gap: 0.5rem;​
margin-bottom: 1.5rem;​
}​
.input-group input {​
flex-grow: 1;​
padding: 0.75rem 1rem;​
border: 1px solid #d1d5db;​
border-radius: 0.75rem;​
outline: none;​
transition: border-color 0.2s;​
}​
.input-group input:focus {​
border-color: #3b82f6; /* Blue focus ring */​
}​
.input-group button {​
background-color: #3b82f6; /* Blue */​
color: white;​
padding: 0.75rem 1.5rem;​
border-radius: 0.75rem;​
font-weight: 600;​
transition: background-color 0.2s ease-in-out;​
cursor: pointer;​
}​
.input-group button:hover {​
background-color: #2563eb; /* Darker blue */​
}​
.message-box {​
background-color: #fff;​
border-radius: 0.75rem;​
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);​
padding: 1rem;​
margin-top: 1rem;​
text-align: center;​
position: fixed;​
top: 50%;​
left: 50%;​
transform: translate(-50%, -50%);​
z-index: 1000;​
display: none; /* Hidden by default */​
max-width: 300px;​
}​
.message-box button {​
background-color: #3b82f6;​
color: white;​
padding: 0.5rem 1rem;​
border-radius: 0.5rem;​
margin-top: 0.75rem;​
cursor: pointer;​
}​
.message-box.show {​
display: block;​
}​
</style>​
</head>​
<body class="antialiased">​
<div class="container">​
<h1 class="text-3xl font-bold text-center text-gray-800
mb-6">My To-Do List</h1>​

<div class="input-group">​
<input type="text" id="taskInput" placeholder="Add a new
task..." class="focus:ring-2 focus:ring-blue-500">​
<button id="addTaskBtn">Add Task</button>​
</div>​

<ul id="taskList" class="divide-y divide-gray-200">​
<!-- Tasks will be dynamically added here -->​
</ul>​
</div>​

<!-- Custom Message Box -->​
<div id="messageBox" class="message-box">​
<p id="messageBoxText"></p>​
<button id="messageBoxCloseBtn">OK</button>​
</div>​

<script>​
// Get DOM elements​
const taskInput = document.getElementById('taskInput');​
const addTaskBtn = document.getElementById('addTaskBtn');​
const taskList = document.getElementById('taskList');​
const messageBox = document.getElementById('messageBox');​
const messageBoxText =
document.getElementById('messageBoxText');​
const messageBoxCloseBtn =
document.getElementById('messageBoxCloseBtn');​

// Array to store tasks​
// Each task object will have: { id: string, text: string,
completed: boolean }​
let tasks = [];​

/**​
* Displays a custom message box instead of alert().​
* @param {string} message - The message to display.​
*/​
function showMessageBox(message) {​
messageBoxText.textContent = message;​
messageBox.classList.add('show');​
}​

/**​
* Hides the custom message box.​
*/​
function hideMessageBox() {​
messageBox.classList.remove('show');​
}​

// Event listener for the message box close button​
messageBoxCloseBtn.addEventListener('click', hideMessageBox);​

/**​
* Loads tasks from local storage when the page loads.​
*/​
function loadTasks() {​
const storedTasks = localStorage.getItem('tasks');​
if (storedTasks) {​
tasks = JSON.parse(storedTasks);​
renderTasks();​
}​
}​

/**​
* Saves the current tasks array to local storage.​
*/​
function saveTasks() {​
localStorage.setItem('tasks', JSON.stringify(tasks));​
}​

/**​
* Renders all tasks from the `tasks` array to the DOM.​
*/​
function renderTasks() {​
taskList.innerHTML = ''; // Clear existing list items​

if (tasks.length === 0) {​
taskList.innerHTML = '<li class="text-center
text-gray-500 py-4">No tasks yet. Add one!</li>';​
return;​
}​

tasks.forEach(task => {​
const listItem = document.createElement('li');​
listItem.classList.add('task-item');​
listItem.setAttribute('data-id', task.id); // Store
task ID on the element​

// Task text span​
const taskTextSpan = document.createElement('span');​
taskTextSpan.classList.add('task-text',
'text-gray-700', 'text-lg');​
taskTextSpan.textContent = task.text;​
if (task.completed) {​
taskTextSpan.classList.add('completed');​
}​

// Action buttons container​
const actionButtonsDiv =
document.createElement('div');​
actionButtonsDiv.classList.add('action-buttons');​

// Complete button​
const completeBtn = document.createElement('button');​
completeBtn.classList.add('complete-btn');​
completeBtn.textContent = task.completed ? 'Undo' :
'Complete';​
completeBtn.addEventListener('click', () =>
toggleComplete(task.id));​

// Delete button​
const deleteBtn = document.createElement('button');​
deleteBtn.classList.add('delete-btn');​
deleteBtn.textContent = 'Delete';​
deleteBtn.addEventListener('click', () =>
deleteTask(task.id));​

// Append elements​
actionButtonsDiv.appendChild(completeBtn);​
actionButtonsDiv.appendChild(deleteBtn);​
listItem.appendChild(taskTextSpan);​
listItem.appendChild(actionButtonsDiv);​
taskList.appendChild(listItem);​
});​
}​

/**​
* Adds a new task to the list.​
*/​
function addTask() {​
const taskText = taskInput.value.trim(); // Get text and
remove whitespace​

if (taskText === '') {​
showMessageBox('Task cannot be empty!');​
return;​
}​

const newTask = {​
id: crypto.randomUUID(), // Generate a unique ID for
the task​
text: taskText,​
completed: false​
};​

tasks.push(newTask);​
saveTasks(); // Save to local storage​
renderTasks(); // Re-render the list​
taskInput.value = ''; // Clear input field​
}​

/**​
* Toggles the 'completed' status of a task.​
* @param {string} id - The ID of the task to toggle.​
*/​
function toggleComplete(id) {​
tasks = tasks.map(task =>​
task.id === id ? { ...task, completed: !task.completed
} : task​
);​
saveTasks();​
renderTasks();​
}​

/**​
* Deletes a task from the list.​
* @param {string} id - The ID of the task to delete.​
*/​
function deleteTask(id) {​
tasks = tasks.filter(task => task.id !== id);​
saveTasks();​
renderTasks();​
}​

// Event listener for Add Task button click​
addTaskBtn.addEventListener('click', addTask);​

// Event listener for Enter key press in the input field​
taskInput.addEventListener('keypress', (event) => {​
if (event.key === 'Enter') {​
addTask();​
}​
});​

// Load tasks when the page first loads​
document.addEventListener('DOMContentLoaded', loadTasks);​
</script>​
</body>​
</html>​

You might also like