| کانال توسعه‌دهندگان جاوااسکریپت |
3.92K subscribers
53 photos
3 videos
36 links
⭕️ کانال توسعه‌دهندگان جاوااسکریپت دولوپیکس

💠 دولوپیکس | جامعه توسعه‌دهندگان ایرانی

💎 @Developix
🚀 Developix.ir

📌 پشتیبانی و تبلیغات:
@DevelopixSupport
Download Telegram
🎯 هندل‌کردن کلیک‌ها با Event Delegation

وقتی تعداد زیادی دکمه یا آیتم در DOM داریم، اضافه‌کردن یک event listener روی هر عنصر هم کند است، هم کد را شلوغ می‌کند. با Event Delegation فقط روی والد گوش می‌دهیم و رویداد را از مسیر event.target مدیریت می‌کنیم. 🔥

این کار:
• تعداد listenerها را کم می‌کند ✂️
• مدیریت عناصر اضافه/حذف‌شونده را ساده می‌کند

مثال ساده روی یک لیست:

const list = document.querySelector('#user-list');

list.addEventListener('click', (event) => {
const item = event.target.closest('li');
if (!item || !list.contains(item)) return;

const userId = item.dataset.userId;
console.log('Clicked user:', userId);
});


حالا هر <li data-user-id="123"> که بعداً با JavaScript به #user-list اضافه شود، بدون نیاز به listener جدید، کلیک را هندل می‌کند. 🚀

برای جزئیات بیشتر: MDN - Event delegation

توی پروژه‌های سنگین DOM، این الگو هم خوانایی کد را بهتر می‌کند هم عملکرد را. 😉

🔖 #Javascript #JS #جاوااسکریپت #JavaScript #Event_Delegation #DOM #Performance #ES6

👤 Developix

💎 Channel: @DevelopixJavascript
👍51
در این نمونه‌کد، برای هر درخواست ورودی چندین بار JSON.parse روی req.body انجام می‌شود که می‌تواند در درخواست‌های سنگین باعث کاهش کارایی، افزایش مصرف CPU و حتی بروز خطا در صورت خراب شدن ساختار بدنه شود. در مقابل، در نسخه اصلاح‌شده با استفاده از یک middleware اختصاصی، فقط یک بار پارس انجام شده و نتیجه در یک فیلد امن روی req ذخیره می‌شود.

به‌نظر شما در پروژه‌های واقعی، استفاده از این الگو چه مزیت‌هایی از نظر performance، قابلیت تست و کاهش ریسک Side Effect نسبت به پارس مستقیم JSON در هر هندلر دارد؟

🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
👍2
خروجی این کد JavaScript چیست؟

به ترتیب و با دقت به event loop و صف microtask و macrotask فکر کنید و رشته‌ای را که در کنسول چاپ می‌شود، دقیقا به همان ترتیب بنویسید.

const p = Promise.resolve(1);

p.then(v => {
console.log("A", v);
return v + 1;
}).then(v => {
console.log("B", v);
return Promise.resolve(v + 1);
}).then(v => {
console.log("C", v);
});

setTimeout(() => {
console.log("D");
Promise.resolve().then(() => {
console.log("E");
});
}, 0);

console.log("F");


خروجی نهایی کنسول را دقیقا و به ترتیب، در یک خط یا چند خط، بنویسید.

🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
در کد زیر سازنده‌ی کلاس تلاش می‌کند با استفاده از async/await و forEach داده‌ها را به‌صورت غیرهم‌زمان لود کند، اما رفتار نهایی با انتظار نویسنده‌ی کد فرق دارد و this.data در زمان استفاده هنوز کامل مقداردهی نشده است.

سؤال: در این کد چه چیزی باید تغییر کند تا اجرای غیرهم‌زمان به‌درستی منتظر اتمام تمام درخواست‌ها بماند و سپس this.data کامل آماده باشد؟

class DataLoader {
constructor(apiUrls) {
this.data = [];
this.load(apiUrls);
}

async load(urls) {
urls.forEach(async (url) => {
const res = await fetch(url);
const json = await res.json();
this.data.push(json);
});
}
}

const loader = new DataLoader([
"https://example.com/api/users",
"https://example.com/api/posts"
]);

// بعداً در جایی دیگر:
console.log("Loaded:", loader.data.length); // مقدار اینجا غیرقابل اعتماد است


🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
کتابخونه 🐻 Zustand یه state manager مینیمال برای React هست که بدون ceremony اضافی، state سراسری رو برات حل می‌کنه.

مزیتش نسبت به Redux و بقیه چیه؟
• API خیلی ساده و مستقیم
• بدون providerهای تو در تو؛ فقط یک store
• performance عالی با رندر کم‌تر کامپوننت‌ها
• پشتیبانی از TypeScript بدون دردسر

یه مثال ساده:
npm install zustand


import { create } from 'zustand'

const useBearStore = create((set) => ({
bears: 0,
increase: () => set((state) => ({ bears: state.bears + 1 })),
}))

function Counter() {
const bears = useBearStore((state) => state.bears)
const increase = useBearStore((state) => state.increase)

return <button onClick={increase}>Bears: {bears}</button>
}


برای پروژه‌هایی که Redux براشون زیادی سنگینه، Zustand گزینه‌ایه که هم کد رو تمیز نگه می‌داره هم مقیاس‌پذیره. مخصوصاً برای dashboardها، panelهای ادمین و appهای data-heavy خیلی جواب می‌ده.

مستندات و سورس:
GitHub
Docs

یک بار تو یه پروژه واقعی تستش کن، معمولاً بعدش برگردوندنش به عقب سخته 😄

🔖 #Javascript #JS #جاوااسکریپت #JavaScript #React #Zustand #State_Management #Frontend

👤 Developix

💎 Channel: @DevelopixJavascript
👍4
در نمونه‌کد بالا برای فیلتر کردن یک آرایه بزرگ، در هر بار تکرار حلقه دوباره sort و filter روی کل داده‌ها اجرا شده که هم باعث پیچیدگی زمانی بسیار بالا (حدود O(n² log n)) و هم مصرف اضافی مموری می‌شود. در نسخهٔ بهبود‌یافته، ابتدا داده‌ها یک‌بار نرمال‌سازی و مرتب می‌شوند و سپس فقط یک‌بار روی آن‌ها فیلتر و جست‌وجو انجام می‌گیرد.

به نظر شما چرا استفاده از متغیرهای میانی و جلوگیری از ایجاد آرایه‌های موقت متعدد می‌تواند در این سناریو باعث کاهش مصرف مموری و افزایش سرعت برنامه، مخصوصاً روی لیست‌های بزرگ (مثلاً > ۱۰۰هزار آیتم)، شود؟

🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
🔥3
جلوگیری از اجرای اضافی event با debounce ساده

تو پروژه‌های فرانت، eventهایی مثل scroll و input می‌تونن تو هر ثانیه ده‌ها بار فایر بشن و باعث لگ، مصرف CPU و رندر اضافه بشن. یک تکنیک ساده برای کنترل این ماجرا، استفاده از debounce هست. 😉

ایده: تا وقتی کاربر داره پشت‌سرهم تایپ یا اسکرول می‌کنه، تابع اصلی اجرا نشه؛ فقط وقتی یک مدت کوتاه هیچ eventی نیومد، تابع رو صدا بزن.

function debounce(fn, delay) {
let timeoutId;
return function (...args) {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}

const handleSearch = debounce((event) => {
console.log("Search:", event.target.value);
}, 400);

const input = document.querySelector("#search");
input.addEventListener("input", handleSearch);


💡 این کار تعداد requestهای سرچ، رندر مجدد و لاگ‌ها رو به‌شدت کم می‌کنه و UX نرم‌تری می‌سازه.

مرجع بیشتر در مورد timing ها و eventها:
MDN - setTimeout

امتحانش روی inputهای سرچ یا resize و scroll نتیجه خیلی ملموسی می‌دهد. 🚀

🔖 #Javascript #JS #جاوااسکریپت #JavaScript #debounce #performance #events #frontend

👤 Developix

💎 Channel: @DevelopixJavascript
👍51
خروجی این کد JavaScript چیست؟

در این مثال از prototype chain و shadowing روی یک property استفاده شده است. با دقت به ترتیب اجرا و این‌که هر بار چه مقداری log می‌شود توجه کنید و خروجی نهایی را به ترتیب سطرها بنویسید.

const base = {
value: 1,
inc() {
this.value++;
console.log("base:", this.value);
}
};

const obj = Object.create(base);
obj.value = 10;

obj.inc();
base.inc();

delete obj.value;
obj.inc();

base.value = 100;
obj.inc();


خروجی دقیق این کد در console چه خواهد بود؟ (همه سطرهای خروجی را به ترتیب بنویسید)

🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
3
ساخت TODO List یکی از بهترین تمرین‌های واقعی برای جا افتادن مفاهیم DOM، event ها و ذخیره داده‌ها در مرورگر است. این بار بدون React و فریم‌ورک، فقط با Vanilla JS و LocalStorage 💪

منبع اصلی ایده و توضیحات: آموزش MDN درباره Web Storage API و مستندات DOM Manipulation.

ایده کلی 👇
یک فرم ساده داریم برای اضافه‌کردن task ها، لیست را با JS توی DOM رندر می‌کنیم و هر تغییری که کاربر می‌دهد را در localStorage ذخیره می‌کنیم تا بعد از refresh هم از بین نرود.

نکته خوبش این است که با همین مثال ساده، چند تا چیز مهم جا می‌افتد:

• کار با querySelector و ساخت element های جدید
• مدیریت event ها مثل submit و click
• تبدیل array به string و برعکس با JSON.stringify و JSON.parse
• sync نگه‌داشتن UI با state ذخیره‌شده در localStorage

یک اسکلت ساده از کد 👇

<!-- index.html -->
<form id="todo-form">
<input id="todo-input" placeholder="کار جدید..." />
<button type="submit">اضافه کن</button>
</form>

<ul id="todo-list"></ul>

<script src="app.js"></script>


و فایل جاوااسکریپت:

// app.js
const form = document.querySelector("#todo-form");
const input = document.querySelector("#todo-input");
const list = document.querySelector("#todo-list");

const STORAGE_KEY = "jsdev_todos";
let todos = loadTodos();
render();

form.addEventListener("submit", (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;

todos.push({ id: Date.now(), text });
input.value = "";
saveTodos();
render();
});

list.addEventListener("click", (e) => {
if (!e.target.matches("button[data-id]")) return;
const id = Number(e.target.dataset.id);
todos = todos.filter((t) => t.id !== id);
saveTodos();
render();
});

function loadTodos() {
const raw = localStorage.getItem(STORAGE_KEY);
try {
return raw ? JSON.parse(raw) : [];
} catch {
return [];
}
}

function saveTodos() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
}

function render() {
list.innerHTML = "";
todos.forEach((todo) => {
const li = document.createElement("li");
li.textContent = todo.text + " ";

const btn = document.createElement("button");
btn.textContent = "حذف";
btn.dataset.id = todo.id;

li.appendChild(btn);
list.appendChild(li);
});
}


چند نکته ریز ولی مهم 🧠
• همیشه روی داده localStorage یک try...catch بگذار که اگر خراب شد (مثلاً دستی ویرایش شده بود) اپ کرش نکند.
• از یک key ثابت مثل jsdev_todos استفاده کن تا اگر بعداً چند بخش دیگر هم اضافه شد، تداخل نداشته باشند.
• برای id ها اینجا از Date.now() استفاده شده، که برای پروژه‌های کوچک کافی است.

با همین ساختار پایه می‌شود بعدها checkbox برای completed، فیلتر، و حتی sync ساده با backend هم اضافه کرد. یک بار این نمونه را تا انتها بساز، بعد هر جای اپ‌های واقعی که state و UI sync می‌شوند خیلی قابل فهم‌تر به نظر می‌رسد ⚡️

اگر ساختی و کمی شخصی‌سازی‌اش کردی، نسخه‌ات را برای بقیه بچه‌های جاوااسکریپت هم بفرست؛ همین مثال‌های کوچک کم‌کم تبدیل به component و pattern های جدی‌تر می‌شوند.

🔖 #Javascript #JS #جاوااسکریپت #JavaScript #LocalStorage #DOM #VanillaJS #Frontend

👤 Developix

💎 Channel: @DevelopixJavascript
6👍2
در کد زیر یک اشتباه رایج در استفاده از async/await داخل حلقه دیده می‌شود که باعث اجرای سریالی درخواست‌ها و کند شدن شدید برنامه در مواجهه با تعداد آیتم‌های زیاد می‌شود. در نسخه‌ی اصلاح‌شده با استفاده از Promise.all و تبدیل حلقه به مجموعه‌ای از Promiseها، همه‌ی درخواست‌ها به صورت موازی اجرا می‌شوند و در سناریوهایی مثل فراخوانی چندین API مستقل، زمان پاسخ برنامه به شکل چشم‌گیری کاهش پیدا می‌کند.

به نمونه‌کد بالا دقت کنید: در چه شرایطی استفاده از Promise.all به جای await داخل حلقه می‌تواند بهترین انتخاب برای بهبود Performance و کاهش زمان اجرای کل عملیات باشد؟

🔖 #Javascript #JS #جاوااسکریپت

👤 Developix

💎 Channel: @DevelopixJavascript
🔥 پروژه: ‏DrawDB؛ طراحی بصری دیتابیس آنلاین

ویرایشگر تحت‌وب DrawDB یک ابزار رایگان برای طراحی نمودار دیتابیس و تولید خودکار SQL است 🧩

برای وقتی عالی است که قبل از نوشتن کد، اسکیمای دیتابیس پروژه جاوااسکریپتی‌تان (مثلاً با ORMهایی مثل Prisma یا TypeORM) را می‌خواهید شفاف و تمیز طراحی کنید.

رابط کاربری خیلی ساده است: جدول می‌سازی، ستون و رابطه می‌کشی و درنهایت اسکریپت SQL ساخت جداول را تحویل می‌گیری.

برای دیدن پروژه و دمو، سر بزن به: سورس DrawDB روی گیت‌هاب

⭐️ 665 stars this week

🔖 #Javascript #JS #جاوااسکریپت #جاوااسکریپت #طراحی_دیتابیس

💎 @DevelopixJavascript
5
اصل مهمی در Clean Code این است که هر تابع فقط یک وظیفهٔ واضح داشته باشد

وقتی یک تابع چند کار مختلف انجام می‌دهد:
- تست‌نویسی سخت می‌شود
- احتمال باگ بالا می‌رود
- خواندن و تغییر کد زمان‌بر می‌شود

به این ایده در Design Principleها می‌گویند: Single Responsibility Principle (SRP).

نمونهٔ یک تابع شلوغ در Node.js 👇
const registerUser = async (req, res) => {
const { email, password } = req.body;

// validate
if (!email || !password || password.length < 8) {
return res.status(400).json({ message: 'Invalid data' });
}

// check duplicate
const existing = await User.findOne({ email });
if (existing) {
return res.status(409).json({ message: 'Email exists' });
}

// hash password
const bcrypt = await import('bcrypt');
const hashed = await bcrypt.hash(password, 10);

// save user
const user = await User.create({ email, password: hashed });

// send welcome email
await sendWelcomeEmail(email);

return res.status(201).json({ id: user.id, email: user.email });
};

این تابع هم اعتبارسنجی می‌کند، هم دیتابیس می‌زند، هم ایمیل می‌فرستد؛ یعنی چند مسئولیت در یک جا.

حالا همان منطق را با توابع کوچک‌تر تمیزتر می‌کنیم 🧼
const validateRegisterInput = ({ email, password }) => {
if (!email || !password) return 'Email and password are required';
if (password.length < 8) return 'Password too short';
return null;
};

const ensureEmailUnique = async (email) => {
const existing = await User.findOne({ email });
if (existing) throw new Error('EMAIL_TAKEN');
};

const createUser = async ({ email, password }) => {
const bcrypt = await import('bcrypt');
const hashed = await bcrypt.hash(password, 10);
return User.create({ email, password: hashed });
};

const registerUser = async (req, res) => {
const { email, password } = req.body;

const error = validateRegisterInput({ email, password });
if (error) return res.status(400).json({ message: error });

try {
await ensureEmailUnique(email);
const user = await createUser({ email, password });
await sendWelcomeEmail(email);

return res.status(201).json({ id: user.id, email: user.email });
} catch (err) {
if (err.message === 'EMAIL_TAKEN') {
return res.status(409).json({ message: 'Email exists' });
}
return res.status(500).json({ message: 'Server error' });
}
};

حالا:
- تست کردن validateRegisterInput و createUser مستقل و ساده است
- تغییر قانون پسورد، فقط در یک تابع انجام می‌شود
- خطاها واضح‌تر و قابل مدیریت‌تر هستند

برای کدهای React، Express، Next.js و هر محیط JavaScript/TypeScript دیگر همین ایده جواب می‌دهد: توابع کوچک، با نام خوب و یک کار مشخص.

برای مطالعهٔ بیشتر می‌توان از Refactoring.Guru و MDN کمک گرفت.

قدم‌های کوچک refactor کردن همین امروز روی یکی از فانکشن‌های شلوغ پروژه، بعد از چند هفته تاثیر زیادی در خوانایی و کیفیت کد می‌گذارد 🚀

🔖 #Javascript #JS #جاوااسکریپت #JavaScript #Clean_Code #Node_js #Refactoring #SRP

👤 Developix

💎 Channel: @DevelopixJavascript
👍61