مجموعهها و پیمایشگرها
Set و WeakSet، Map و WeakMap، متدهای آنها، WeakRef و FinalizationRegistry، Iterator و Generator، yield، Async Generator و پیمایش ناهمگام.
Set — مجموعه مقادیر یکتا
Set مجموعهای از مقادیر بدون تکرار است:
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log(set); // Set(3) {1, 2, 3}
console.log(set.size); // 3
// افزودن — تکراریها نادیده گرفته میشوند:
set.add(4);
set.add(4); // بیاثر
console.log(set.has(4)); // true
// حذف:
set.delete(1);
console.log(set.size); // 3
// حذف همه:
// set.clear();
کاربرد عملی Set — حذف تکراریها
const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4, 5]
// بررسی عضویت سریع — O(1):
const banned = new Set(["spam", "bad", "fake"]);
console.log(banned.has("spam")); // true — سریعتر از includes
Map — دیکشنری کلید-مقدار
Map بر خلاف Object هر نوعی را بهعنوان کلید میپذیرد:
const map = new Map();
// کلیدهای مختلف — حتی Object و NaN:
map.set("name", "علی");
map.set(42, "عدد");
map.set({ id: 1 }, "شیء");
map.set(NaN, "Not a Number");
console.log(map.size); // 4
console.log(map.get("name")); // علی
console.log(map.get(NaN)); // Not a Number — NaN هم کار میکند!
// زنجیرهای:
map.set("a", 1).set("b", 2);
// حذف:
map.delete(42);
console.log(map.has(42)); // false
پیمایش Map
const scores = new Map([
["علی", 18],
["سارا", 20],
["رضا", 16]
]);
// entries — پیشفرض:
for (const [name, score] of scores) {
console.log(name + ": " + score);
}
// فقط کلیدها یا مقادیر:
console.log([...scores.keys()]); // ["علی", "سارا", "رضا"]
console.log([...scores.values()]); // [18, 20, 16]
Map در برابر Object: Map ترتیب را حفظ میکند، اندازه (size) دارد، هر کلیدی میپذیرد و برای دادههای پویا بهینه است.
WeakMap — مراجع ضعیف
کلیدها باید شیء باشند و GC میتواند آنها را جمع کند:
const weakMap = new WeakMap();
let user = { name: "علی" };
weakMap.set(user, "داده اضافه");
console.log(weakMap.get(user)); // داده اضافه
// حالا اگر user حذف شود، داده هم خودکار آزاد میشود:
user = null; // — قابل بازیابی توسط GC — بدون نشتی حافظه!
WeakMap قابل پیمایش نیست و
size ندارد — فقط get/set/has/delete.WeakSet
const visited = new WeakSet();
let node1 = { id: 1 };
let node2 = { id: 2 };
// علامتگذاری بدون جلوگیری از GC:
visited.add(node1);
console.log(visited.has(node1)); // true
console.log(visited.has(node2)); // false
WeakRef و FinalizationRegistry
برای نگهداری ضعیف و callback پس از GC:
let target = { data: "مهم" };
// WeakRef — مرجع بدون جلوگیری از GC:
const ref = new WeakRef(target);
console.log(ref.deref()?.data); // مهم — هنوز زنده است
// FinalizationRegistry — اطلاع پس از جمعآوری:
const registry = new FinalizationRegistry(name => {
console.log(name + " جمعآوری شد");
});
registry.register(target, "target");
target = null; // پس از GC پیام چاپ میشود
Iterator — پیمایشگر
هر شیء قابل پیمایش (Iterable) متد Symbol.iterator دارد که یک Iterator برمیگرداند:
const numbers = [10, 20, 30];
const iterator = numbers[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
ساخت Iterable سفارشی
class NumberRange {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
const step = this.step;
return {
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const range = new NumberRange(1, 10, 3);
console.log([...range]); // [1, 4, 7, 10]
for (const n of range) console.log(n);
Generator Function — تابع مولد
با function* تعریف و با yield مقدار تولید میشود:
function* idGenerator() {
let id = 1;
while (true) { // حلقه بینهایت — بیخطر!
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// هر بار که لازم شد مقدار بعدی — بدون محاسبه همه!
yield — تولید و دریافت مقدار
function* conversation() {
const name = yield "اسمت چیه؟";
yield "سلام " + name + "!";
const mood = yield "چطوری؟";
yield "خوشحالم که " + mood + " هستی";
}
const talk = conversation();
console.log(talk.next().value); // اسمت چیه؟
console.log(talk.next("علی").value); // سلام علی!
console.log(talk.next().value); // چطوری؟
console.log(talk.next("خوب").value); // خوشحالم که خوب هستی
yield* — واگذاری به Generator دیگر
function* inner() {
yield 1;
yield 2;
}
function* outer() {
yield 0;
yield* inner(); // اجرای کامل inner
yield 3;
}
console.log([...outer()]); // [0, 1, 2, 3]
Generator برای دنباله بینهایت
function* fibonacci() {
let [a, b] = [0, 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); // 0 1 1 2 3 5 8 13 21 34
}
Async Iterator و Async Generator
// Async Generator — تولید مقادیر ناهمگام:
async function* fetchPages(baseUrl) {
let page = 1;
while (page <= 3) {
// شبیهسازی fetch:
const data = await new Promise(resolve =>
setTimeout(() => resolve("صفحه " + page++), 100)
);
yield data;
}
}
// for await...of — پیمایش ناهمگام:
(async () => {
for await (const page of fetchPages()) {
console.log(page); // صفحه 1، صفحه 2، صفحه 3
}
})();
جمعبندی
| ساختار | ویژگی | کاربرد |
|---|---|---|
Set | مقادیر یکتا | حذف تکراری، عضویت سریع |
Map | کلید هر نوع | دیکشنری، کش |
WeakMap | کلید شیء، ضعیف | متادیتا بدون نشتی |
Generator | تولید تنبل | دنبالهها، بینهایت |
Async Generator | تولید ناهمگام | صفحهبندی، استریم |
این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.