Reference Material

Pro JS Documentation

১২ মাসের সিলেবাসের প্রতিটি টপিকের বিস্তারিত ধারণা এবং কনসেপ্ট এই পেইজে দেওয়া হলো।

Enough & Pro Documentation

এই ডকুমেন্টেশনে যে টপিকগুলো আছে সেগুলোই রিয়েল প্রজেক্ট ও ইন্টারভিউতে সবচেয়ে বেশি ব্যবহার হয়। প্রতিটি টপিক কী, কেন শিখবেন, কখন কোথায় লাগবে এবং সিনট্যাক্স/উদাহরণ দিয়ে ভালোভাবে বোঝানো হয়েছে। একবার ঠিকমতো পড়ে প্র্যাকটিস করলে ভবিষ্যতে জব, ফ্রিল্যান্স ও ওপেন সোর্সে অনেক এগিয়ে থাকবেন।

অতি গুরুত্বপূর্ণ

Array, Object, Loop, Event Loop ও Event Listener

JS এ এই টপিকগুলো না বুঝলে প্রায় কিছুই করা যায় না। নিচে ধারণা, কখন কোথায় কীভাবে ব্যবহার করবেন এবং অনেক উদাহরণ দেওয়া হলো — কপি করে Console এ রান করে দেখুন।

ভবিষ্যতে কোথায় লাগবে

Array (map/filter/reduce): প্রায় প্রতিটি প্রজেক্টে লিস্ট ডেটা ট্রান্সফর্ম, API রেসপন্স প্রসেসিং, সার্চ/ফিল্টার। ইন্টারভিউতে প্রায়ই জিজ্ঞাসা। Object: ইউজার/প্রোডাক্ট ডেটা, সেটিংস, ফ্রিকোয়েন্সি কাউন্টার, JSON। Event Loop: অ্যাসিঙ্ক বাগ ডিবাগ, সিনক্রোনাস vs অ্যাসিঙ্ক বোঝা। Event Listener: যেকোনো UI — বাটন, ফর্ম, ডায়নামিক এলিমেন্ট। এগুলো ভালো করলে রিয়েল ওয়ার্কে দ্রুত অ্যাডজাস্ট করতে পারবেন।

কেন Loop, Event Loop, Array, Object ও String সবচেয়ে গুরুত্বপূর্ণ?

ArrayObject দিয়েই জেএসে যেকোনো ডেটা স্ট্রাকচার বানানো হয় — API থেকে ডেটা আসে অ্যারে বা অবজেক্টে, ইউজার লিস্ট, প্রোডাক্ট লিস্ট, সেটিংস সব এখানেই। Loop ছাড়া অ্যারেতে ঘুরে মান বের করা বা ট্রান্সফর্ম করা যায় না; for, for...of, forEach প্রতিদিন ব্যবহার হয়। Event Loop বোঝা জরুরি কারণ জেএস এক থ্রেড — API কল, টাইমার, ক্লিক ইভেন্ট সব অ্যাসিঙ্ক; কখন কোন কোড রান হবে সেটা Event Loop দিয়ে নিয়ন্ত্রিত। না বুঝলে "ডেটা আসার আগেই ব্যবহার" করার মতো বাগ হয়। String — ফর্ম ভ্যালিডেশন, সার্চ, URL বা টেক্সট কাটাকুটি, API থেকে আসা স্ট্রিং পার্স করা সব জায়গায় লাগে। তাই এই পাঁচটা টপিক ভালো করে শিখলে বাকি জেএস অনেক সহজ হয়ে যায়।

১. Array (অ্যারে)

কী: অ্যারে হলো অর্ডার করা লিস্ট — একই বা আলাদা টাইপের অনেকগুলো ভ্যালু একটা ভেরিয়েবলে। ইনডেক্স ০ থেকে শুরু। কখন কোথায়: লিস্ট ডেটা (ইউজার লিস্ট, টাস্ক, প্রোডাক্ট), লুপ চালাতে, map/filter/reduce দিয়ে ট্রান্সফর্ম করতে।

সিনট্যাক্স (প্রায়ই ব্যবহৃত):

array.map(callback) → নতুন অ্যারে।  |  array.filter(callback) → শর্ত মেনে উপাদান।  |  array.reduce((acc, item) => {...}, initial) → একক মান।

কিভাবে কাজ করে ও ব্যবহার করবেন:

// বানানো ও অ্যাক্সেস
const arr = [10, 20, 30, 40, 50];
console.log(arr[0]);   // 10 (প্রথম)
console.log(arr.length); // 5
arr.push(60);  // শেষে যোগ
arr.pop();     // শেষটা বাদ
// map: প্রতিটা আইটেম বদলিয়ে নতুন অ্যারে
const doubled = arr.map(x => x * 2);
console.log(doubled); // [20, 40, 60, 80, 100]
// filter: শর্ত মেনে শুধু কিছু রাখা
const big = arr.filter(x => x > 25);
console.log(big); // [30, 40, 50]
// reduce: পুরো অ্যারে থেকে একটা মান (যেমন যোগফল)
const sum = arr.reduce((acc, x) => acc + x, 0);
console.log(sum); // 150
// find: প্রথম যে আইটেম শর্ত মেনে
const first = arr.find(x => x > 25);
console.log(first); // 30

map, filter সহ যে মেথডগুলো কোডকে efficient করে — এবং কিভাবে কাজ করে

এই মেথডগুলো লুপ নিজে লিখার বদলে এক লাইনে কাজ সেরে দেয়; কোড ছোট হয়, পড়তে ও বাগ কম হয়। নিচে প্রতিটির কিভাবে কাজ করে সংক্ষেপে দেওয়া হলো।

map — কিভাবে কাজ করে

অ্যারের প্রতিটি উপাদানের ওপর আপনার দেওয়া callback ফাংশন একবার করে কল হয়; প্রতিবার যে মান ফাংশন return করে সেটা নতুন অ্যারের সেই পজিশনে বসে। শেষে একদম সমান লেন্থের নতুন অ্যারে ফিরে আসে। অরিজিনাল অ্যারে বদলায় না।

filter — কিভাবে কাজ করে

প্রতিটি উপাদানে callback চালিয়ে যেগুলো true রিটার্ন করে শুধু সেগুলো নতুন অ্যারে তে রাখে। যে উপাদানে false বা falsy রিটার্ন সেটা বাদ পড়ে। তাই লেন্থ অরিজিনালের চেয়ে কম বা সমান হতে পারে। অরিজিনাল বদলায় না।

reduce — কিভাবে কাজ করে

একটা "accumulator" (acc) মান দিয়ে শুরু করেন (দ্বিতীয় আর্গুমেন্ট)। অ্যারে বাম থেকে ডানে একটার পর একটা উপাদান নিয়ে callback(acc, currentItem) চালিয়ে যে মান return করেন সেটাই পরের স্টেপের acc হয়। শেষ উপাদান শেষে যে acc থাকে সেটাই reduce এর রিটার্ন। দিয়ে যোগফল, গড়, অ্যারে থেকে অবজেক্ট, গ্রুপ বাই — সব বের করা যায়।

find / findIndex — কিভাবে কাজ করে

বাম থেকে ঘুরে যেই প্রথম উপাদানে callback true রিটার্ন করে, find সেটা (ভ্যালু) রিটার্ন করে, findIndex সেই ইনডেক্স রিটার্ন করে। কোনটাই মিল না পেলে find undefined, findIndex -1 দেয়।

some / every — কিভাবে কাজ করে

some: যেই মুহূর্তে কোনো একটা উপাদানে callback true রিটার্ন করে, তখনই some true রিটার্ন করে এবং বাকি চেক বন্ধ। সব false হলে false। every: সব উপাদানে callback true হতে হবে; একটা false হলেই every false রিটার্ন করে এবং বন্ধ। খালি অ্যারে every → true, some → false।

flat / flatMap — কিভাবে কাজ করে

flat(depth): নেস্টেড অ্যারে (অ্যারের ভেতরে অ্যারে) কে এক লেভেল বা কয় লেভেল চ্যাপ্টা করে দেয়। [[1,2],[3,4]].flat()[1,2,3,4]flatMap: আগে map চালায়, তারপর ফলাফল এক লেভেল flat করে। তাই map + flat(1) এর শর্টকাট; প্রতিটি আইটেম থেকে অ্যারে বের করে সবার ভ্যালু এক অ্যারে করতে কাজে লাগে।

at(index) — কিভাবে কাজ করে

অ্যারের কোনো ইনডেক্সের ভ্যালু নিতে arr.at(0) প্রথম, arr.at(-1) শেষ। নেগেটিভ ইনডেক্স পেছন থেকে (যেমন -1 = শেষ, -2 = শেষের আগের)। arr[arr.length - 1] লিখার বদলে arr.at(-1) দিলে পড়তে সহজ।

sort — কিভাবে কাজ করে

অরিজিনাল অ্যারে কে জায়গায় (in-place) সর্ট করে, মানে নিজেই বদলে যায়। callback না দিলে স্ট্রিং হিসেবে ইউনিকোড অর্ডারে সাজায় (সংখ্যা সঠিকভাবে সর্ট হয় না)। সংখ্যা সর্ট করতে (a, b) => a - b (আরোহী) বা (a, b) => b - a (অবরোহী) দিতে হয়। অরিজিনাল রাখতে চাইলে আগে [...arr].sort(...) করে কপি নিন।

// flatMap: প্রতিটি শব্দ ভাঙে অক্ষরের অ্যারে, তারপর সবার একসাথে
['hi', 'ok'].flatMap(s => s.split(''));  // ['h','i','o','k']
// at: শেষ আইটেম
const last = [10, 20, 30].at(-1);  // 30
// sort সংখ্যা (কপি নিয়ে, অরিজিনাল অক্ষুণ্ণ)
const sorted = [...[3, 1, 2]].sort((a, b) => a - b);  // [1, 2, 3]

কোডিং লাইফে সবচেয়ে বেশি ব্যবহৃত Array মেথড — সংক্ষিপ্ত টেবিল

মেথডকোথায় লাগে (কোডিং লাইফ)সংক্ষিপ্ত উদাহরণ
push / popলিস্টে আইটেম যোগ/সরানো (টো-ডু, কার্ট)cart.push(item); cart.pop();
mapAPI ডেটা থেকে UI এর জন্য নতুন অ্যারে (নামের লিস্ট, আইডি)users.map(u => u.name)
filterসার্চ/ফিল্টার (ক্যাটাগরি, প্রাইস রেঞ্জ)products.filter(p => p.price < 100)
reduceযোগফল, গড়, গ্রুপ বাই, এক অবজেক্টে জমাprices.reduce((sum, p) => sum + p, 0)
find / findIndexআইডি দিয়ে একটা আইটেম খোঁজাusers.find(u => u.id === 5)
includesকোনো মান আছে কিনা চেক (সিম্পল)ids.includes(10)
some / everyকোনো একটা শর্ত মেনেছে? / সব মেনেছে?arr.some(x => x > 0)
flat / flatMapনেস্টেড অ্যারে চ্যাপ্টা করা; map + flat একসাথেarr.flat(); arr.flatMap(x => x.items)
at(i)ইনডেক্স দিয়ে ভ্যালু; at(-1) = শেষarr.at(-1)
sliceকপি বা অংশ নেওয়া (অরিজিনাল বদলায় না)arr.slice(0, 5)
sortসর্ট (সংখ্যা: (a,b) => a-b); অরিজিনাল বদলায়, কপি চাইলে [...arr].sort()[...arr].sort((a,b) => a-b)
// রিয়েল লাইফ উদাহরণ: API থেকে ইউজার নিয়ে শুধু নাম + টোটাল কাউন্ট
const users = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
const names = users.map(u => u.name);           // ['A', 'B']
const total = users.reduce((n, u) => n + 1, 0); // 2
const hasA = users.some(u => u.name === 'A');   // true

২. Object (অবজেক্ট)

কী: অবজেক্ট হলো key-value জোড়ার কালেকশন — একটা এনটিটির বিভিন্ন প্রপার্টি (নাম, বয়স, ইমেইল) একসাথে রাখা। কখন কোথায়: ইউজার/প্রোডাক্ট ডেটা, সেটিংস, API থেকে আসা JSON, ফ্রিকোয়েন্সি কাউন্টার (কোন অক্ষর কতবার)।

Object.keys(obj) → কীগুলোর অ্যারে।  |  Object.values(obj) → ভ্যালুগুলোর অ্যারে।  |  Object.entries(obj) → [key, value] জোড়ার অ্যারে।

কিভাবে কাজ করে: Object.keys(obj) অবজেক্টের নিজস্ব (enumerable) প্রপার্টিগুলোর শুধু কী নামগুলো অ্যারে হিসেবে দেয়। Object.values(obj) সেই কীগুলোর ভ্যালুগুলো অ্যারে করে দেয়। Object.entries(obj) প্রতিটি প্রপার্টিকে [key, value] জোড়া হিসেবে দেয় — লুপে for (const [k, v] of Object.entries(obj)) চালাতে বা Map বানাতে সুবিধা। এই তিনটা মেথড অরিজিনাল অবজেক্ট বদলায় না।

কিভাবে কাজ করে ও ব্যবহার করবেন:

// বানানো ও অ্যাক্সেস
const user = { name: 'Karim', age: 28, city: 'Dhaka' };
console.log(user.name);  // Karim (dot)
console.log(user['age']); // 28 (bracket)
user.email = 'k@mail.com'; // নতুন প্রপার্টি যোগ
// Keys, Values, Entries — লুপ বা অ্যারে বানানোর জন্য
console.log(Object.keys(user));   // ['name','age','city','email']
console.log(Object.values(user)); // ['Karim', 28, 'Dhaka', 'k@mail.com']
console.log(Object.entries(user)); // [['name','Karim'], ['age',28], ...]
// ফ্রিকোয়েন্সি কাউন্টার (কোন অক্ষর কতবার)
function countChars(str) {
  const obj = {};
  for (const ch of str) {
    obj[ch] = (obj[ch] || 0) + 1;
  }
  return obj;
}
console.log(countChars('hello')); // { h:1, e:1, l:2, o:1 }

কোডিং লাইফে সবচেয়ে ব্যবহৃত Object প্যাটার্ন ও মেথড

মেথড/প্যাটার্নকোথায় লাগেউদাহরণ
Object.keys(obj)অবজেক্টের সব কী ঘুরতে, ফর্ম ফিল্ড চেকObject.keys(settings).forEach(k => ...)
Object.values(obj)শুধু ভ্যালু নিয়ে অ্যারে বানানোObject.values(user)
Object.entries(obj)key-value জোড়া দিয়ে লুপ, Map বানানোfor (const [k,v] of Object.entries(obj))
{ ...obj } (spread)কপি, একটু বদলে নতুন অবজেক্ট{ ...user, name: 'New' }
obj[key] = (obj[key] || 0) + 1ফ্রিকোয়েন্সি কাউন্টার (অক্ষর, শব্দ)ইন্টারভিউ ও অ্যালগোরিদমে খুব কমন
// রিয়েল লাইফ: সেটিংস থেকে শুধু সক্রিয় কীগুলো
const settings = { theme: 'dark', sound: true, lang: 'en' };
const keys = Object.keys(settings);  // ['theme','sound','lang']
const copy = { ...settings, theme: 'light' }; // কপি + ওভাররাইড

৩. Loop (লুপ)

কী: একই কাজ বারবার করানোর জন্য লুপ। কখন কোনটা: ইনডেক্স দরকার হলে for (let i=0; ...); অ্যারের ভ্যালু দরকার হলে for...of; অবজেক্টের কী/ভ্যালু ঘুরতে for...in বা Object.keys(); প্রতিটায় একটা কাজ করতে forEach

কিভাবে কাজ করে: for (let i=0; i<arr.length; i++) — i দিয়ে ০ থেকে length-১ পর্যন্ত এক এক করে বাড়ে, প্রতিবার arr[i] এক্সেস করেন। for...of অ্যারের প্রতিটি উপাদানকে একটার পর একটা ভ্যারিয়েবলে দেয় (ইনডেক্স দেয় না)। for...in অবজেক্টের keys দিয়ে ঘোরে; অ্যারে এ ব্যবহার করলে ইনডেক্স (স্ট্রিং) পাবেন, তাই অ্যারে ঘুরতে for...of ভালো। forEach অ্যারের প্রতিটি উপাদানের জন্য callback(value, index, array) একবার করে চালায়; মাঝে break করা যায় না, return শুধু সেই callback থেকে বের হয়।

উদাহরণ — কপি করে রান করুন:

const nums = [10, 20, 30];
// for — ইনডেক্স দরকার হলে
for (let i = 0; i < nums.length; i++) {
  console.log(i, nums[i]); // 0 10, 1 20, 2 30
}
// for...of — শুধু ভ্যালু দরকার (সবচেয়ে সহজ)
for (const n of nums) {
  console.log(n); // 10, 20, 30
}
// for...in — অবজেক্টের keys (অ্যারে না দেয়া ভালো)
const obj = { a: 1, b: 2 };
for (const key in obj) {
  console.log(key, obj[key]); // a 1, b 2
}
// forEach — অ্যারের প্রতিটি উপাদানে ফাংশন চালানো
nums.forEach((value, index) => {
  console.log(index, value);
});

কোডিং লাইফে কখন কোন লুপ ব্যবহার করবেন

ইনডেক্স দরকার (যেমন i, position) বা পেছন দিক দিয়ে ঘুরতে চাইলে → for (let i = 0; i < arr.length; i++)। শুধু ভ্যালু দরকার, অ্যারেতে ঘুরতে → for (const item of arr) — সবচেয়ে পড়তে সহজ। অবজেক্টের key/value ঘুরতে → for (const key in obj) বা Object.keys(obj).forEach(...)। অ্যারের প্রতিটায় সাইড ইফেক্ট (DOM আপডেট, লগ) করতে চাইলে → arr.forEach((item, i) => { ... })। নতুন অ্যারে বা এক মান বের করতে চাইলে map/filter/reduce ব্যবহার করুন, শুধু লুপের জন্য for বা for...of।

৪. Event Loop (ইভেন্ট লুপ)

কী: JavaScript একটাই থ্রেডে চলে। Event Loop হলো ইঞ্জিনের যে মেকানিজম দিয়ে এটি সিনক্রোনাস কোড আগে রান করে, তারপর কলব্যাক/প্রমিজ (async) গুলো একটার পর একটা চালায় — যাতে UI ব্লক না হয়। কিভাবে কাজ করে (সংক্ষেপে): (১) Call Stack এ যে ফাংশনগুলো একটার পর একটা চলে সেগুলো শেষ হয়; (২) তারপর Task Queue / Microtask Queue থেকে একটা করে টাস্ক নিয়ে স্ট্যাকে দিয়ে রান করে; (৩) এই সাইকেল চলতেই থাকে। তাই setTimeout(fn, 0) বা প্রমিজের .then সবসময় বর্তমান সিনক্রোনাস কোডের পরে রান হয়।

কখন কোথায় মাথায় রাখবেন:

API কল, setTimeout, addEventListener — এগুলো অ্যাসিঙ্ক; এগুলো শেষ হওয়ার পরের কোড তখনই রান হবে যখন Event Loop ওই কলব্যাকটা স্ট্যাকে দেবে। তাই async ডেটার পরের লজিক কলব্যাক বা async/await এর ভেতরে লিখতে হয়।

// Event Loop বোঝার জন্য: এটা রান করে দেখুন
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// আউটপুট: A, D, C, B (সিনক্রোনাস আগে, তারপর মাইক্রোটাস্ক, তারপর টাস্ক)

কোডিং লাইফে: fetch() বা API কল করলে রেসপন্স আসতে সময় লাগে। সেই সময় পর্যন্ত বাকি কোড রান করতে থাকে। তাই API এর ডেটা ব্যবহার করতে চাইলে অবশ্যই await fetch() বা .then(callback) এর ভেতরে লিখতে হয় — বাইরে লিখলে ডেটা তখনো আসেনি বলে undefined বা এরর পাবেন। Event Loop এই নিয়ম দিয়ে অ্যাসিঙ্ক কাজগুলো সিরিয়াল রাখে।

৫. Event Listener (ইভেন্ট লিসেনার)

কী: ইউজার ক্লিক, টাইপ, স্ক্রল করলে ব্রাউজার একটা ইভেন্ট ফায়ার করে। Event Listener দিয়ে আপনি বলতে পারেন “এই ইভেন্ট হলে এই ফাংশন চালাও”। কখন কোথায়: বাটন ক্লিক, ইনপুটে টাইপ, ফর্ম সাবমিট, স্ক্রল, কী-প্রেস — যেকোনো ইউজার অ্যাকশনে রেসপন্স দিতে।

element.addEventListener('eventType', callback) — eventType: 'click', 'submit', 'keyup', 'input', 'scroll', 'load'

কিভাবে লিখতে হয় ও ব্যবহার করবেন:

// বেসিক: একটা এলিমেন্টে ক্লিক শুনা
// HTML: <button id="btn">Click</button>
const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
  console.log('Button clicked!');
});
// ইভেন্ট অবজেক্ট ব্যবহার (কি ক্লিক হয়েছে, কী প্রেস হয়েছে)
btn.addEventListener('click', (e) => {
  console.log(e.target);      // যে এলিমেন্টে ক্লিক
  console.log(e.type);       // 'click'
});
input.addEventListener('keyup', (e) => {
  console.log(e.key, e.target.value);
});
// Event Delegation: প্যারেন্টে এক লিসেনার, অনেক চাইল্ডের জন্য
// HTML: <ul id="list"><li>A</li><li>B</li></ul>
document.getElementById('list').addEventListener('click', (e) => {
  const li = e.target.closest('li');
  if (!li) return;
  console.log('Clicked:', li.textContent);
});
// কমন ইভেন্ট টাইপ: 'click', 'submit', 'input', 'keyup', 'scroll', 'load'
form.addEventListener('submit', (e) => {
  e.preventDefault(); // পেজ রিলোড আটকাতে
  console.log('Form submitted');
});

Pro Tip

map/filter/reduce চেইন করলে পড়া সহজ হয়: arr.filter(x => x > 0).map(x => x * 2).reduce((a,b) => a + b, 0)। Event Listener অনেক বাটন/আইটেমে দিতে Event Delegation ব্যবহার করুন — প্যারেন্টে এক লিসেনার, ভেতরে e.target.closest('button') দিয়ে চেক করুন।

সংক্ষেপে মনে রাখুন

  • Array: লিস্ট ডেটা; map/filter/reduce দিয়ে ট্রান্সফর্ম ও একক মান বের করুন।
  • Object: key-value; এক এনটিটির প্রপার্টি বা ফ্রিকোয়েন্সি কাউন্টার।
  • Loop: ইনডেক্স চাইলে for; শুধু ভ্যালু for...of; অবজেক্টে for...in বা Object.keys।
  • Event Loop: সিনক্রোনাস কোড আগে; তারপর অ্যাসিঙ্ক কলব্যাক — তাই async ডেটার পরের কাজ কলব্যাক/await এর ভেতরে লিখুন।
  • Event Listener: addEventListener('eventType', callback); অনেক এলিমেন্টে এক লিসেনার দিতে Delegation (প্যারেন্ট + e.target.closest) ব্যবহার করুন।
Variables, Scope & Hoisting

JS এর বেসিক ফাউন্ডেশন। কিভাবে মেমরিতে ভেরিয়েবল কাজ করে তা না জানলে অনেক বাগ ফেস করতে হয়। নিচের উদাহরণগুলো কপি করে ব্রাউজারের Console এ রান করে দেখতে পারবেন।

ভবিষ্যতে কোথায় লাগবে

var/let/const ও Hoisting ইন্টারভিউতে প্রায়ই জিজ্ঞাসা হয়। বড় প্রজেক্টে scope ভুল বোঝলে বাগ হয়; টিম ওয়ার্কে let/const ব্যবহারের নিয়ম জানা জরুরি।

কিভাবে ফাংশনে ব্যবহার করব?

যে ভ্যালু পরিবর্তন হবে সেখানে let, যে পরিবর্তন হবে না (রেফারেন্স বা প্রিমিটিভ) সেখানে const ব্যবহার করুন। ফাংশনের ভেতরে বা ব্লক (if/for) এর ভেতরে declare করলে শুধু সেখানেই এক্সেস থাকবে। লুপের ভেতরে var না দিয়ে let দিন যাতে লুপের বাইরে সেই ভেরিয়েবল লিক না হয়।

কেন let/const ভালো?

Block scope হওয়ায় বাগ কমে; একই নাম দিয়ে অন্য ব্লকে ব্যবহার করলেও সংঘর্ষ হয় না। const দিয়ে ইচ্ছাকার পরিবর্তন কমিয়ে কোড পড়া সহজ হয়। Hoisting-এর কারণে var দিয়ে অপ্রত্যাশিত undefined পেতে পারেন; let/const টিডিজেডে থাকায় declare করার আগে ব্যবহার করলে সরাসরি এরর পাবেন, ডিবাগ করতে সুবিধা।

var, let, const

var হলো function-scoped এবং এর হোইস্টিং এ ঝামেলা আছে। তাই আধুনিক JS এ সবসময় let (চেঞ্জেবল ভ্যালু) এবং const (ফিক্সড ভ্যালু) ব্যবহার করা হয়, যা block-scoped.

// var - একই নাম দিয়ে একাধিকবার declare করা যায় (খারাপ প্র্যাকটিস)
var x = 5;
var x = 10; // allowed
console.log(x); // 10
// let - একই স্কোপে দ্বিতীয়বার declare করা যাবে না
let y = 5;
// let y = 10; // Error: Identifier 'y' has already been declared

মনে রাখবেন: প্রাক্টিক্যাল প্রোজেক্টে সবসময় let এবং const ব্যবহার করবেন।

Hoisting (সহজ ভাবে)

কোড রান করার আগে JS ইঞ্জিন ভেরিয়েবল এবং ফাংশন ডিক্লারেশনগুলোকে ফাইলের শুরুতে নিয়ে যায়, একে Hoisting বলে। কিন্তু let এবং const TDZ (Temporal Dead Zone) এ থাকে বলে প্রথমে ব্যবহার করলে Error হয়।

console.log(a); // Output: undefined (var এর ক্ষেত্রে)
var a = 5;
// let ব্যবহার করলে:
// console.log(b); // ReferenceError (TDZ এর জন্য)
let b = 10;

এক কথায়: var আগে থেকে রেজিস্টার হয় (value = undefined), আর let/const আগে থেকে ব্যবহার করলে ক্র্যাশ করে।

Mini Practice (Copy & Run)

নিচের কোড ব্লক কপি করে Console এ পেস্ট করুন এবং কী আউটপুট আসে তা নিজে নিজে প্রেডিক্ট করার চেষ্টা করুন।

function testScope() {
  if (true) {
    var a = 'var inside if';
    let b = 'let inside if';
  }
  console.log(a); // ?
  console.log(typeof b); // ?
}
testScope();

Hint: a function scope, কিন্তু b শুধু if ব্লকের ভেতরে।

Arrays & Objects Mastery

ডেটা ম্যানিপুলেশনের জন্য এই মেথডগুলো জানা বাধ্যতামূলক। নিচের উদাহরণগুলো কপি করে রান করলে ভিজুয়ালি বুঝতে সুবিধা হবে।

কিভাবে কোডে ব্যবহার করব?

যেকোনো অ্যারে বা অবজেক্টের উপর ডট দিয়ে মেথড কল করুন: arr.map(...), arr.filter(...), Object.keys(obj)। ফাংশনের ভেতরে রিটার্ন করতে চাইলে এই মানগুলো ভেরিয়েবলে রাখুন (যেমন const evens = numbers.filter(...)) তারপর return evens

কেন map / filter / reduce ব্যবহার ভালো?

map: প্রতিটি আইটেম ট্রান্সফর্ম করতে (নতুন অ্যারে)। filter: শর্ত মেনে শুধু কিছু আইটেম রাখতে। reduce: পুরো অ্যারে থেকে একটা একক মান বের করতে (যোগফল, গড়, অবজেক্ট বানানো)। for লুপের চেয়ে ইচ্ছা পরিষ্কার এবং চেইন করা যায় (যেমন .filter().map())।

Array Methods

  • push() / pop(): Array এর শেষে ডেটা যোগ করা এবং রিমুভ করা। O(1) টাইম নেয়।
  • map(): একটি Array এর প্রতিটি আইটেমকে মডিফাই করে নতুন Array রিটার্ন করে।
  • filter(): কন্ডিশনের উপর ভিত্তি করে নির্দিষ্ট আইটেমগুলো ছেঁকে বের করে।
  • reduce(): পুরো Array কে প্রসেস করে একটি সিঙ্গেল ভ্যালুতে (যেমন যোগফল) নিয়ে আসে।
const numbers = [1, 2, 3, 4, 5];
// Filter even numbers
const evens = numbers.filter(n => n % 2 === 0);
// Sum of all numbers
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(evens, sum); // [2,4], 15

Object & String

  • Object.keys(): Object এর সব Key গুলোর Array দেয়।
  • Object.values(): Object এর সব Value গুলোর Array দেয়।
  • split(''): String কে ভেঙে Array বানায়।
  • join(''): Array কে জোড়া লাগিয়ে String বানায়।
const user = { name: 'Rahim', age: 25 };
console.log(Object.keys(user));   // ['name','age']
console.log(Object.values(user)); // ['Rahim',25]
const text = 'level';
const reversed = text.split('').reverse().join('');
console.log(reversed === text); // palindrome check

Mini Project: Frequency Counter

একটি স্ট্রিং-এর প্রতিটি অক্ষর কতবার এসেছে তা গণনা করার জন্য Object ব্যবহার করা হয়।

function charFrequency(str) {
  const freq = {};
  for (const ch of str) {
    freq[ch] = (freq[ch] || 0) + 1;
  }
  return freq;
}
console.log(charFrequency('hello'));

String, JSON ও Destructuring

প্রোজেক্টে স্ট্রিং ম্যানিপুলেশন, API থেকে আসা JSON এবং ডেটা ভাঙতে Destructuring প্রতিদিন লাগে।

ভবিষ্যতে কোথায় লাগবে

JSON.parse / JSON.stringify: প্রায় প্রতিটি API রেসপন্স ও ফ্রন্ট-ব্যাকেন্ড ডেটা এক্সচেঞ্জ। Destructuring: React/Vue-তে props, state; ফাংশন প্যারামিটার; কোড ছোট ও পড়া সহজ। String (split, trim, includes): সার্চ, ভ্যালিডেশন, প্যালিনড্রোম/অ্যালগোরিদম।

কোডিং লাইফে সবচেয়ে ব্যবহৃত String মেথড — আলোচনা ও উদাহরণ

মেথডকোথায় লাগেউদাহরণ
trim()ইউজার ইনপুটের আগাপিছনের স্পেস সরানো' email@x.com '.trim()
toLowerCase() / toUpperCase()তুলনা বা ডিসপ্লে (কেস ইগনোর)str.toLowerCase() === 'yes'
split(sep)স্ট্রিং থেকে অ্যারে (শব্দ, লাইন, CSV)'a,b,c'.split(',')['a','b','c']
join(sep)অ্যারে থেকে স্ট্রিং (অবজেক্টে অ্যারের মেথড)['a','b'].join('-')'a-b'
includes(sub)সার্চ — সাবস্ট্রিং আছে কিনাtitle.includes('JS')
startsWith / endsWithপ্রিফিক্স/সাফিক্স চেক (ফাইলনেম, URL)path.startsWith('/api')
slice(start, end)কোনো অংশ কেটে নেওয়া (অরিজিনাল বদলায় না)'hello'.slice(0, 2)'he'
replace(old, new)একটা রিপ্লেস; সব বদলাতে regexstr.replace(/\s/g, '-')
// রিয়েল লাইফ: ফর্ম ভ্যালিডেশন + প্যালিনড্রোম
const input = '  Level  ';
const cleaned = input.trim().toLowerCase();  // 'level'
const rev = cleaned.split('').reverse().join('');
const isPalindrome = cleaned === rev;  // true
// সার্চ: 'JavaScript Pro'.includes('Pro')  // true

String Methods — সংক্ষিপ্ত রেফারেন্স

split, join, slice, includes, trim, toLowerCase — প্যালিনড্রোম চেক, সার্চ, কেস বদলাতে ব্যবহার করুন।

const s = '  Hello World  ';
s.trim().toLowerCase();        // 'hello world'
s.split(' ').filter(Boolean);  // ['Hello','World']
const rev = 'level'.split('').reverse().join('');
rev === 'level'; // true (palindrome)

JSON & Destructuring

API রেসপন্স স্ট্রিংকে JSON.parse(); অবজেক্ট/অ্যারে থেকে ভ্যালু বের করতে destructuring।

const jsonStr = '{"name":"A","age":25}';
const obj = JSON.parse(jsonStr);
const { name, age } = obj;
const [a, b] = [1, 2];
JSON.stringify(obj); // আবার স্ট্রিং

Advanced DOM & Events

ভবিষ্যতে কোথায় লাগবে

DOM ম্যানিপুলেশন ও Event Delegation যেকোনো ভ্যানিলা জেএস প্রজেক্টে লাগে। React/Vue ব্যবহার করলেও ভিতরে একই কনসেপ্ট; ইন্টারভিউতে "Event Bubbling", "Delegation", "DocumentFragment" জিজ্ঞাসা হয়। ডায়নামিক লিস্ট/টেবিলে এক প্যারেন্টে এক লিসেনার দেয়া প্র্যাকটিস করুন।

কিভাবে ইভেন্টে ব্যবহার করব?

কোন এলিমেন্টে ক্লিক/ইনপুট হলে কী হবে সেটা addEventListener('click', function) দিয়ে বাঁধুন। অনেকগুলো বাটন/লিস্ট আইটেম থাকলে প্রতিটায় আলাদা listener না দিয়ে, তাদের প্যারেন্ট একটায় listener দিন; ভেতরে event.target বা event.target.closest('li') দিয়ে কোন চাইল্ড ক্লিক হয়েছে চেক করুন — এটাই ইভেন্ট ডেলিগেশন।

কেন Event Delegation ও Fragment ভালো?

Delegation: ১০০টা বাটনে ১০০টা listener না দিয়ে একটা প্যারেন্টে একটাই দিলে মেমরি কম লাগে এবং ডায়নামিকভাবে যোগ হওয়া আইটেমেও কাজ করে। DocumentFragment: অনেকগুলো নোড লুপে বানিয়ে একবারে DOM-এ যোগ করলে রিফ্লো একবারই হয়, তাই পারফরম্যান্স ভালো।

Event Bubbling & Delegation

HTML এ ইভেন্টগুলো নিচ থেকে উপরের দিকে (Child -> Parent) পাস হয়, একে Event Bubbling বলে। এর সুবিধা নিয়ে Event Delegation করা হয়। অর্থাৎ ১০০টি বাটনে ১০০টি Event Listener যুক্ত করার বদলে, তাদের Parent এ মাত্র ১টি Listener যুক্ত করা, যা মেমরি সাশ্রয় করে।

// HTML ধারণা:
// <ul id="todo-list">
//   <li data-id="1">Task 1</li>
//   <li data-id="2">Task 2</li>
// </ul>
const list = document.getElementById('todo-list');
list.addEventListener('click', (event) => {
  const li = event.target.closest('li');
  if (!li) return;
  console.log('Clicked id:', li.dataset.id);
});

DocumentFragment

লুপ চালিয়ে ১০০টি এলিমেন্ট DOM এ যোগ করলে ব্রাউজার ১০০ বার রিফ্রেশ (Reflow) হয়। এর বদলে DocumentFragment ব্যবহার করলে মেমরিতে সব যোগ করে ব্রাউজারে মাত্র একবার রিফ্রেশ করা যায়।

const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = 'Item ' + i;
  fragment.appendChild(li); // মেমরিতে যোগ হচ্ছে
}
document.querySelector('ul').appendChild(fragment); // একসাথে যোগ!

Practice Idea: DOM কে WordPress এর DOM Tree-এর মতো ভেবে নিন — শুধু এখানে আপনি সরাসরি JavaScript দিয়ে node create/modify করতে পারেন।

Async & Await — বিস্তারিত

ইন্টারনেট থেকে ডেটা আনা, ফাইল রিড করা, বা যে কাজে সময় লাগে — সেগুলো JavaScript এ Asynchronousasync এবং await দিয়ে এই কাজগুলো সহজ ও পড়তে সুবিধা হয় (Promise-এর চেয়ে)।

ভবিষ্যতে কোথায় লাগবে

প্রায় প্রতিটি অ্যাপে API কল, ডেটা লোড, ফর্ম সাবমিট — সবই async। React/Vue/Angular বা যে ফ্রেমওয়ার্কেই যান, async/await + try/catch এবং Promise.all জানা থাকলে কাজ দ্রুত করবেন। ইন্টারভিউতেও প্রায়ই জিজ্ঞাসা।

async function fn() { const data = await fetch(url).then(r => r.json()); } — try/catch দিয়ে এরর হ্যান্ডল করুন। একসাথে অনেক API: await Promise.all([fetch(u1), fetch(u2)])

১. async এবং await কী?

  • async: কোনো ফাংশনের আগে লিখলে সেই ফাংশনটা একটা Promise রিটার্ন করে। মানে ফাংশনটা “আগামী কোনো সময়ে” রেজাল্ট দেবে।
  • await: শুধু async ফাংশনের ভেতরে ব্যবহার করা যায়। এটি কোনো Promise-এর সামনে বসালে JavaScript সেই Promise রেজলভ হওয়া পর্যন্ত অপেক্ষা করে, তারপর পরের লাইন চালায়। তাই কোড দেখতে একদম সাধারণ সিরিয়াল কোডের মতো লাগে।

২. কেন async/await ব্যবহার করব?

  • কোড পড়া ও লিখা সহজ — একটার পর একটা স্টেপ লিখতে পারি, .then().then() এর চেয়ে পরিষ্কার।
  • এরর হ্যান্ডলিং এক জায়গায় — try { ... } catch (err) { ... } দিয়ে সব async এরর ধরা যায়।
  • API কল, ফাইল রিড, টাইমার — যেকোনো “সময় নেয় এমন” কাজে ইন্ডাস্ট্রি স্ট্যান্ডার্ড।

৩. কিভাবে কোডে লিখতে হয় (স্টেপ বাই স্টেপ)

স্টেপ ১: যে ফাংশন থেকে await ব্যবহার করবেন, সেই ফাংশনের আগে async লিখুন।

স্টেপ ২: যেই জায়গায় “অপেক্ষা” করতে চান (যেমন fetch, res.json()), সেখানে সেই এক্সপ্রেশনের আগে await লিখুন।

স্টেপ ৩: এরর হ্যান্ডল করার জন্য পুরো অংশ try { ... } এর ভেতরে রাখুন এবং catch (err) { ... } দিয়ে এরর লগ বা মেসেজ দিন।

উদাহরণ ১ — সবচেয়ে বেসিক (কপি করে Console এ রান করুন)

এখানে দেখুন: async ফাংশন, await fetch(), await res.json(), এবং try/catch একসাথে কীভাবে ব্যবহার হচ্ছে।

async function loadUsers() {
  try {
    const res = await fetch('https://jsonplaceholder.typicode.com/users');
    if (!res.ok) throw new Error('Network error');
    const data = await res.json();
    console.log('Total users:', data.length);
    console.log('First user:', data[0]);
    return data;
  } catch (err) {
    console.error('Failed:', err.message);
  }
}
loadUsers();

উদাহরণ ২ — একের পর এক দুইটা API কল (সিরিয়াল)

প্রথম API থেকে ডেটা আসার পরই দ্বিতীয়টা কল হচ্ছে। কোড যেভাবে লিখেছেন সেভাবেই এক্সিকিউট হয়।

async function loadInOrder() {
  try {
    const usersRes = await fetch('https://jsonplaceholder.typicode.com/users');
    const users = await usersRes.json();
    console.log('Users loaded:', users.length);
    const postsRes = await fetch('https://jsonplaceholder.typicode.com/posts');
    const posts = await postsRes.json();
    console.log('Posts loaded:', posts.length);
    return { users, posts };
  } catch (e) {
    console.error(e.message);
  }
}
loadInOrder();

উদাহরণ ৩ — একসাথে অনেক API (Promise.all + await)

কয়েকটা API একসাথে কল করতে চাইলে Promise.all() ব্যবহার করুন। সব একসাথে রান হবে, মোট সময় = সবচেয়ে ধীরটার সময়।

async function loadDashboard() {
  try {
    const [usersRes, postsRes] = await Promise.all([
      fetch('https://jsonplaceholder.typicode.com/users'),
      fetch('https://jsonplaceholder.typicode.com/posts')
    ]);
    const users = await usersRes.json();
    const posts = await postsRes.json();
    console.log(users.length, posts.length);
    return { users, posts };
  } catch (e) {
    console.error(e.message);
  }
}
loadDashboard();

Error Handling (try / catch / throw)

নেটওয়ার্ক ফেইল বা বাজে ডেটা এড়াতে async কোড সবসময় try { ... } catch (err) { ... } দিয়ে wrap করুন। নিজে এরর দিতে throw new Error('message') ব্যবহার করুন।

try {
  const data = await fetch(url).then(r => r.json());
  if (!data.id) throw new Error('Invalid data');
} catch (err) {
  console.error(err.message);
}

সংক্ষেপে মনে রাখুন

  • async ফাংশন = ফাংশনটা একটা Promise রিটার্ন করে।
  • await = শুধু async ফাংশনের ভেতরে; Promise রেজলভ হওয়া পর্যন্ত অপেক্ষা করে।
  • এরর ধরতে: try { await ... } catch (err) { ... } ব্যবহার করুন।
  • বহু API একসাথে: await Promise.all([fetch(...), fetch(...)])

Performance Optimization

কিভাবে কোডে লিখতে হয়?

Debounce: একটা হেল্পার ফাংশন লিখুন যেটা setTimeoutclearTimeout দিয়ে “আগের টাইমার বাতিল + নতুন টাইমার” করে। সেই ফাংশন দিয়ে আপনার আসল হ্যান্ডলার wrap করুন (যেমন সার্চের জন্য)। Throttle: Date.now() দিয়ে শেষ কখন রান হয়েছিল দেখুন; যদি নির্দিষ্ট সময় (যেমন ২০০ms) পার হয়ে যায় তবেই ফাংশন রান করুন, নাহলে আটকে দিন।

কেন ব্যবহার ভালো?

সার্চ বক্সে প্রতি কী-স্ট্রোকে API কল করলে সার্ভার ও নেটওয়ার্ক চাপ পড়ে; debounce দিয়ে শুধু টাইপ থামার পর একবার কল হয়। স্ক্রল/রিসাইজের মতো ঘন ঘন ফায়ার হওয়া ইভেন্টে throttle দিলে নির্দিষ্ট সময়ে একবারই লজিক চলে, তাই UI হালকা থাকে।

Debounce Concept:

সার্চ বক্সে টাইপ করার সময় প্রতিটা অক্ষরের জন্য যদি API কল হয়, তাহলে সার্ভার ক্র্যাশ করতে পারে। Debounce এর কাজ হলো, ইউজার টাইপ করা থামানোর নির্দিষ্ট সময় (যেমন ৫০০ মিলিসেকেন্ড) পর ফাংশন রান করা। এর আগে আবার টাইপ করলে আগের টাইমার ক্যান্সেল হয়ে নতুন করে শুরু হবে।

function debounce(fn, delay) {
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn.apply(this, args), delay);
  };
}
const searchInput = document.getElementById('search');
const handleSearch = debounce((event) => {
  console.log('Searching for:', event.target.value);
}, 500);
searchInput.addEventListener('input', handleSearch);

Throttle Concept:

অবিরাম চলতে থাকা ইভেন্ট (যেমন: Page Scroll) এর ক্ষেত্রে আমরা চাই নির্দিষ্ট সময় পরপর একবার ফাংশনটি কাজ করুক। Throttle এটি নিশ্চিত করে।

function throttle(fn, limit) {
  let lastCall = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastCall >= limit) {
      lastCall = now;
      fn.apply(this, args);
    }
  };
}
window.addEventListener('scroll', throttle(() => {
  console.log('Scroll position:', window.scrollY);
}, 200));

Data Structures

কিভাবে ফাংশনে ব্যবহার করব?

Set: const s = new Set(arr) দিয়ে ইউনিক ভ্যালু; অ্যারে চাইলে [...s]Map: map.set(key, value), map.get(key)। ডুপ্লিকেট চেক, কাউন্ট, ক্যাশের মতো লুকআপের জন্য ফাংশনের ভেতরে ব্যবহার করুন। Stack/Queue: ক্লাস বানিয়ে push/pop বা enqueue/dequeue দিয়ে অ্যারে দিয়ে ইমপ্লিমেন্ট করুন।

কেন ব্যবহার ভালো?

অ্যারে থেকে ইউনিক নিতে বা “এটা আগে দেখেছি কি না” চেক করতে Set O(1) দেয়। Key দিয়ে ভ্যালু খোঁজা Map-এ সহজ ও ফাস্ট। Stack দিয়ে Undo/Back, Queue দিয়ে টাস্ক অর্ডার মেইনটেইন করা যায়। তাই ডেটা কীভাবে ব্যবহার করছেন সেটার উপর ভিত্তি করে সঠিক ডাটা স্ট্রাকচার বেছে নিলে কোড সহজ ও দ্রুত হয়।

Set (Unique Data)

Set এ কখনো ডুপ্লিকেট ডেটা থাকে না। Array থেকে ডুপ্লিকেট বাদ দেওয়ার সবচেয়ে ফাস্ট উপায় এটি।

const unique = [...new Set([1,2,2,3])];

Map (Key-Value Power)

Object এর Key হিসেবে শুধু String বসে, কিন্তু Map এ যেকোনো কিছু (এমনকি Object বা Array) Key হতে পারে।

const prices = new Map();
prices.set('apple', 120);
prices.set('orange', 80);
console.log(prices.get('apple'));

Stack (LIFO)

Last In, First Out. যেমন ব্রাউজারের ব্যাক বাটন বা Undo সিস্টেম। শেষে যা যোগ হয়েছে, সবার আগে সেটাই রিমুভ হবে।

class Stack {
  constructor() { this.items = []; }
  push(item) { this.items.push(item); }
  pop() { return this.items.pop(); }
}

Queue (FIFO)

First In, First Out. লাইনে দাঁড়ানোর মতো। যে আগে এসেছে সে আগে সার্ভিস পাবে। যেমন প্রিন্টার টাস্ক।

class Queue {
  constructor() { this.items = []; }
  enqueue(item) { this.items.push(item); }
  dequeue() { return this.items.shift(); }
}

Big O & Time Complexity

আপনার কোড ১০টি ডেটার জন্য ভালো চললেও ১ লক্ষ ডেটার জন্য ব্রাউজার ক্র্যাশ করতে পারে। এখানেই আসে Time Complexity.

কিভাবে কোডে চিন্তা করব?

লুপ একবার চলে = O(N)। লুপের ভেতর আরেক লুপ (প্রতি আইটেমের জন্য পুরো অ্যারে) = O(N²)। ইনডেক্স বা অবজেক্ট কী দিয়ে সরাসরি অ্যাক্সেস = O(1)। ফাংশন লিখার পর নিজে জিজ্ঞেস করুন: ডেটা দ্বিগুণ হলে স্টেপ কতগুণ বাড়বে? যত কম (১, log N, N) তত ভালো।

কেন ব্যবহার ভালো?

বড় ডেটা বা স্লো ইউজার এক্সপেরিয়েন্স এড়াতে অ্যালগরিদম বেছে নেওয়ার সময় Big O দিয়ে তুলনা করা যায়। নেস্টেড লুপ কমিয়ে Map/Set দিয়ে লুকআপ করলে O(N²) থেকে O(N) করা যায় — রিয়েল প্রজেক্টে এটাই পারফরম্যান্স বাঁচায়।

  • O(1)
    Constant Time: এটি সবচেয়ে ফাস্ট। ডিরেক্ট Array Index বা Object Key দিয়ে ডেটা খোঁজা। ডেটা যত খুশি হোক, টাইম সমান লাগবে।
  • O(N)
    Linear Time: ডেটা যত বাড়বে, সময় তত লাগবে। যেমন সাধারণ for loop বা Array.find()
  • O(N²)
    Quadratic Time: লুপের ভেতর লুপ (Nested Loops)। প্রো লেভেলে এটা সবসময় অ্যাভয়েড করতে হয়!
// O(N) উদাহরণ
function findItem(arr, target) {
  for (const item of arr) {
    if (item === target) return true;
  }
  return false;
}
// O(N²) উদাহরণ
function hasDuplicatePair(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) return true;
    }
  }
  return false;
}

Algorithms & Sorting

কিভাবে ফাংশনে লিখতে হয়?

Recursion: একটা Base Case রাখুন (যেখানে আর কল না করে রিটার্ন); তারপর নিজে নিজেকে ছোট ইনপুট দিয়ে কল করুন (যেমন factorial(n-1))। Binary Search: সর্টেড অ্যারে নিন; left, right, mid দিয়ে মাঝখান দিয়ে ভাগ করে টার্গেট বামে নাকি ডানে চেক করুন; লুপ বা রিকার্শন দিয়ে একদিকে সরতে থাকুন। Sort: প্র্যাকটিসের জন্য bubble/selection লিখুন; প্রজেক্টে arr.sort((a,b) => a - b) ব্যবহার করুন।

কেন ব্যবহার ভালো?

রিকার্শন ট্রি/নেস্টেড ডেটা বা গাণিতিক সংজ্ঞা (factorial, fibonacci) একদম সোজা করে। বাইনারি সার্চ সর্টেড ডেটায় O(log N) এ খোঁজ দেয়। সঠিক অ্যালগরিদম বেছে নিলে বড় ডেটায় অ্যাপ ফাস্ট থাকে।

Recursion

একটি ফাংশন যখন তার নিজের ভেতরে নিজেকেই কল করে। এটি ট্রি (Tree) ডাটা স্ট্রাকচার বা জটিল প্রবলেম সলভ করতে কাজে লাগে। তবে Base Case না দিলে ইনফিনিট লুপে পড়ে ব্রাউজার ক্র্যাশ করতে পারে।

function factorial(n) {
  if (n === 0) return 1; // Base case
  return n * factorial(n - 1); // Recursive call
}
console.log(factorial(5)); // 120

Binary Search

১০ লক্ষ ডেটা থেকে কোনো কিছু খুঁজতে Linear Search (for loop) নিলে ১০ লক্ষ বার চেক করতে হতে পারে (O(N))। কিন্তু ডেটাগুলো সাজানো (Sorted) থাকলে Binary Search দিয়ে মাঝখান থেকে ভাগ করে খুঁজলে মাত্র ২০ বারে ডেটা পাওয়া সম্ভব (O(log N))।

function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1;
}
console.log(binarySearch([1,3,5,7,9], 7)); // 3

Sorting Algorithms

Bubble Sort এবং Selection Sort বেসিক শেখার জন্য ভালো। কিন্তু রিয়েল লাইফে Array.sort() ব্যবহার করা হয়, যা সাধারণত Merge Sort বা Quick Sort এর মতো ফাস্ট অ্যালগরিদম ব্যবহার করে।

function bubbleSort(arr) {
  const copy = [...arr];
  for (let i = 0; i < copy.length; i++) {
    for (let j = 0; j < copy.length - 1 - i; j++) {
      if (copy[j] > copy[j + 1]) {
        [copy[j], copy[j + 1]] = [copy[j + 1], copy[j]];
      }
    }
  }
  return copy;
}

Problem Solving Patterns

LeetCode বা ইন্টারভিউ প্রবলেম সহজে সলভ করার জন্য কিছু নির্দিষ্ট প্যাটার্ন ব্যবহার করা হয়।

কিভাবে কোডে ব্যবহার করব?

Two Pointers: সর্টেড অ্যারে/স্ট্রিং-এ left = 0, right = length - 1 দিয়ে শুরু করুন; sum বা তুলনা করে হয় left++ নয় right-- করুন যতক্ষণ না কন্ডিশন মিলে বা ক্রস হয়। Sliding Window: একটা সাইজের (k) উইন্ডো নিয়ে প্রথম k আইটেমের যোগফল/মান নিন; তারপর এক ধাপ ডানে সরে আগেরটা বাদ দিয়ে নতুনটা যোগ করুন; ম্যাক্স/মিন আপডেট করুন।

কেন এই প্যাটার্ন ভালো?

Two Pointer দিয়ে পেয়ার সাম, প্যালিনড্রোম, টার্গেট সাম — নেস্টেড লুপ ছাড়াই O(N) এ সলভ করা যায়। Sliding Window দিয়ে “কটি পরপরের সর্বোচ্চ যোগফল” বা সাবস্ট্রিং প্রবলেম O(N) এ করা যায়। তাই প্যাটার্ন চিনে থাকলে কোড দ্রুত ও পরিষ্কার লিখতে পারবেন।

Two Pointers

Array এর দুই প্রান্ত থেকে (শুরু এবং শেষ) দুটি পয়েন্টার ধরে ডেটা চেক করা। Array সাজানো (Sorted) থাকলে খুব দ্রুত কোনো পেয়ার (Pair) খুঁজে পেতে সাহায্য করে।

// Sorted array থেকে pair sum খোঁজা
function pairSum(arr, target) {
  let left = 0, right = arr.length - 1;
  while (left < right) {
    const sum = arr[left] + arr[right];
    if (sum === target) return [arr[left], arr[right]];
    if (sum < target) left++;
    else right--;
  }
  return null;
}

Sliding Window

Array বা String এর একটি নির্দিষ্ট অংশের (Window) সাবসেট নিয়ে কাজ করা। যেমন: "একটি Array এর ৩টি পাশাপাশি সংখ্যার সর্বোচ্চ যোগফল বের করা"। এটি Nested Loop এর বদলে O(N) টাইম কমপ্লেক্সিটিতে সমাধান দেয়।

function maxSubArraySum(arr, k) {
  let max = 0, temp = 0;
  for (let i = 0; i < k; i++) {
    temp += arr[i];
  }
  max = temp;
  for (let i = k; i < arr.length; i++) {
    temp = temp - arr[i - k] + arr[i];
    max = Math.max(max, temp);
  }
  return max;
}

Advanced JS Architecture

ভবিষ্যতে কোথায় লাগবে

Closure: Debounce/Throttle, প্রাইভেট ভেরিয়েবল, React-এর হুকের পিছনের লজিক; ইন্টারভিউতে প্রায়ই জিজ্ঞাসা। this: ক্লাস, ইভেন্ট হ্যান্ডলার, ক্যালব্যাকে কনটেক্সট বোঝা; অ্যারো ফাংশন vs সাধারণ ফাংশন কখন ব্যবহার করবেন জানা জরুরি।

কিভাবে কোডে ব্যবহার করব?

Closure: একটা ফাংশনের ভেতরে আরেক ফাংশন রিটার্ন করুন; ইনার ফাংশন আউটারের কোনো ভেরিয়েবল ব্যবহার করলে সেটা “বন্ধনীতে” থেকে যাবে — প্রাইভেট কাউন্টার, ডিবাউন্সের টাইমার ইত্যাদি এভাবে বানান। this: অবজেক্টের মেথডে সাধারণ ফাংশন ব্যবহার করলে this সেই অবজেক্ট; অ্যারো ফাংশনে this উপরের স্কোপের। ইভেন্ট লিসেনারে কনটেক্সট ঠিক রাখতে অ্যারো ব্যবহার করুন বা .bind(this)

কেন ব্যবহার ভালো?

Closure দিয়ে ডেটা প্রাইভেট রাখা যায় এবং একই ফাংশন দিয়ে অনেকগুলো আলাদা স্টেট (কাউন্টার, ক্যাশ) ম্যানেজ করা যায়। this সঠিক বোঝা থাকলে ইভেন্ট হ্যান্ডলার, ক্লাস, ক্যালব্যাকে বাগ এড়ানো যায়।

Closures

যখন একটি ইনার ফাংশন তার আউটার ফাংশনের ভেরিয়েবলগুলোকে মনে রাখে (এমনকি আউটার ফাংশন এক্সিকিউট হয়ে যাওয়ার পরও), তখন তাকে Closure বলে। এটি ডেটা প্রাইভেসি (Private Variables) তৈরির জন্য খুব জনপ্রিয়।

function createCounter() {
  let count = 0; // private variable
  return function () {
    count++;
    console.log('Count:', count);
  };
}
const inc = createCounter();
inc(); // 1
inc(); // 2

The this Keyword

this মানে হলো "বর্তমান কনটেক্সট"। সাধারণ ফাংশনে এটি Window অবজেক্টকে নির্দেশ করে, কিন্তু Arrow Function এ এটি নিজস্ব this তৈরি করে না, বরং তার পেরেন্ট এর this কে ব্যবহার করে।

const person = {
  name: 'Rahim',
  regular() { console.log(this.name); },
  arrow: () => { console.log(this.name); }
};
person.regular(); // 'Rahim'
person.arrow();   // undefined (global this)

Code Splitting & Lazy Loading

সাইট লোড হওয়ার সময় পুরো সাইটের সব JS ফাইল একসাথে লোড না করে, শুধু যখন যে পেজ বা কম্পোনেন্ট দরকার, তখন সেই নির্দিষ্ট JS ফাইল লোড করা। এতে ওয়েবসাইটের Initial Load Time অনেক ফাস্ট হয়।

React / Vite / Next.js এর মতো ফ্রেমওয়ার্কগুলো আন্ডার দ্য হুড এই কনসেপ্টগুলো ব্যবহার করে।