Advance JavaScript শিখুন বাংলায় (পর্ব ৫) — Spread ও Rest Operator শিখুন

Spread ও Rest Operator

আগের পর্বে আমরা Destructuring শিখেছিলাম। এই পর্বে আমরা দেখব ... (তিনটা বিন্দু) — একই সিনট্যাক্স, কিন্তু দুটো সম্পূর্ণ বিপরীত কাজে ব্যবহার হয়: Spread (ছড়িয়ে দেওয়া) আর Rest (বাকিটা জড়ো করা)।

Spread বনাম Rest — পার্থক্য কোথায়?

দেখতে একই রকম হলেও এদের কাজ বিপরীত:

অপারেটর কাজ কোথায় ব্যবহার হয়
Spread একটা অ্যারে/অবজেক্টকে "ছড়িয়ে" আলাদা আলাদা মান বানায় ফাংশন কল, অ্যারে/অবজেক্ট লিটারেলের ভেতরে
Rest একাধিক আলাদা মানকে একটা অ্যারে/অবজেক্টে "জড়ো" করে ফাংশন প্যারামিটার, Destructuring-এর শেষে

Spread — Array-তে

একটা অ্যারের সব উপাদান আরেকটা অ্যারেতে "ছড়িয়ে" দেওয়া যায়:

const fruits = ["আম", "কাঁঠাল"];
const vegetables = ["আলু", "পটল"];

// দুটো অ্যারে মিলিয়ে নতুন একটা বানানো
const all = [...fruits, ...vegetables];
console.log(all); // ["আম", "কাঁঠাল", "আলু", "পটল"]

// অ্যারে কপি করা (আসল অ্যারে বদলে যাবে না)
const fruitsCopy = [...fruits];

// মাঝখানে নতুন আইটেম যোগ করা
const withNew = ["কলা", ...fruits, "লিচু"];
console.log(withNew); // ["কলা", "আম", "কাঁঠাল", "লিচু"]

Spread — Object-এ

অবজেক্টের প্রপার্টি মিলিয়ে নতুন অবজেক্ট বানানো, বা পুরনো অবজেক্ট থেকে কিছু প্রপার্টি পাল্টে নতুন অবজেক্ট বানানো (মূল অবজেক্ট না বদলে) খুব সাধারণ একটা প্যাটার্ন:

const user = { name: "আরিফ", age: 28 };

// একটা প্রপার্টি পাল্টে নতুন অবজেক্ট বানানো (মূল user অপরিবর্তিত থাকবে)
const updatedUser = { ...user, age: 29 };
console.log(updatedUser); // { name: "আরিফ", age: 29 }
console.log(user); // { name: "আরিফ", age: 28 } — এখনো আগেরটাই

// নতুন প্রপার্টি যোগ করা
const userWithRole = { ...user, role: "admin" };

// দুটো অবজেক্ট মার্জ করা (পরেরটা আগেরটার উপর override করে)
const defaults = { theme: "light", fontSize: 14 };
const customSettings = { theme: "dark" };
const finalSettings = { ...defaults, ...customSettings };
console.log(finalSettings); // { theme: "dark", fontSize: 14 }

গুরুত্বপূর্ণ প্যাটার্ন: { ...user, age: 29 } — এখানে মূল user অবজেক্ট সরাসরি না পাল্টে একটা নতুন অবজেক্ট বানানো হচ্ছে। এই "immutable update" প্যাটার্নটা React state আপডেট করার সময় সবচেয়ে বেশি ব্যবহার হয়।

Spread — ফাংশন কলে

একটা অ্যারেকে আলাদা আলাদা আর্গুমেন্ট হিসেবে পাঠাতে চাইলে:

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [10, 20, 30];

console.log(sum(...numbers)); // 60 — একই sum(10, 20, 30) হিসেবে কল হচ্ছে

// Math.max-এর সাথে খুব উপকারী, কারণ এটা অ্যারে সরাসরি নেয় না
console.log(Math.max(...numbers)); // 30

Rest — ফাংশন প্যারামিটারে

যখন কতগুলো আর্গুমেন্ট আসবে সেটা আগে থেকে জানা নেই, Rest দিয়ে সবগুলো একটা অ্যারেতে জড়ো করা যায়:

function sumAll(...numbers) {
  // numbers এখন একটা সাধারণ অ্যারে, যত ইচ্ছা আর্গুমেন্ট নিতে পারবে
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sumAll(1, 2, 3)); // 6
console.log(sumAll(1, 2, 3, 4, 5)); // 15
console.log(sumAll()); // 0

প্রথম কয়েকটা প্যারামিটার আলাদা রেখে বাকিগুলো Rest দিয়ে জড়ো করাও যায়:

function introduce(name, ...hobbies) {
  console.log(`${name}-এর শখ: ${hobbies.join(", ")}`);
}

introduce("আরিফ", "কোডিং", "লেখালেখি", "ফটোগ্রাফি");
// আরিফ-এর শখ: কোডিং, লেখালেখি, ফটোগ্রাফি

নিয়ম: Rest প্যারামিটার সবসময় সবার শেষে থাকতে হবে — function bad(...rest, name) লিখলে Syntax Error হবে।

Rest — Destructuring-এর সাথে

আগের পর্বে শেখা Destructuring-এর সাথে মিলিয়ে "বাকি সবকিছু" ধরে রাখা যায়:

// Array-তে
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;

console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]

// Object-এ
const user = { name: "আরিফ", age: 28, city: "ঢাকা", role: "admin" };
const { name, ...otherInfo } = user;

console.log(name); // আরিফ
console.log(otherInfo); // { age: 28, city: "ঢাকা", role: "admin" }

লাইভ ডেমো — Spread দিয়ে অবজেক্ট আপডেট

নিচের ফর্ম পূরণ করে বাটনে চাপো — মূল অবজেক্ট অপরিবর্তিত রেখে Spread দিয়ে একটা নতুন, আপডেট করা অবজেক্ট বানানো দেখো।

সতর্কতা — Spread শুধু "shallow copy" করে

Spread দিয়ে কপি করলে শুধু উপরের লেভেলের প্রপার্টিগুলো কপি হয় — ভেতরের nested অবজেক্ট/অ্যারে এখনো একই রেফারেন্স শেয়ার করে।

const original = {
  name: "আরিফ",
  address: { city: "ঢাকা" }
};

const copy = { ...original };
copy.address.city = "চট্টগ্রাম"; // ভেতরের অবজেক্ট পাল্টে দিচ্ছি

console.log(original.address.city); // "চট্টগ্রাম" — মূলটাও বদলে গেছে!
// কারণ copy.address আর original.address একই অবজেক্টকে রেফার করে

সমাধান: Nested অবজেক্টও আলাদা করে কপি করতে হবে ({ ...original, address: { ...original.address } }), অথবা structuredClone() ব্যবহার করতে হবে যদি সম্পূর্ণ deep copy দরকার হয়।

একটা সম্পূর্ণ উদাহরণ — To-do লিস্ট আইটেম টগল করা

চলো Spread ব্যবহার করে একটা to-do লিস্টে একটা আইটেমের "সম্পন্ন" স্ট্যাটাস টগল করি, বাকি আইটেম অপরিবর্তিত রেখে — এটা React-স্টাইল state আপডেটের একটা সাধারণ প্যাটার্ন।

const todos = [
  { id: 1, text: "বাজার করা", done: false },
  { id: 2, text: "কোড রিভিউ", done: false },
  { id: 3, text: "পোস্ট পাবলিশ", done: true }
];

function toggleTodo(todos, id) {
  return todos.map((todo) =>
    todo.id === id
      ? { ...todo, done: !todo.done } // এই আইটেমটা নতুন অবজেক্ট, done উল্টানো
      : todo // বাকিগুলো একইরকম থাকবে
  );
}

const updatedTodos = toggleTodo(todos, 2);
console.log(updatedTodos);
// [
//   { id: 1, text: "বাজার করা", done: false },
//   { id: 2, text: "কোড রিভিউ", done: true },  ← শুধু এটা পাল্টেছে
//   { id: 3, text: "পোস্ট পাবলিশ", done: true }
// ]
console.log(todos === updatedTodos); // false — সম্পূর্ণ নতুন অ্যারে
console.log(todos[0] === updatedTodos[0]); // true — অপরিবর্তিত আইটেম একই রেফারেন্স

এই প্যাটার্নটা গুরুত্বপূর্ণ কারণ এটা মূল todos অ্যারে না পাল্টে একটা নতুন অ্যারে রিটার্ন করছে — এই "immutability" ধারণাটা আধুনিক ফ্রেমওয়ার্ক (React, Vue) আর স্টেট ম্যানেজমেন্টে অত্যন্ত গুরুত্বপূর্ণ।

নিজে চেষ্টা করো

Exercise:

  1. দুটো অ্যারে মার্জ করে ডুপ্লিকেট মুছে ফেলো — হিন্ট: [...new Set([...arr1, ...arr2])]
  2. একটা ফাংশন লিখো যেটা যেকোনো সংখ্যক আর্গুমেন্ট নিয়ে সবচেয়ে বড়টা রিটার্ন করবে (Rest ব্যবহার করে)।
  3. উপরের toggleTodo ফাংশনটা নিজে লিখো, তারপর একটা deleteTodo(todos, id) ফাংশনও বানাও যেটা Spread/Rest ব্যবহার করে একটা আইটেম বাদ দিয়ে নতুন অ্যারে বানাবে (হিন্ট: filter ব্যবহার করা যায়)।
  4. একটা nested অবজেক্ট নিয়ে "shallow copy বাগ" নিজে তৈরি করো, তারপর সেটা ঠিক করার চেষ্টা করো।

পরের পর্বে যা থাকছে

পরের পর্বে আমরা শিখব Promise বিস্তারিতPromise.all, Promise.race, আর Promise.allSettled দিয়ে একাধিক অ্যাসিনক্রোনাস কাজ কীভাবে সামলানো যায়।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম