پروژهها و تمرینها
پروژههای مقدماتی، متوسط و پیشرفته، تمرینهای Syntax و شرط و حلقه و آرایه و Object و Function و DOM و Event و API و Async و پروژههای بازی و کاربردی.
چرا پروژه مهم است؟
دانش بدون تمرین فراموش میشود — پروژهها مفاهیم را تثبیت میکنند:
- اعمال همه مباحث در یک مجموعه
- رویارویی با چالشهای واقعی
- ساخت پورتفولیو برای کار
- یادگیری دیباگ و حل مسئله
نقشه راه یادگیری
مقدماتی ──── متوسط ──── پیشرفته
│ │ │
ماشین TODO PWA
حساب اپ با AI
ماشین بازی Chart
حساب Snake Library
پروژههای مقدماتی
سطح: مبانی تا آرایهها و توابع
۱. ماشینحساب ساده
// چالشها:
// - چهار عمل اصلی + درصد
// - دکمه C برای پاکسازی
// - اعمال اولویت عملگرها (ساده)
class Calculator {
#display = "0";
#operator = null;
#firstOperand = null;
inputDigit(d) {
this.#display = this.#display === "0"
? String(d)
: this.#display + d;
return this.#display;
}
setOperator(op) {
this.#firstOperand = parseFloat(this.#display);
this.#operator = op;
this.#display = "0";
}
calculate() {
const second = parseFloat(this.#display);
const ops = {
"+": (a, b) => a + b,
"-": (a, b) => a - b,
"*": (a, b) => a * b,
"/": (a, b) => b === 0 ? "خطا" : a / b
};
this.#display = String(
ops[this.#operator](this.#firstOperand, second)
);
return this.#display;
}
}
۲. تبدیل واحد
// تبدیل دما، ارز، طول
const converter = {
cToF: c => c * 9/5 + 32,
kmToMile: km => km * 0.621371,
kgToLb: kg => kg * 2.20462
};
۳. بازی حدس عدد
function guessingGame() {
const secret = Math.floor(Math.random() * 100) + 1;
let attempts = 0;
return function guess(n) {
attempts++;
if (n === secret) return `درست! در ${attempts} تلاش`;
return n < secret ? "بالاتر!" : "پایینتر!";
};
}
const game = guessingGame();
console.log(game(50)); // بالاتر!
۴. لیست کارها (نسخه ساده)
// با localStorage:
const todos = JSON.parse(localStorage.getItem("todos") || "[]");
function addTodo(text) {
todos.push({ text, done: false, id: Date.now() });
localStorage.setItem("todos", JSON.stringify(todos));
}
function toggleTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) todo.done = !todo.done;
localStorage.setItem("todos", JSON.stringify(todos));
}
پروژههای متوسط
سطح: DOM، رویداد، Async، API
۱. اپ آبوهوا
// با API واقعی:
class WeatherApp {
#apiKey;
#cache = new Map();
constructor(apiKey) {
this.#apiKey = apiKey;
this.#bindUI();
}
async getWeather(city) {
// کش ۱۰ دقیقهای:
if (this.#cache.has(city)) {
const { data, time } = this.#cache.get(city);
if (Date.now() - time < 600000) return data;
}
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${this.#apiKey}`;
const res = await fetch(url);
if (!res.ok) throw new Error("شهر پیدا نشد");
const data = await res.json();
this.#cache.set(city, { data, time: Date.now() });
return this.#transform(data);
}
#transform(raw) {
return {
temp: Math.round(raw.main.temp - 273.15),
desc: raw.weather[0].description,
icon: raw.weather[0].icon,
humidity: raw.main.humidity
};
}
#bindUI() {
document.querySelector("#search")
.addEventListener("submit", async (e) => {
e.preventDefault();
const city = e.target.city.value;
try {
const weather = await this.getWeather(city);
this.#render(weather);
} catch (err) {
this.#showError(err.message);
}
});
}
#render(w) { /* نمایش در DOM */ }
#showError(msg) { /* پیام خطا */ }
}
۲. بازی Snake
با Canvas — به مثال درس ۲۸ مراجعه کنید
۳. Quiz اپلیکیشن
class QuizApp {
#questions;
#current = 0;
#score = 0;
constructor(questions) {
this.#questions = questions;
}
getCurrent() {
return this.#questions[this.#current];
}
answer(choiceIndex) {
const correct = choiceIndex === this.getCurrent().correct;
if (correct) this.#score++;
this.#current++;
return { correct, finished: this.#isFinished() };
}
#isFinished() {
return this.#current >= this.#questions.length;
}
getResults() {
return {
score: this.#score,
total: this.#questions.length,
percent: Math.round(this.#score / this.#questions.length * 100)
};
}
}
۴. Markdown Previewer
// textarea → preview زنده:
const input = document.querySelector("#editor");
const preview = document.querySelector("#preview");
// پارسر ساده:
function parseMarkdown(md) {
return md
.replace(/^## (.+)$/gm, "<h2>$1</h2>")
.replace(/^# (.+)$/gm, "<h1>$1</h1>")
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
.replace(/\*(.+?)\*/g, "<em>$1</em>")
.replace(/`(.+?)`/g, "<code>$1</code>");
}
input.addEventListener("input", () => {
preview.innerHTML = parseMarkdown(input.value);
});
پروژههای پیشرفته
۱. PWA کامل
// Service Worker + Cache + Offline:
// manifest.json + sw.js + push notification
۲. کتابخانه نمودار
// Canvas API از صفر:
class Chart {
constructor(canvas, options) {
this.ctx = canvas.getContext("2d");
this.options = options;
this.#setup();
}
#setup() {
// devicePixelRatio برای وضوح:
const dpr = window.devicePixelRatio || 1;
const rect = this.ctx.canvas.getBoundingClientRect();
this.ctx.canvas.width = rect.width * dpr;
this.ctx.canvas.height = rect.height * dpr;
this.ctx.scale(dpr, dpr);
}
draw(data) {
this.#drawGrid();
this.#drawAxes();
this.#drawData(data);
}
#drawData(data) {
// رسم خط با bezier:
this.ctx.beginPath();
data.forEach((point, i) => {
const { x, y } = this.#scale(point);
i === 0 ? this.ctx.moveTo(x, y) : this.ctx.lineTo(x, y);
});
this.ctx.stroke();
}
#scale(point) { /* تبدیل داده به مختصات */ }
#drawGrid() { /* خطوط راهنما */ }
#drawAxes() { /* محورها */ }
}
۳. Chat Application
// WebSocket + مدیریت وضعیت:
class ChatStore {
#messages = [];
#subscribers = new Set();
#socket;
constructor(url) {
this.#socket = new WebSocket(url);
this.#socket.onmessage = (e) => {
const msg = JSON.parse(e.data);
this.#addMessage(msg);
};
}
send(text) {
this.#socket.send(JSON.stringify({ text, time: Date.now() }));
}
#addMessage(msg) {
this.#messages.push(msg);
this.#notify();
}
subscribe(fn) {
this.#subscribers.add(fn);
return () => this.#subscribers.delete(fn);
}
#notify() {
this.#subscribers.forEach(fn => fn([...this.#messages]));
}
}
تمرینهای Syntax
// ۱. خروجی چیست؟
console.log(typeof null); // "object"!
console.log(0.1 + 0.2 === 0.3); // false!
console.log([] + []); // ""
console.log("5" + 3, "5" - 3); // "53", 2
// ۲. این کد را اصلاح کن:
function scopeTest() {
if (true) {
var x = 1;
let y = 2;
}
console.log(x); // 1 — hoisted
// console.log(y); // خطا!
}
// ۳. نتیجه؟
const [a, ...rest] = [1, 2, 3, 4];
console.log(a, rest); // 1 [2,3,4]
تمرینهای شرط و حلقه
// ۱. FizzBuzz کلاسیک:
function fizzBuzz(n) {
const result = [];
for (let i = 1; i <= n; i++) {
let out = "";
if (i % 3 === 0) out += "Fizz";
if (i % 5 === 0) out += "Buzz";
result.push(out || i);
}
return result;
}
// ۲. مثلث ستارهای:
function pyramid(h) {
for (let i = 1; i <= h; i++) {
console.log(" ".repeat(h - i) + "*".repeat(2*i - 1));
}
}
// ۳. جستوجو در دنباله:
function isArmstrong(n) {
const digits = String(n).split("");
const sum = digits.reduce((a, d) => a + d ** digits.length, 0);
return sum === n;
}
console.log(isArmstrong(153)); // true
تمرینهای آرایه
// ۱. بدون متد آماده chunk بنویس:
function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
console.log(chunk([1,2,3,4,5], 2)); // [[1,2],[3,4],[5]]
// ۲. شمارش تکرار:
function countBy(arr) {
return arr.reduce((acc, item) => {
acc[item] = (acc[item] || 0) + 1;
return acc;
}, {});
}
console.log(countBy(["a","b","a"])); // {a:2, b:1}
// ۳. چک کردن تو در تو:
const hasNested = arr => arr.some(Array.isArray);
// ۴. یکسانسازی سطح:
const flatten = arr => arr.flat(Infinity);
تمرینهای Object
// ۱. مقایسه عمیق:
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== "object" || typeof b !== "object") return false;
if (Object.keys(a).length !== Object.keys(b).length) return false;
return Object.keys(a).every(key => deepEqual(a[key], b[key]));
}
// ۲. گرفتن مسیر:
function getByPath(obj, path) {
return path.split(".").reduce((o, key) => o?.[key], obj);
}
console.log(getByPath({a:{b:{c:42}}}, "a.b.c")); // 42
تمرینهای Function
// ۱. Curry کردن:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...more) => curried.apply(this, [...args, ...more]);
};
}
const add3 = curry((a, b, c) => a + b + c);
console.log(add3(1)(2)(3)); // 6
console.log(add3(1, 2)(3)); // 6
// ۲. Memoize:
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
تمرینهای DOM و Event
// ۱. Modal بدون کتابخانه:
class Modal {
#el;
constructor() {
this.#el = document.createElement("div");
this.#el.className = "modal";
this.#el.innerHTML = `
<div class="modal-content">
<span class="close">×</span>
<div class="modal-body"></div>
</div>`;
document.body.appendChild(this.#el);
this.#el.querySelector(".close")
.addEventListener("click", () => this.close());
}
open(content) {
this.#el.querySelector(".modal-body").innerHTML = content;
this.#el.classList.add("visible");
}
close() {
this.#el.classList.remove("visible");
}
}
تمرینهای API و Async
// ۱. درخواست موازی با محدودیت:
async function limitedParallel(tasks, limit = 3) {
const results = [];
let executing = [];
for (const task of tasks) {
const p = task().then(result => {
executing = executing.filter(e => e !== p);
return result;
});
executing.push(p);
results.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
پروژههای بازی
| بازی | سطح | مفاهیم |
|---|---|---|
| Tic-Tac-Toe | مقدماتی | منطق، آرایه دوبعدی |
| Memory Match | مقدماتی | DOM، رویداد، تایمر |
| Snake | متوسط | Canvas، حلقه بازی |
| Tetris | پیشرفته | الگوریتم، انیمیشن |
| Flappy Bird | متوسط | فیزیک ساده، rAF |
سطحبندی پروژهها
| سطح | زمان | فناوری |
|---|---|---|
| مقدماتی | ۱-۳ روز | JS خالص |
| متوسط | ۱-۲ هفته | + API، localStorage |
| پیشرفته | ۱ ماه+ | + PWA، Worker، WebGL |
نکات موفقیت پروژه
- کوچک شروع کن — MVP اول!
- Git از روز اول
- هر روز کمی — تداوم مهمتر از شدت
- کد را نشان بده — بازخورد بگیر
- README بنویس — مستندسازی
این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.