مقدمه

برای آشنایی با هر زبان برنامه‌نویسی، اولین گام درک چند مفهوم و ویژگی پایه‌ای است که عبارتند از: ویژگی‌های گرامری (syntax features)، سیستم نوع (type system)، ساختارهای کنترلی (control flow) و توابع (functions). در مورد جاوااسکریپت، این مفاهیم و ویژگی‌ها در استاندارد ECMA-262 در فرم یک شبه‌زبان به نام ECMAScript یا ES تعریف شده است. ES در همه‌ی مرورگرهای وب (و Node.js) پیاده‌سازی شده و خوشبختانه بر خلاف یک دهه قبل، مرورگرها به‌سرعت خود را با آخرین ویژگی‌های ES که سالانه منتشر می‌شوند، سازگار می‌کنند. در این درس، ویژگی‌های گرامری زبان جاوااسکریپت را بررسی می‌کنیم که شامل مواردی مثل حساسیت به حروف (case sensitivity)، رفتار مفسر جاوااسکریپت با فضاهای خالی و کاراکترهای whitespace، نحوه‌ی جداسازی دستورات و مواردی از این دست می‌شود. سایر مفاهیم پایه‌ی برنامه‌نویسی در جاوااسکریپت را در درس‌های بعدی این فصل بررسی می‌کنیم.

گرامر پایه جاوااسکریپت

بین زبان‌های برنامه‌نویسی و زبان‌های انسانی یا طبیعی شباهت‌های زیادی وجود دارد. هر زبان انسانی و هر زبان برنامه‌نویسی دارای یک مجموعه قواعد نحوی مشخص است که با عنوان گرامر (syntax) آن زبان شناخته می‌شود. همانطور که صحبت کردن به یک زبان انسانی بدون اطلاع از گرامر آن زبان ممکن نیست، بدون اشراف به گرامر یک زبان برنامه‌نویسی نیز نمی‌توان آن زبان را به کار گرفت. هر زبان انسانی دارای تعدادی واژ‌ه (word) است و در مقابل، هر زبان برنامه‌نویسی نیز دارای تعدادی کلمه کلیدی (keyword) است که برای مفسر یا کامپایلر آن زبان، معنای مشخصی دارند. همچنین، گزاره‌ها (statements) در زبان‌های برنامه‌نویسی حکم جملات (sentences) در زبان‌های انسانی را دارند و پاراگراف‌های زبان‌های انسانی را که از مجموعه‌ای از جملات تشکیل می‌شوند، می‌توانیم معادل بلاک‌های کد بدانیم که مجموعه‌ای از دستورات هستند که معمولاً درون یک جفت آکلاد قرار می‌گیرند.

شباهت گرامری جاوااسکریپت و زبان‌های دیگر

گرامر جاوااسکریپت شباهت زیادی به زبان C و سایر زبان‌های C-like مانند جاوا و Perl دارد. البته این مطلب در مورد وضعیت فعلی جاوااسکریپت صادق است و نسخه‌های ابتدایی جاوااسکریپت، اغلب ویژگی‌های خود را از زبان‌های کمتر شناخته‌شده‌ی Scheme و Self اقتباس کرده بودند.

به طور کلی یک برنامه‌ی جاوااسکریپت مجموعه‌ای از گزاره‌هاست و یک گزاره از ترکیب مقادیر، عملگرها، عبارت‌ها و کلمات کلیدی ساخته می‌شود. مقادیر (values) داده‌های مورد استفاده در برنامه هستند که در قالب لیترال‌ها (literals) و یا متغیرها در برنامه وارد می‌شوند. یک مقدار لیترال مقدار ثابتی است که مستقیماً در برنامه وارد می‌شود. عملگرها (operators) نمادهایی هستند که برای انجام عمل مشخصی روی مقادیر کاربرد دارند و مقادیری که عملگر روی آنها اعمال می‌شود، عملوند (operand) نام دارند. کلمات کلیدی (keywords) کلماتی هستند که برای مفسر جاوااسکریپت معنای مشخصی دارند و اصطلاحاً‌ توسط مفسر رزرو شده‌اند. اما عبارت‌ها (expressions) و گزاره‌ها (statements) دو مفهوم کلیدی در جاوااسکریپت هستند. یک عبارت که معمولاً از ترکیب عملگرها و عملوندها ایجاد می‌شود، تکه‌کدی است که به یک مقدار ارزیابی (evaluate) می‌شود اما کاری انجام نمی‌دهد و به عبارت دیگر، تغییری در وضعیت برنامه به وجود نمی‌آورد. ولی یک گزاره مقدار ندارد اما می‌تواند باعث تغییر وضعیت برنامه شود.

در گزاره‌ی ساده‌ی زیر همه‌ی موجودیت‌هایی که در پاراگراف قبل معرفی کردیم، دیده می‌شوند.

let x = 5 + 6;

در این گزار‌ه‌ let یک کلمه کلیدی است که برای تعریف متغیر در جاوااسکریپت کاربرد دارد و x نا‌می است که برای این متغیر تعیین کرده‌ایم. نمادهای = و + دو عملگر هستند که اولی (=) عملگر تخصیص (assignment) نامیده می‌شود و برای تخصیص مقدار به متغیر کاربرد دارد و دو‌می ‌(+) عملگر جمع است که وقتی مانند گزاره‌ی بالا روی اعداد اعمال شود، حکم عمل جمع جبری را دارد. همچنین، اعداد 5 و 6 مقادیر لیترالی هستند که در این گزاره نقش عملوند را برای عملگر جمع دارند. 5 + 6 یک عبارت (expression) است که به مقدار 11 ارزیابی می‌شود. با اجرای گزاره‌ی بالا متغیری با نام x ایجاد شده و مقدار 11 به آن اختصاص داده می‌شود و این تغییری است که با اجرای این گزاره در برنامه ایجاد می‌شود.

برخی از زبان‌های برنامه‌نویسی نسبت به بزرگی یا کوچکی حروف حساس هستند و برخی دیگر تمایزی بین حروف کوچک و بزرگ قائل نیستند. همچنین، استفاده از فضاهای خالی اضافی برای افزایش خوانایی کدها در برخی زبان‌ها مجاز است و در برخی دیگر خیر. علاوه بر اینها، هر زبان روشی را برای جاسازی توضیحات (comments) در بین کدها ارائه می‌دهد. جداسازی دستورات از یکدیگر نیز در زبان‌های مختلف به روش یکسانی انجام نمی‌شود. در ادامه، این ویژگی‌ها و نکات پایه‌ی گرامری را در جاوااسکریپت بررسی می‌کنیم.

شناسه‌ها و کلمات کلیدی رزرو شده

منظور از یک شناسه (identifier) نام یک متغیر، تابع یا آرگومان تابع است. شناسه‌ها می‌توانند از یک یا چند کاراکتر به فرمت زیر باشند:

  • اولین کاراکتر باید یک حرف یا یکی از کاراکترهای _ و $ باشد.
  • سایر کاراکترها می‌توانند حرف، کاراکترهای _ و $ و یا عدد باشند.

لزومی ندارد که حروف یک شناسه از مجموعه‌ی کاراکتری ASCII باشند و امکان استفاده از هر کاراکتر Unicode (از جمله حروف فارسی) هم در نام شناسه‌ها وجود دارد. اما بهتر است فقط از کاراکترهای ASCII به این منظور استفاده کنیم.

استایل قراردادی camelCase

بنابر قرارداد، شناسه‌های ES با استفاده از استایل camelCase ایجاد می‌شوند. در این استایل، اگر نام مورد نظر یک کلمه باشد، با حروف کوچک (lowercase) نوشته می‌شود و اگر از بیش از یک کلمه تشکیل شده باشد، کلمات به هم چسبیده و بدون فاصله نوشته می‌شوند اما از کلمه‌ی دوم به بعد، حرف اول هر کلمه به صورت بزرگ (Capitalize) نوشته می‌شود (مثل camelCase یا doSomeWork). بهتر است شما هم متغیرها و توابع خود را به همین شیوه نامگذاری کنید.

استاندارد ECMA-262 شامل تعریف مجموعه‌ای از کلمات کلیدی رزرو شده (reserved keywords) است. این کلمات معنای مشخصی برای مفسر دارند و نمی‌توان از آنها به عنوان شناسه استفاده کرد. لیست کامل کلمات کلیدی در استاندارد ES 6 از این قرار است:

await break case catch class const continue debugger default delete do else export extends false finally for function if import in instanceof let new null return super switch this throw true try typeof var void while with yield

علاوه بر این، چند کلمه‌ی دیگر نیز داریم که به عنوان کلمات کلیدی آینده، رزرو شده‌اند و آنها را نیز نمی‌توانیم به عنوان شناسه تعیین کنیم. این کلمات عبارتند از:

enum arguments eval implements interface package private protected public static

البته به جز enum بقیه‌ی این کلمات تنها در Strict Mode رزرو شده محسوب می‌شوند اما به هر حال، بهتر است هیچ‌وقت از آنها به عنوان شناسه استفاده نکنیم. در مورد Strict Mode در بخش پایانی همین درس صحبت می‌کنیم.

جاوااسکریپت و حساسیت به حروف

جاوااسکریپت نسبت به بزرگی یا کوچکی حروف حساس است و اصطلاحاً این زبان case sensitive است. بنابراین، برای مفسر جاوااسکریپت a و A با هم یکی نیستند. به این مطلب باید در هنگام تعیین نام برای متغیرها و توابع و نیز هنگام استفاده از کلمات کلیدی و شناسه‌های رزرو شده توجه داشته باشیم.

این ویژگی جاوااسکریپت باعث می‌شود که استفاده از نام‌هایی که تنها در بزرگی یا کوچکی حروف با هم فرق دارند، برای متغیرها مجاز باشد. به عنوان مثال، در یک برنامه می‌توانیم متغیری با نام test و متغیر دیگری با نام Test داشته باشیم. با این وجود، توصیه می‌کنم که از این کار اجتناب کنید.

جاوااسکریپت و فضاهای خالی

جاوااسکریپت یک زبان free-format است؛ به این معنی که مفسر این زبان خطوط خالی، تورفتگی‌ها و فاصله‌های اضافی را نادیده ‌می‌گیرد و بنابراین، ‌می‌توانیم از این موضوع برای افزایش خوانایی کدها استفاده کنیم. به عنوان مثال، قرار دادن یک فاصله قبل و بعد از عملگرها ‌می‌تواند باعث شود خواندن و ویرایش کدها ساده‌تر باشد. هر دو گزاره‌ی زیر از نظر مفسر جاوااسکریپت مجاز بوده و به یک معنا هستند اما گزاره‌ی اول از خوانایی بالاتری برخوردار است:

let x = 5 + 6;
let x=5+6;

استفاده از تورفتگی (indentation) نیز مجاز بوده و می‌تواند افزایش خوانایی کدها را به همراه داشته باشد. اگر از یک ویرایشگر ساده برای نوشتن کدهای جاوااسکریپت استفاده می‌کنید، خودتان باید تورفتگی‌ها را ایجاد کنید اما ویرایشگرهای هوشمند به طور خودکار هر جا لازم باشد مقداری تورفتگی ایجاد می‌کنند.

جداسازی دستورات در جاوااسکریپت

برخی از زبان‌های برنامه‌نویسی از سمی‌کالن ( ; ) برای جداسازی دستورات از یکدیگر استفاده می‌کنند و برخی دیگر هر خط را یک دستور مجزا در نظر می‌گیرند و رفتن به خط بعدی را به منزله‌ی شروع یک دستور جدید می‌دانند. روش جاوااسکریپت برای جداسازی دستورات، چیزی بین این دو رویکرد است.

در جاوااسکریپت، خطی که با سمی‌کالن به انتها برسد، تکلیفش روشن است و یک گزاره‌ی مجزا تلقی می‌شود. اما اگر خطی بدون سمی‌کالن تمام شود، مفسر ابتدا سعی می‌کند آن خط را به صورت یک گزاره تفسیر کند. اگر این امکان وجود داشته باشد، مفسر این خط را یک دستور مجزا تلقی می‌کند و سراغ خط بعدی می‌رود. اما اگر امکان تفسیر خط بدون سمی‌کالن به عنوان یک گزاره‌ برای مفسر وجود نداشته باشد، خط بعدی را به عنوان ادامه‌ی این خط در نظر می‌گیرد. برای روشن شدن موضوع، مثال زیر را ببینید:

let i = 0
i = i + 1;
let
j
j=3;
console.log(j);

در اینجا با وجودی که در پایان خط اول خبری از سمی‌کالن نیست اما مفسر متوجه پایان یافتن گزاره می‌شود و لذا خط بعدی را شروع یک دستور جدید فرض می‌کند. تکلیف خط دوم که به خاطر وجود سمی‌کالن روشن است. خط سوم نیز بدون سمی‌کالن تمام شده اما مفسر متوجه می‌شود که این خط که تنها از کلمه کلیدی let تشکیل شده، نمی‌تواند یک گزاره باشد و لذا خط بعدی را ادامه‌ی این خط فرض می‌کند.

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

let i = 0; i = i + 1;

به طور کلی، عدم استفاده از سمی‌کالن در انتهای دستورات در جاوااسکریپت کار پسندیده‌ای نیست و سناریوهایی وجود دارد که عدم توجه برنامه‌نویس می‌تواند مفسر را به اشتباه بیندازد. بنابراین، توصیه می‌کنم که حتماً‌ از سمی‌کالن در انتهای دستورات استفاده کنید.

کامنت‌های جاوااسکریپت

مثل هر زبان برنامه‌نویسی دیگر، در جاوااسکریپت نیز امکان درج کامنت‌ها در بین کدها برای افزایش خوانایی و مستندسازی کدها وجود دارد. از کامنت‌ها ‌می‌توان برای اهدافی مانند مستندسازی برنامه و ارائه‌ی توضیحات در مورد کدها و حتی یادآوری‌هایی برای کاری که باید در آینده انجام شود، استفاده کرد.

در جاوااسکریپت امکان ایجاد کامنت‌های تک‌خطی (single-line) و چندخطی (multi-line) وجود دارد. کامنت‌های تک‌خطی با دو اسلش (//) شروع ‌می‌شوند. مفسر جاوااسکریپت از ابتدا تا انتهای خطی را که با دو کاراکتر اسلش شروع شود، نادیده گرفته و آن را کامنت تلقی ‌می‌کند. البته هیچ لزو‌می ‌ندارد که یک کامنت تک‌خطی از ابتدای یک خط جدید شروع شود و ‌می‌توان در هر جایی از یک خط یک کامنت تک‌خطی ایجاد کرد. یک کامنت چندخطی نیز با /* شروع شده و با */ به پایان ‌می‌رسد. مفسر جاوااسکریپت کل متن قرار گرفته بین این کاراکترها را نادیده می‌گیرد.

یکی از موارد تمایز بین برنامه‌نویسان حرفه‌ای و آماتور، نوع نگاه آنها به کامنت‌هاست. برای یک برنامه‌نویس تازه‌کار، درک اهمیت واقعی کامنت‌ها مشکل است اما برنامه‌نویسان باسابقه ‌می‌دانند که نوشتن کامنت‌های مناسب از چه اهمیت بالایی برخوردار است. اهمیت استفاده‌ی مناسب از کامنت‌ها برای توصیف بخش‌های مختلف کدها در یک پروژه‌ی تیمی کاملاً مشخص است. اما حتی اگر روی یک پروژه به صورت انفرادی کار می‌کنید، باز هم مهم است که به خوبی از کامنت‌ها استفاده کنید. هیچ تضمینی نیست که چند ماه بعد (یا حتی چند روز بعد) به کدهای خودتان نگاه کنید و از همه چیز آن سر در بیاورید.

مجموعه کاراکتری جاوااسکریپت

برنامه‌های جاوااسکریپت با استفاده از مجموعه کاراکتری Unicode نوشته می‌شوند و ما می‌توانیم از کاراکترهای این مجموعه برای مواردی مانند نام متغیرها و توابع، رشته‌های متنی، عبارات با قاعده (regular expressions) و کامنت‌ها استفاده کنیم. البته تأکید می‌کنم که در مورد نام متغیرها و توابع، با وجودی که امکان استفاده از کاراکترهای غیر از ASCII وجود دارد و مثلاً می‌توانیم از هر کاراکتر یونیکد (مانند حروف فارسی) برای نامگذاری متغیرها و توابع استفاده کنیم، بهتر است تنها کاراکترهای ASCII را برای این منظور به کار بگیریم.

در جاوااسکریپت روشی برای وارد کردن کاراکترهای Unicode با استفاده از کاراکترهای ASCII وجود دارد که دنباله‌های گریز (escape sequences) نام دارد. در این روش، به جای هر کاراکتر Unicode دنباله‌ی گریز معادل آن که با \u شروع شده و با چهار رقم هگزادسیمال ‌ادامه می‌یابد، وارد می‌شود. به عنوان مثال، برای نمایش کاراکتر п از دنباله‌ی گریز \u03A0 استفاده می‌شود.

حالت سخت‌گیرانه (Strict Mode)

در نسخه‌های ابتدایی جاوااسکریپت، ویژگی‌های عجیب و بحث‌برانگیزی وجود داشت که با گذر زمان، ناکارآمدی آنها مشخص شد اما به‌خاطر ضرورت‌های مربوط به Backward Compatibility که حفظ ویژگی‌های قدیمی را ایجاب می‌کند، نمی‌توان به‌راحتی این ویژگی‌ها را کنار گذاشت.

برای حل این مشکل، در ES 5 حالت سخت‌گیرانه یا strict mode معرفی شد که یک مدل تفسیر و اجرای متفاوت برای جاوااسکریپت است. یعنی وقتی حالت سخت‌گیرانه را فعال کنیم، مفسر از مدل متفاوتی برای تفسیر و اجرای کدها استفاده می‌کند و در صورت استفاده از برخی ویژگی‌ها که در حالت عادی مجاز هستند، خطا گزارش می‌کند.

اگر بخواهیم حالت سخت‌گیرانه را برای کل اسکریپت فعال کنیم، کافیست عبارت زیر را در ابتدای اسکریپت‌ها قرار دهیم:

'use strict';

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

این امکان هم وجود دارد که حالت سخت‌گیرانه را تنها برای یک تابع فعال کنیم. برای این کار، باید عبارت 'use strict'; را در ابتدای بدنه‌ی تابع قرار دهیم:

function doSomething(){
  'use strict';
  //function body
}

ماژول‌های ES 6 و کلاس‌ها به طور پیش‌فرض و بدون نیاز به 'use strict'; در حالت سخت‌گیرانه قرار دارند. همچنین، ابزارهای bundler مانند Webpack به طور خودکار 'use strict'; را در سند درج می‌کنند.