مقدمه
هر برنامهای که مینویسید با داده کار میکند — نام کاربر، تعداد مقالات، وضعیت لاگین، قیمت محصول. این دادهها باید جایی نگهداری شوند تا بتوانید از آنها استفاده کنید. در JavaScript، این «جا» را متغیر مینامیم.
متغیر شاید سادهترین مفهوم برنامهنویسی باشد، اما پایهای است که همه چیز دیگر روی آن بنا میشود — توابع، آبجکتها، کلاسها، همه با متغیر شروع میشوند. درک درست متغیر، یعنی درک رفتارهایی مثل Scope، Hoisting و Closure که بسیاری از باگهای JavaScript از آنها ناشی میشوند.
در این مقاله از صفر شروع میکنیم — از تعریف ساده تا مفاهیم پیشرفتهای که یک JavaScript developer حرفهای باید بداند.
متغیر چیست؟
متغیر یک نام است که به یک مقدار در حافظه اشاره میکند. مثل یک برچسب روی یک جعبه — برچسب نام دارد و داخل جعبه دادهای نگه میدارد.
// بدون متغیر — مشکلدار
console.log('رضا احمدی خوش آمدید');
console.log('رضا احمدی شما ۳ پیام دارید');
console.log('رضا احمدی از کدلوپ خارج شدید');
// اگر اسم تغییر کند، باید همه جا عوض کنید!
// با متغیر — درست ✅
const userName = 'رضا احمدی';
console.log(`${userName} خوش آمدید`);
console.log(`${userName} شما ۳ پیام دارید`);
console.log(`${userName} از کدلوپ خارج شدید`);
// فقط یک جا تغییر میدهیدمتغیر سه مزیت اصلی دارد: استفاده مجدد (یک بار تعریف، هر جا استفاده)، خوانایی (اسم معنادار به جای مقدار خام) و نگهداری (تغییر در یک جا تأثیر میگذارد همه جا).
تعریف متغیر — سه روش
JavaScript سه کلمه کلیدی برای تعریف متغیر دارد. هر کدام رفتار متفاوتی دارند:
// const — ثابت (توصیهشده، بیشترین استفاده)
const siteName = 'کدلوپ';
const maxArticles = 12;
const currentUser = { name: 'رضا', role: 'admin' };
// let — قابل تغییر (وقتی نیاز به reassign دارید)
let currentPage = 1;
let isLoading = false;
let searchQuery = '';
// var — قدیمی (از آن اجتناب کنید)
var oldStyle = 'این روش مشکلات دارد';قانون ساده: همیشه const، اگر نیاز به تغییر بود let، هرگز var. در ادامه دقیق توضیح میدهیم چرا.
نامگذاری متغیر — قوانین و بهترین روشها
نام متغیر باید این قوانین را رعایت کند:
// ✅ نامهای معتبر
let userName = 'رضا';
let _private = 'داخلی';
let $element = document.querySelector('h1');
let articleCount2 = 15;
let camelCaseName = 'این استاندارد است';
// ❌ نامهای غیرمعتبر
let 2fast = 'خطا'; // نمیتواند با عدد شروع شود
let my-variable = 'خطا'; // خط تیره مجاز نیست
let class = 'خطا'; // کلمه رزروشده JavaScript
let let = 'خطا'; // کلمه رزروشده// بهترین روشها — Naming Conventions
// camelCase برای متغیرها و توابع (استاندارد JS)
const firstName = 'رضا';
const articleReadingTime = 10;
function getUserName() { }
// PascalCase برای کلاسها و Component ها
class ArticleCard { }
function ArticleList() { } // React component
// UPPER_SNAKE_CASE برای ثابتهای پیکربندی
const API_URL = 'https://api.codeloop.ir';
const MAX_RETRY_COUNT = 3;
const DEFAULT_LANGUAGE = 'fa';
// _ پیشوند برای متغیرهای private (قرارداد)
class User {
#_password; // private field واقعی
_internalId; // قرارداد — private
}
// نامهای معنادار — مهمترین اصل
// ❌ بد
const d = new Date();
const n = 'رضا احمدی';
const arr = [1, 2, 3];
function fn(x) { return x * 2; }
// ✅ خوب
const publishDate = new Date();
const authorFullName = 'رضا احمدی';
const articleIds = [1, 2, 3];
function doublePrice(price) { return price * 2; }انواع داده (Data Types) در JavaScript
هر متغیر یک نوع داده دارد. JavaScript پویا (Dynamic) است — یعنی نوع در زمان اجرا مشخص میشود، نه هنگام تعریف:
// JavaScript هفت نوع داده Primitive دارد:
// 1. String — متن
const name = 'رضا احمدی';
const title = "آموزش JavaScript";
const template = `سلام ${name}!`; // Template Literal
const multiLine = `خط اول
خط دوم`;
// 2. Number — عدد (صحیح و اعشاری یکی هستند)
const age = 25;
const price = 490_000; // _ برای خوانایی (ES2021)
const pi = 3.14159;
const negative = -15;
const infinity = Infinity; // بینهایت
const notANumber = NaN; // نتیجه عملیات نامعتبر
// 3. Boolean — درست یا نادرست
const isLoggedIn = true;
const isPremium = false;
const hasAccess = age >= 18; // نتیجه مقایسه
// 4. null — عمداً خالی
const selectedArticle = null; // هنوز انتخاب نشده
const deletedUser = null; // حذف شده
// 5. undefined — هنوز مقدار ندارد
let score;
console.log(score); // undefined
function greet(name) {
console.log(name); // undefined اگر آرگومان نداده باشید
}
// 6. Symbol — شناسه یکتا (پیشرفته)
const id = Symbol('article-id');
const id2 = Symbol('article-id');
console.log(id === id2); // false — هر Symbol یکتا است
// 7. BigInt — اعداد بسیار بزرگ
const largeNumber = 9007199254740991n;
const bigInt = BigInt(9007199254740991);// Reference Types — آبجکت، آرایه، تابع
// Object
const user = {
name: 'رضا احمدی',
age: 25,
isPremium: true,
address: {
city: 'تهران',
country: 'ایران'
}
};
// Array
const courses = ['HTML', 'CSS', 'JavaScript', 'React'];
const scores = [95, 87, 92, 78];
const mixed = [1, 'سلام', true, null, { id: 1 }];
// Function
const greet = function(name) { return `سلام ${name}`; };
const double = (n) => n * 2; // Arrow Functiontypeof — تشخیص نوع داده
console.log(typeof 'سلام'); // 'string'
console.log(typeof 42); // 'number'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object' ← باگ تاریخی JavaScript!
console.log(typeof Symbol()); // 'symbol'
console.log(typeof 42n); // 'bigint'
console.log(typeof {}); // 'object'
console.log(typeof []); // 'object' ← آرایه هم object است
console.log(typeof function(){}); // 'function'
// چک کردن نوع در کد واقعی
function processInput(value) {
if (typeof value === 'string') {
return value.trim().toUpperCase();
}
if (typeof value === 'number') {
return value * 2;
}
if (Array.isArray(value)) { // برای آرایه از این استفاده کنید
return value.length;
}
return null;
}تفاوت Primitive و Reference
یکی از مهمترین مفاهیمی که باید بدانید — تفاوت نحوه ذخیرهسازی Primitive و Reference:
// Primitive — کپی میشود (Pass by Value)
let a = 10;
let b = a; // کپی مقدار
b = 20;
console.log(a); // 10 — تغییر نکرد ✅
let name1 = 'رضا';
let name2 = name1;
name2 = 'علی';
console.log(name1); // 'رضا' — تغییر نکرد ✅// Reference — اشاره میشود (Pass by Reference)
const user1 = { name: 'رضا', age: 25 };
const user2 = user1; // اشاره به همان آبجکت در حافظه!
user2.name = 'علی';
console.log(user1.name); // 'علی' — تغییر کرد! ⚠️
// چرا؟ چون هر دو به یک آبجکت در Heap اشاره میکنند
// ┌──────────┐ ┌─────────────────────────┐
// │ user1 │ ──► │ { name: 'علی', age: 25 }│
// │ user2 │ ──► │ (همان آبجکت) │
// └──────────┘ └─────────────────────────┘
// راهحل: کپی واقعی
const user3 = { ...user1 }; // Shallow Copy
const user4 = JSON.parse(JSON.stringify(user1)); // Deep Copy
const user5 = structuredClone(user1); // Deep Copy مدرن (ES2022)
user3.name = 'محمد';
console.log(user1.name); // 'علی' — تغییر نکرد ✅تبدیل نوع (Type Coercion)
JavaScript به صورت خودکار نوعها را تبدیل میکند — که گاهی غافلگیرکننده است:
// تبدیل ضمنی (Implicit Coercion) — خودکار
console.log('5' + 3); // '53' — عدد به رشته تبدیل شد!
console.log('5' - 3); // 2 — رشته به عدد تبدیل شد
console.log('5' * '3'); // 15 — هر دو به عدد
console.log(true + 1); // 2 — true به 1
console.log(false + 1); // 1 — false به 0
console.log(null + 1); // 1 — null به 0
console.log('' + 1); // '1' — خالی + عدد → رشته
// Falsy values — در شرط false محسوب میشوند
console.log(Boolean(0)); // false
console.log(Boolean('')); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(false)); // false
// Truthy — همه چیز دیگر true است
console.log(Boolean(1)); // true
console.log(Boolean('رضا')); // true
console.log(Boolean([])); // true ← آرایه خالی هم true!
console.log(Boolean({})); // true ← آبجکت خالی هم true!// تبدیل صریح (Explicit Conversion) — توصیهشده
const numStr = '42';
const num = Number(numStr); // 42
const num2 = parseInt(numStr, 10); // 42
const num3 = parseFloat('3.14'); // 3.14
const num4 = +numStr; // 42 (unary +)
const bool = Boolean(1); // true
const str = String(42); // '42'
const str2 = (42).toString(); // '42'
const str3 = (255).toString(16); // 'ff' (hex)
// مثال واقعی
const priceInput = document.querySelector('#price').value; // '490000' (string)
const price = Number(priceInput); // 490000 (number)
const discounted = price * 0.9; // 441000Scope — محدوده دسترسی به متغیر
Scope تعیین میکند یک متغیر از کجا قابل دسترسی است. این مفهوم برای جلوگیری از تداخل متغیرها و مدیریت حافظه اهمیت دارد:
Global Scope
// متغیرهای Global — از همه جا قابل دسترسی
const SITE_NAME = 'کدلوپ'; // در بالاترین سطح
function greetUser(name) {
console.log(`${SITE_NAME} خوش آمدید ${name}`); // ✅ دسترسی
}
if (true) {
console.log(SITE_NAME); // ✅ دسترسی
}
// ⚠️ هشدار: از Global Scope کمتر استفاده کنید
// متغیرهای Global باگهای پنهان ایجاد میکنندFunction Scope
function calculateDiscount(price) {
const discountRate = 0.2; // فقط داخل این تابع
const discount = price * discountRate;
return price - discount;
}
console.log(calculateDiscount(100000)); // 80000
// console.log(discountRate); // ❌ ReferenceError — خارج از تابعBlock Scope — ویژه let و const
// let و const به {} احترام میگذارند
if (true) {
const message = 'سلام'; // فقط داخل این if
let count = 0;
console.log(message); // ✅
}
// console.log(message); // ❌ ReferenceError
for (let i = 0; i < 3; i++) {
const item = `آیتم ${i}`;
console.log(item); // ✅
}
// console.log(i); // ❌ ReferenceError
// console.log(item); // ❌ ReferenceError
// مثال کاربردی
function getUserRole(user) {
if (user.isPremium) {
const badge = '⭐ Premium'; // فقط داخل این block
const expiryDate = new Date(user.premiumExpiry);
return `${badge} — تا ${expiryDate.toLocaleDateString('fa-IR')}`;
}
// badge و expiryDate اینجا وجود ندارند
return 'کاربر عادی';
}Lexical Scope و Scope Chain
// JavaScript از Lexical Scope استفاده میکند
// یعنی scope در زمان نوشتن کد تعیین میشود، نه اجرا
const siteName = 'کدلوپ'; // Global
function outer() {
const section = 'مقالات'; // Function scope
function inner() {
const articleTitle = 'JavaScript چیست؟'; // Function scope
// inner میتواند به همه scope های بالاتر دسترسی داشته باشد
console.log(siteName); // ✅ Global
console.log(section); // ✅ outer
console.log(articleTitle); // ✅ خودش
}
// outer نمیتواند به inner دسترسی داشته باشد
// console.log(articleTitle); // ❌
inner();
}
outer();
/* Scope Chain:
inner → outer → global
هر scope به بالا نگاه میکند تا متغیر را پیدا کند */Hoisting — بالا بردن تعریفها
JavaScript قبل از اجرا، تعریف متغیرها و توابع را به بالای scope میبرد — این رفتار را Hoisting مینامند:
// var — hoist میشود با مقدار undefined
console.log(score); // undefined — نه خطا!
var score = 100;
console.log(score); // 100
// JavaScript این را اینطور میبیند:
var score; // ← hoist
console.log(score); // undefined
score = 100;
console.log(score); // 100// let و const — hoist میشوند اما در TDZ (Temporal Dead Zone)
console.log(name); // ❌ ReferenceError: Cannot access 'name' before initialization
let name = 'رضا';
// TDZ یعنی: از ابتدای block تا خط تعریف، متغیر "وجود دارد" اما قابل دسترسی نیست
// این رفتار بهتر است — خطا فوری و واضح// Function Declaration — کاملاً hoist میشود
greet('رضا'); // ✅ کار میکند — قبل از تعریف!
function greet(name) {
console.log(`سلام ${name}`);
}
// Function Expression — مثل let رفتار میکند
// sayHello('رضا'); // ❌ ReferenceError
const sayHello = function(name) {
console.log(`سلام ${name}`);
};
sayHello('رضا'); // ✅ بعد از تعریفClosure — متغیر زنده
Closure یکی از قدرتمندترین مفاهیم JavaScript است — تابعی که به متغیرهای scope بیرونیاش دسترسی دارد، حتی بعد از اینکه آن scope بسته شده:
function createCounter(initialValue = 0) {
let count = initialValue; // این متغیر "بسته" میشود
return {
increment() { count++; },
decrement() { count--; },
reset() { count = initialValue; },
getCount() { return count; }
};
}
const counter = createCounter(10);
counter.increment();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 13
// count مستقیماً قابل دسترسی نیست
// console.log(count); // ❌ ReferenceError — encapsulation!// Closure در دنیای واقعی — مدیریت حالت
function createArticleManager(initialArticles) {
let articles = [...initialArticles]; // کپی داخلی
let currentPage = 1;
return {
getArticles() {
return articles.filter(a => a.status === 'published');
},
addArticle(article) {
articles = [...articles, { ...article, id: Date.now() }];
},
setPage(page) {
currentPage = page;
},
getCurrentPage() {
return currentPage;
}
};
}
const manager = createArticleManager([]);
manager.addArticle({ title: 'HTML چیست؟', status: 'published' });
manager.addArticle({ title: 'CSS چیست؟', status: 'published' });
console.log(manager.getArticles().length); // 2Destructuring — باز کردن متغیرها
یکی از محبوبترین قابلیتهای ES6 — استخراج مقادیر از آبجکت و آرایه:
// Object Destructuring
const article = {
title: 'JavaScript چیست؟',
slug: 'what-is-javascript',
author: 'رضا احمدی',
readingTime: 15,
tags: ['JavaScript', 'فرانتاند'],
};
// روش قدیمی
const title1 = article.title;
const author1 = article.author;
// Destructuring ✅
const { title, slug, author, readingTime } = article;
console.log(title); // 'JavaScript چیست؟'
console.log(author); // 'رضا احمدی'
// با نام جدید
const { title: articleTitle, author: writerName } = article;
// با مقدار پیشفرض
const { viewsCount = 0, likesCount = 0 } = article;
// تودرتو
const { author: { firstName, lastName } = {} } = article; // اگر author آبجکت باشد
// در پارامتر تابع
function renderArticle({ title, author, readingTime = 5 }) {
return `${title} — ${author} (${readingTime} دقیقه)`;
}
renderArticle(article);// Array Destructuring
const courses = ['HTML', 'CSS', 'JavaScript', 'React', 'Node.js'];
const [first, second, ...rest] = courses;
console.log(first); // 'HTML'
console.log(second); // 'CSS'
console.log(rest); // ['JavaScript', 'React', 'Node.js']
// Skip کردن عناصر
const [, , thirdCourse] = courses;
console.log(thirdCourse); // 'JavaScript'
// Swap — جابجایی بدون متغیر موقت
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
// از نتیجه تابع
function getCoordinates() {
return [35.6892, 51.3890];
}
const [lat, lng] = getCoordinates();Spread و Rest — گسترش و جمعآوری
// Spread Operator — گسترش دادن
const baseC {
apiUrl: 'https://api.codeloop.ir',
timeout: 5000,
retries: 3,
};
// کپی و گسترش آبجکت
const authC {
...baseConfig,
token: 'Bearer xyz',
timeout: 10000, // override
};
// ترکیب آرایهها
const fr ['HTML', 'CSS', 'JavaScript'];
const backendCourses = ['Node.js', 'MongoDB', 'Express'];
const allCourses = [...frontendCourses, ...backendCourses];
// کپی آرایه
const coursesCopy = [...frontendCourses];
// پاس دادن آرایه به تابع
const numbers = [5, 3, 8, 1, 9];
console.log(Math.max(...numbers)); // 9// Rest Parameter — جمعآوری آرگومانها
function createTag(tagName, ...classes) {
return `<${tagName} class="${classes.join(' ')}"></${tagName}>`;
}
console.log(createTag('div', 'card', 'featured', 'dark'));
// <div class="card featured dark"></div>
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15var، let، const — انتخاب درست
این یکی از مهمترین تصمیمهایی است که در هر خط کد میگیرید. برای آشنایی کامل با تمام تفاوتها، مقاله تفاوت var، let و const را بخوانید. اما خلاصه اینجاست:
// const — پیشفرض شما باشد
// برای هر چیزی که reassign نمیشود
const API_URL = 'https://api.codeloop.ir';
const user = await fetchUser(id); // آبجکت — مجاز
const articles = await fetchArticles(); // آرایه — مجاز
const btn = document.querySelector('#btn');
const handleClick = () => { /* ... */ };
// let — فقط وقتی نیاز به تغییر دارید
let currentPage = 1;
let isLoading = false;
let errorMessage = '';
for (let i = 0; i < 10; i++) { }
// var — هرگز در کد مدرن
// ❌ var x = 1; — مشکلات Scope و Hoisting داردمتغیرها در دنیای واقعی — پروژه کدلوپ
بریم ببینیم در یک پروژه واقعی چطور از متغیرها استفاده میشود:
// ─── پیکربندی — همیشه const ───
const C Object.freeze({
API_BASE: 'https://api.codeloop.ir',
ARTICLES_PER_PAGE: 12,
MAX_TITLE_LENGTH: 100,
SUPPORTED_LANGUAGES: ['fa', 'en'],
});
// ─── State مدیریت صفحه — let ───
let currentPage = 1;
let isLoading = false;
let hasError = false;
let activeCategory = 'all';
let searchQuery = '';
// ─── DOM Elements — const ───
const articlesGrid = document.querySelector('.articles-grid');
const paginati document.querySelector('.pagination');
const searchInput = document.querySelector('#search');
const categoryBtns = document.querySelectorAll('.category-btn');
const loadingSpinner = document.querySelector('.loading');
// ─── تابع اصلی ───
async function loadArticles() {
if (isLoading) return;
// تغییر state
isLoading = true;
hasError = false;
// نمایش loading
loadingSpinner.classList.remove('hidden');
articlesGrid.classList.add('opacity-50');
try {
// const برای دادههای یکبار مصرف
const params = new URLSearchParams({
page: currentPage,
limit: CONFIG.ARTICLES_PER_PAGE,
category: activeCategory,
search: searchQuery,
});
const resp await fetch(`${CONFIG.API_BASE}/articles?${params}`);
if (!response.ok) {
throw new Error(`خطای سرور: ${response.status}`);
}
const { articles, totalPages, totalCount } = await response.json();
// رندر
renderArticles(articles);
renderPagination(totalPages);
updateResultCount(totalCount);
} catch (error) {
hasError = true;
showError(error.message);
console.error('خطا در بارگذاری مقالات:', error);
} finally {
// همیشه اجرا میشود
isLoading = false;
loadingSpinner.classList.add('hidden');
articlesGrid.classList.remove('opacity-50');
}
}
// ─── Event Handlers ───
searchInput.addEventListener('input', (e) => {
// Destructuring از event
const { value } = e.target;
searchQuery = value.trim();
currentPage = 1; // reset به صفحه اول
loadArticles();
});
categoryBtns.forEach(btn => {
btn.addEventListener('click', () => {
const { category } = btn.dataset;
activeCategory = category;
currentPage = 1;
loadArticles();
});
});اشتباهات رایج با متغیرها
نامگذاری بیمعنا:
data،temp،x،arr— هیچچیز نمیگویند.userArticles،filteredPrices،activeCategoryIdبهتر هستند.تغییر آبجکتهای const بدون آگاهی:
const user = {}به معنای ثابت بودن reference است، نه محتوا. اگر میخواهید واقعاً ثابت باشد ازObject.freeze()استفاده کنید.متغیر Global تصادفی: فراموش کردن
constیاletباعث میشود متغیر Global شود — در strict mode خطا، وگرنه باگ پنهان.Shadow کردن متغیر: تعریف متغیر با همان نام در scope داخلی، متغیر بیرونی را میپوشاند — گاهی عمدی، اغلب اشتباه.
// ❌ اشتباه: متغیر Global تصادفی
function calculateTotal(prices) {
total = 0; // ← const/let فراموش شد!
// total به طور تصادفی Global شد
prices.forEach(p => total += p);
return total;
}
// ✅ درست
function calculateTotal(prices) {
let total = 0; // ← Block Scoped
prices.forEach(p => total += p);
return total;
}
// ❌ اشتباه: Shadow کردن ناخواسته
const user = { name: 'رضا' };
function updateUser(user) { // ← پارامتر اسمش user است — shadow!
console.log(user.name); // این پارامتر است، نه Global user
}
// ✅ درست: نام متفاوت
function updateUser(updatedUser) {
console.log(updatedUser.name);
}متغیرها زبان برنامهنویسی شما هستند — اسم خوب برای آنها انتخاب کنید. کدی که خودش را توضیح میدهد، نیاز به کامنت کمتری دارد. وقتی بعد از سه ماه به کد خودتان برمیگردید، نامهای معنادار بهترین هدیهای هستند که به آیندهتان میدهید. اگر میخواهید تفاوت دقیق var، let و const را بدانید، مقاله تخصصی آن را بخوانید.
نتیجهگیری
متغیر پایهایترین مفهوم JavaScript است — و همانطور که دیدید، عمق زیادی دارد. از تعریف ساده با const و let، تا Scope، Hoisting، Closure، Destructuring و Reference vs Primitive — همه اینها از مفهوم «ظرفی برای نگهداری داده» شروع میشوند.
چیزهایی که باید از این مقاله با خود ببرید:
همیشه
constاستفاده کنید، مگر نیاز به reassign داشته باشید.نام معنادار انتخاب کنید — کد برای انسان نوشته میشود.
Primitive کپی میشود، Reference اشاره میکند — این فرق مهم است.
Scope را بشناسید — متغیر را کوچکترین Scope ممکن تعریف کنید.

