Chapter 2ساختار برنامه

And my heart glows bright red under my filmy, translucent skin and they have to administer 10cc of JavaScript to get me to come back. (I respond well to toxins in the blood.) Man, that stuff will kick the peaches right out your gills!

_why, Why's (Poignant) Guide to Ruby
Picture of tentacles holding objects

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

عبارت‌ها و دستورات

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

قطعه کدی که باعث تولید یک مقدار می شود را اصطلاحا عبارت (expression) می گویند. هر مقداری که در برنامه به طور مستقیم نوشته میشود (مانند 22 یا "psychoanalysis" ) در واقع یک عبارت است. یک عبارت اگر داخل پرانتز قرار گیرد نیز یک عبارت محسوب می شود. به همین ترتیب، عملگر دودویی اگر به دو عبارت اعمال شود یا عملگر یکانی اگر به یک عبارت اعمال شود، تولید عبارت جدید می کنند.

این شیوه کارکرد عبارت‌ها گوشه‌ای از زیبایی شباهت زبان برنامه نویسی به زبان طبیعی را نشان می دهد. درست شبیه زیر‌جمله‌ها در زبان های بشری، عبارت‌ها می توانند تو در تو باشند . یک زیرجمله می تواند زیرجمله‌ی دیگری را در بر داشته باشد و الی آخر. این ویژگی باعث می شود بتوانیم عبارت‌ها را ترکیب کنیم تا محاسبات پیچیده‌تری را انجام دهیم.

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

ساده ترین شکل یک دستور شامل یک عبارت و یک سمی کالن در انتهای آن است. این یک برنامه است:

1;
!false;

البته برنامه بالا، کاربردی ندارد. یک عبارت می تواند منجر به تولید یک مقدار شود، مقداری که بعدا توسط کدهای اطرافش استفاده شود. یک دستور به طور مستقل عمل می کند و زمانی می توان آن را دستوری صحیح نامید که اثری برجای بگذارد. می تواند چیزی را روی صفحه نمایش دهد – که اثرگذاری محسوب می شود – یا حالت درونی برنامه را به گونه‌ای تغییر دهد که بر عبارت هایی که بعد می آیند اثر بگذارد. این تغییرات را اثرات جانبی (side effects) می نامند. در مثال قبل، دستوراتی که آورده شدند فقط مقدارهای 1 و true را تولید می کردند و بلافاصله آن ها را بدون استفاده رها می کردند. این کار به هیچ وجه اثری بر جای نمی گذارد. زمانی که این برنامه اجرا می شود، اتفاق قابل مشاهده‌ای رخ نمی دهد.

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

متغیرها

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

let caught = 5 * 5;

استفاده از متغیر، نوع دومی از دستور را به ما معرفی می کند. کلمه‌ی کلیدی let مشخص می کند که این جمله قرار است تا متغیری را تعریف نماید. بعد از این کلمه کلیدی، نام متغیر می‌آید و در صورتی که بخواهیم مقداری را بلافاصله به آن اختصاص دهیم، می توانیم از عملگر = استفاده کنیم.

دستور بالا متغیری به نام caught را ایجاد کرده و از آن برای نگه‌داری یک عدد که از حاصل ضرب 5 در 5 به وجود آمده است، استفاده می کند.

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

let ten = 10;
console.log(ten * ten);
// → 100

زمانی که یک متغیر به مقداری اشاره می کند، به این معنی نیست که برای همیشه به آن گره خورده است. عملگر = را می توان در هر زمان استفاده کرد و متغیرهای فعلی را از مقادیری که به آن‌ها اشاره می کنند جدا کرده و به مقادیر جدیدی اختصاص داد.

let mood = "light";
console.log(mood);
// → light
mood = "dark";
console.log(mood);
// → dark

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

اجازه دهید تا مثالی دیگر را بررسی کنیم. برای به یاد آوری دلارهایی که لوئیجی هنوز به شما بدهکار است، می توانید از یک متغیر استفاده کنید. زمانی که او 35 دلار به شما پس می دهد، مقدار متغیر مورد نظر را تغییر می دهید.

let luigisDebt = 140;
luigisDebt = luigisDebt - 35;
console.log(luigisDebt);
// → 105

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

میتوان از یک let برای تعریف چندین متغیر استفاده نمود. این تعریف ها بایستی به وسیله ویرگول جدا شوند.

let one = 1, two = 2;
console.log(one + two);
// → 3

کلمه‌های var و const را نیز می توان به طور مشابهی برای ایجاد متغیرها ( binding) استفاده کرد. درست شبیه استفاده از let.

var name = "Ayda";
const greeting = "Hello ";
console.log(greeting + name);
// → Hello Ayda

اولین کلمه کلیدی، var (کوتاه شده‌ی “variable”)، روشی بود که انتساب‌ها را در نسخه قبل از 2015 جاوااسکریپت با آن تعریف می می کردند. در فصل بعدی به تفاوت آن با let خواهیم پرداخت. فعلا، به خاطر داشته باشید که هر دوی آن‌ها در بیشتر اوقات، کار مشابهی انجام می دهند، اما در این کتاب ما به ندرت از var استفاده خواهیم کرد به خاطر اینکه خصوصیات گیج کننده‌ای دارد.

کلمه کلیدی const به معنای انتساب ثابت است. این کلمه کلیدی یک ثابت تعریف می نماید که این ثابت تا زمانی که وجود دارد، به مقدار یکسانی اشاره خواهد کرد. این نوع تعریف، برای نامگذاری یک مقدار در طول برنامه برای مراجعات بعدی مفید است.

نامگذاری انتساب‌ها (متغیرها)

می توان از هر کلمه‌ای برای نامگذاری انتساب‌ها استفاده کرد. می توان از ارقام برای بخشی از نام متغیر استفاده کرد – مثلا catch22 یک نام معتبر است– اما نمی توان آن را با اعداد شروع کرد. نام یک متغیر می تواند شامل علامت دلار ($) یا زیرخط (_) باشد اما دیگر علامت‌های نقطه گذاری یا کاراکترهای ویژه را نمی توان استفاده کرد.

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

break case catch class const continue debugger default
delete do else enum export extends false finally for
function if implements import interface in instanceof let
new package private protected public return static super
switch this throw true try typeof var void while with yield

نگران حفظ کردن کلمات بالا نباشید. اگر هنگام تعریف یک متغیر با خطایی خلاف انتظار روبرو شدید، مطمئن شوید که از کلمات لیست بالا استفاده نکرده باشید.

محیط اجرایی

به مجموعه‌ی انتساب‌ها (متغیرها) و مقادیر آن‌ها که در یک زمان مشخص فعال هستند محیط اجرایی environment گفته می شود. زمانی که برنامه‌ای اجرا می شود، این محیط خالی نیست و حاوی انتساب‌هایی است که بخشی از استاندارد زبان هستند. بیشتر اوقات، محیط اجرایی، مواردی را هم فراهم می نماید تا بتوان به وسیله‌ی آن‌ها با سیستم پیرامون تعامل برقرار کرد. به عنوان مثال، در یک مرورگر وب، توابعی در دسترس هستند که می توان از طریق آن ها با وب سایتی که بارگذاری شده است تعامل داشت و یا ورودی‌های موس و صفحه‌کلید را خواند.

توابع

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

prompt("Enter passcode");
A prompt dialog

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

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

تابع console.log

در مثال های قبلی، من از console.log برای نمایش مقدارها استفاده کردم. اکثر سیستم‌های جاوااسکریپت ( شامل همه مرورگرهای مدرن و node.js) تابعی به نام console.log را فراهم می کنند که آرگومان‌های ورودیش را در جایی به صورت متنی نشان می دهند. در مرورگرها، این خروجی در کنسول جاوااسکریپت قرار می گیرد. کنسول بخشی از مرورگر است که به صورت پیش فرض مخفی است،‌ اما در بیشتر مرورگرها با فشار دادن کلید F12 یا در مک با command-option-I در دسترس خواهد بود. اگر این کلیدهای میانبر کار نکرد، از طریق منوی مرورگر به دنبال “developer tools” و شبیه آن بگردید.

زمانی که مثال‌های اینجا(یا کدهای خودتان) را در صفحات این کتاب اجرا می کنید، خروجی تابع console.log، به جای نمایش در کنسول جاوااسکریپت مرورگر، بعد از خود مثال نشان داده خواهد شد.

let x = 30;
console.log("the value of x is", x);
// → the value of x is 30

اگرچه در نام متغیرها نمی توان از کاراکتر نقطه استفاده کرد، console.log نقطه دارد. علت آن این است که console.log یک انتساب (متغیر) ساده نیست. در واقع عبارتی است که خصیصه‌ی log را از مقداری که در متغیر console نگه داری شده است بر می گرداند. در فصل 4، با مفهوم آن به طور دقیق آشنا خواهیم شد.

بازگرداندن مقادیر

نمایش یک پنجره تعاملی یا نوشتن متن در صفحه‌ی نمایش یک اثر جانبی می باشد. خیلی از توابع به خاطر اثر جانبی ای که تولید می کنند کاربرد دارند. توابع همچنین می توانند مقدار تولید کنند و در این صورت نیازی به اثر جانبی ندارند تا در برنامه کاربرد داشته باشند. به عنوان مثال، تابع Math.max به تعداد دلخواه آرگومان عددی می گیرد و بزرگترین آن ها را برمی گرداند.

console.log(Math.max(2, 4));
// → 4

زمانی که یک تابع مقداری را تولید می کند، گفته می شود که آن مقدار را برگردانده است. در جاوااسکریپت هرچیزی که مقداری را تولید می کند یک عبارت است که به این معناست که می توان فراخوانی توابع را در عبارت‌های بزرگ‌تر صورت داد. در اینجا فراخوانی Math.min که عکس تابع Math.max می باشد، به عنوان بخشی از یک عبارت جمع حسابی استفاده شده است:

console.log(Math.min(2, 4) + 100);
// → 102

در فصل بعد توضیح می دهیم که چگونه توابع خود را بنویسید.

جریان کنترل

زمانی که برنامه شما بیش از یک دستور دارد، دستورها مثل خواندن یک داستان، از بالا به پایین اجرا می شوند. به عنوان مثال، این برنامه دو دستور دارد. اولین دستور از کاربر درخواست می کند که عددی را وارد نماید و دومین که پس از آن اجرا می شود، مربع عدد وارد شده را نشان می دهد.

let theNumber = Number(prompt("Pick a number"));
console.log("Your number is the square root of " +
            theNumber * theNumber);

تابع Number مقدار ورودی اش را به عدد تبدیل می کند. ما به این تبدیل نیاز داریم چرا که خروجی تابع prompt از جنس رشته است و ما به دنبال عدد هستیم. توابع مشابهی به نام‌های String و Boolean وجود دارند که مقادیر را به نوع خودشان تبدیل می کنند.

در اینجا شمایی ابتدایی از چگونگی جریان کنترل خطی مستقیم نمایش داده شده است.

Trivial control flow

اجرای شرطی

همه‌ی برنامه‌ها از جنس خطی و مستقیم نیستند. مثلا ممکن است بخواهیم یک راه دیگر هم در برنامه در نظر بگیریم که برنامه با توجه به شرایطی که در دست دارد راه مناسب را انتخاب کند. به این کار اجرای شرطی می گویند.

Conditional control flow

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

let theNumber = Number(prompt("Pick a number"));
if (!Number.isNaN(theNumber)) {
  console.log("Your number is the square root of " +
              theNumber * theNumber);
}

با این تغییر، اگر رشته‌ی “parrot” را وارد کنید، چیزی به عنوان خروجی نشان داده نخواهد شد.

کلمه کلیدی if براساس مقدار یک عبارت بولی، دستوری را اجرا یا از آن صرف نظر می کند. این عبارت بولی که مبنای تصمیم گیری است درست بعد از کلمه کلیدی، داخل پرانتز نوشته می شود و در ادامه عبارتی می آید که قرار است اجرا شود.

تابع Number.isNaN یک تابع استاندارد جاوااسکریپت است که زمانی مقدار true را برمی گرداند که آرگومان ورودی آن از نوع NaN باشد. اگر رشته‌ای که به تابع Number داده می شود، بیانگر یک عدد معتبر نباشد، این تابع مقدار NaN را برمی‌گرداند. بنابراین، معنای عبارت شرطی این‌گونه می شود: “اگر theNumber از جنس غیر عدد نباشد (عددی باشد) فلان کار را انجام بده”.

در این مثال دستوری که در خط بعد از if قرار دارد توسط کروشه‌های مجعد محصور شده است ({ و }). از کروشه ها می توان برای گروه بندی دستورات متعدد به عنوان یک دستور استفاده کرد که به آن بلاک گفته می شود. در مثال فوق می توانستید که کروشه‌ها را حذف کنید چون فقط یک دستور داشتید اما برای دوری از ابهام مواقع گذاشتن یا نگذاشتن آن‌ها، بیشتر برنامه نویسان جاوااسکریپت از کروشه‌ها، در مواردی شبیه به این هم استفاده می کنند. ما هم در این کتاب از همین عرف استفاده می کنیم مگر در بعضی موارد که قصد داریم کلا دستور در یک خط باشد.

if (1 + 1 == 2) console.log("It's true");
// → It's true

همیشه اینطور نیست که برنامه در صورت true بودن شرط اجرا شود، بلکه گاهی عکس این مطلب اتفاق می افتد. این مسیر جانبی در دیاگرام با پیکان دوم نمایش داده شده است. استفاده از کلمه کلیدی else به همراه if این امکان را به ما می تا دهد دو مسیر اجرایی متفاوت تعریف کنیم.

let theNumber = Number(prompt("Pick a number"));
if (!Number.isNaN(theNumber)) {
  console.log("Your number is the square root of " +
              theNumber * theNumber);
} else {
  console.log("Hey. Why didn't you give me a number?");
}

اگر بیش از دو مسیر برای انتخاب داشتیم، می توان از چندین جفت if/else که با هم زنجیر شده اند استفاده کرد. به مثال توجه کنید:

let num = Number(prompt("Pick a number"));

if (num < 10) {
  console.log("Small");
} else if (num < 100) {
  console.log("Medium");
} else {
  console.log("Large");
}

برنامه در ابتدا بررسی می کند که آیا num از 10 مقدارش کم تر است یا خیر. اگر کمتر بود، به سراغ آن شاخه از کد می رود و "Small" را نشان می دهد و کار تمام است. اگر شرط دوم (< 100) برقرار بود، به این معناست که عدد بین 10 و 100 است که در این صورت "Medium" نمایش داده می شود. اگر هیچ کدام نبود، دومین و آخرین else انتخاب می شود.

نمودار برنامه بالا چیزی شبیه عکس زیر خواهد بود.

Nested if control flow

while و do loops

برنامه ای را در نظر بگیرید که همه‌ی اعداد زوج بین 0 و 12 را چاپ می کند. یکی از راه های نوشتن این برنامه به شکل زیر است:

console.log(0);
console.log(2);
console.log(4);
console.log(6);
console.log(8);
console.log(10);
console.log(12);

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

Loop control flow

جریان کنترل حلقه‌ای به ما این امکان را می دهد که به نقطه‌ای در برنامه برگردیم، جایی که قبلا بوده ایم و آن را در حالت فعلی برنامه تکرار کنیم. اگر این امکان را با یک متغیر شمارشگر ترکیب کنیم، می توانیم کاری شبیه زیر را انجام دهیم:

let number = 0;
while (number <= 12) {
  console.log(number);
  number = number + 2;
}
// → 0
// → 2
//   … etcetera

دستوری که با کلمه کلیدی while شروع شود، یک حلقه ایجاد می کند. ساختار while بسیار شبیه به if است. کلمه while در ابتدا،‌ بعد از آن یک جفت پرانتز که در داخلش یک عبارت قرار می گیرد و بعد دستور نوشته می شود. حلقه while، دستور مورد نظر را به صورت مداوم اجرا می کند البته تا زمانی که عبارت داخل پرانتز true ارزیابی شود. مقدار داخل پرانتز به نوع داده بولی تبدیل می شود و بعد ارزیابی می شود.

متغیر number در اینجا نشان می دهد که چگونه می توان از یک متغیر برای نگه‌داری پیش رفت یک برنامه استفاده کرد. هر بار که حلقه تکرار می شود، مقدار number به اندازه‌ی 2 واحد از قبل بیشتر می شود. در ابتدای هر تکرار این عدد با عدد 12 مقایسه شده تا مشخص شود که آیا به پایان اجرای برنامه رسیده ایم یا خیر.

به عنوان یک مثال کاربردی، اکنون می توانیم برنامه ای بنویسیم که مقدار 210 را محاسبه کند و نمایش دهد. از دو متغیر استفاده خواهیم کرد: یکی برای نگه داشتن نتیجه محاسبه و دیگری برای شمردن تعداد دفعاتی که نتیجه را در 2 ضرب کرده ایم. حلقه بررسی می کند که متغیر دوم به 10 رسیده باشد که در غیر این صورت هر دوی متغیرها را به روز رسانی کند.

let result = 1;
let counter = 0;
while (counter < 10) {
  result = result * 2;
  counter = counter + 1;
}
console.log(result);
// → 1024

می توانستیم متغیر شمارنده‌ی counter را از عدد 1 شروع کنیم که در این صورت شرط به صورت <= 10 بررسی می شود. به دلایلی که در فصل 4 روشن خواهد شد، بهتر است که عادت کنیم تا از 0 بشماریم.

حلقه do یک ساختار کنترلی است شبیه به حلقه‌ی while است. تفاوت فقط در یک چیز است: یک حلقه‌ی do حداقل بدنه‌اش را یک بار اجرا می کند و بررسی شرط توقف را بعد از اولین اجرا انجام می دهد. برای نشان دادن این ویژگی، قسمت بررسی شرط، بعد از بدنه‌ی حلقه نوشته می شود:

let yourName;
do {
  yourName = prompt("Who are you?");
} while (!yourName);
console.log(yourName);

این برنامه شما را وادار خواهد کرد که حتما نامی را وارد نمایید. این درخواست آن قدر پرسیده خواهد شد تا بالاخره چیزی غیر از رشته‌ی خالی را دریافت کند. افزودن عملگر ! باعث می شود که یک مقدار قبل از منفی شدن به نوع بولی تبدیل شود و می دانیم که تمام رشته‌ها به جز "" به true تبدیل می شوند. این بدین معناست که حلقه تا زمانی که نامی غیر تهی وارد نکنید اجرا می شود.

ایجاد تورفتگی در کدها

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

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

if (false != true) {
  console.log("That makes sense.");
  if (1 < 2) {
    console.log("No surprise there.");
  }
}

اکثر برنامه های ویرایشگر کد (مثل ویرایشگر کد این کتاب)، تو رفتگی ها را به صورت خودکار و با اندازه مناسب ایجاد می کنند.

حلقه‌های for

خیلی از حلقه‌ها از الگویی که در مثال‌های while دیدیم پیروی می کنند. در ابتدا یک متغیر “شمارنده” ایجاد می شود تا پیشرفت حلقه را نگه‌داری کند. سپس حلقه‌ی while خواهد آمد که عبارت تست آن معمولا مقدار شمارنده را بررسی می کند که به مرز خاصی رسیده است باشد. در پایان بدنه‌ی حلقه، شمارنده به روز رسانی می شود تا پیشرفت برنامه پیگیری شود.

چون این الگو بسیار رایج است، جاوااسکریپت و زبان های مشابه‌‌ آن روش کمی کوتاهتر و جامع تری را ارائه می دهند، حلقه for.

for (let number = 0; number <= 12; number = number + 2) {
  console.log(number);
}
// → 0
// → 2
//   … etcetera

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

پرانتزهای بعد از کلمه کلیدی for باید حتما دارای دو سمی کالن (نقطه ویرگول) باشند. قسمت قبل از نقطه‌ویرگول اول، حلقه را مقداردهی اولیه می کند، که این کار معمولا با تعریف یک متغیر انجام می شود. قسمت دوم، عبارتی است که بررسی می کند تا کی حلقه باید ادامه پیدا کند. بخش نهایی وضعیت حلقه را بعد از هربار تکرار به روز رسانی می کند. در بیشتر موارد، این روش از ساختار while کوتاهتر و سرراست تر است.

در اینجا کدی را مشاهده می کنید که 210 را محاسبه می کند البته با for به جای while:

let result = 1;
for (let counter = 0; counter < 10; counter = counter + 1) {
  result = result * 2;
}
console.log(result);
// → 1024

شکستن حلقه و خروج از آن

تنها راه به پایان رسیدن یک حلقه فقط تولید مقدار false توسط بخش شرط حلقه نیست. دستور خاصی به نام break وجود دارد که در صورت استفاده باعث می شود که بلافاصله برنامه از حلقه‌ی محصورش بیرون بیاید.

برنامه‌ی زیر چگونگی استفاده از break را نمایش می دهد. این برنامه، اولین عددی که بزرگتر و مساوی 20 است و همچنین قابل تقسیم بر 7 است را می یابد.

for (let current = 20; ; current = current + 1) {
  if (current % 7 == 0) {
    console.log(current);
    break;
  }
}
// → 21

با استفاده از عملگر باقی مانده (%) می توان به راحتی بخش پذیری یک عدد بر عددی دیگر را امتحان کرد. اگر بخش پذیر باشد، باقی مانده تقسیمشان برابر صفر خواهد بود.

ساختار for در این مثال، بخشی که پایان حلقه را بررسی می کند را ندارد. این بدین معناست که حلقه هرگز متوقف نخواهد شد مگر اینکه دستور break در درون آن اجرا شود.

اگر آن دستور break را حذف کنید یا به طور اتفاقی شرطی را بنویسید که همیشه مقدار true را تولید نماید، برنامه شما در یک حلقه‌ی بی‌نهایت گیر خواهد افتاد. برنامه‌ای که در حلقه بی نهایت بیفتد، اجرای آن پایان نمی یابد، که معمولا چیز بدی است.

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

کلمه‌ی کلیدی continue همانند break روی پیشروی حلقه تاثیر می گذارد. زمانی که continue در داخل بدنه حلقه اجرا شود، کنترل برنامه از بدنه خارج شده و به تکرار بعدی منتقل شده و ادامه می یابد.

روش کوتاه به روزرسانی متغیرها

به طور خاص در حلقه‌ها، معمولا برنامه نیازمند به روز رسانی یک متغیر بر اساس مقدار قبلی آن است.

counter = counter + 1;

جاوااسکریپت راه میانبری برای این کار دارد:

counter += 1;

میانبرهای مشابهی برای دیگر عملگرها وجود دارد مثل result *= 2 که مقدار متغیر result را دوبرابر می کند یا counter -= 1 که مقدار count را کاهش می دهد.

با این روش می توانیم مثال شمارش اعداد را کمی کوتاه تر بنویسیم.

for (let number = 0; number <= 12; number += 2) {
  console.log(number);
}

برای counter += 1 و counter -= 1 معادل‌های کوتاهتری هم وجود دارد: counter++ وcounter--.

تصمیم گیری بر اساس یک مقدار به کمک switch

کدهایی شبیه کد زیر بسیار رایج هستند:

if (x == "value1") action1();
else if (x == "value2") action2();
else if (x == "value3") action3();
else defaultAction();

ساختاری به نام switch وجود دارد که در مواردی شبیه بالا خواناتر و سرراست تر است. متاسفانه، سبکی که جاوااسکریپت برای این ساختار استفاده می کند ( که از زبان‌های شبیه C/Java گرفته شده است) کمی بدقواره است – زنجیره‌ای از دستورات if ممکن است زیباتر به نظر بیاید. به مثال توجه فرمایید:

switch (prompt("What is the weather like?")) {
  case "rainy":
    console.log("Remember to bring an umbrella.");
    break;
  case "sunny":
    console.log("Dress lightly.");
  case "cloudy":
    console.log("Go outside.");
    break;
  default:
    console.log("Unknown weather type!");
    break;
}

می توانید هر تعداد دلخواه برچسب case درون یک بلاک switch قرار دهید. برنامه با توجه به مقداری که به switch داده می شود برچسب متناظر را انتخاب کرده و به آن قسمت منتقل می شود یا اگر موردی پیدا نشود قسمت default اجرا می شود. پس از انتخاب برچسب، دستورات آن اجرا می شوند حتی دستوراتی که زیر برچسب دیگری قرار دارند تا زمانیکه برنامه به دستور break برسد. در بعضی موارد، مانند case مربوط به "sunny" در مثال بالا، این امکان را می توان برای به اشتراک گذاری کدهایی بین case ها استفاده کرد ( برنامه، رفتن به بیرون شهر را برای هر دو هوای ابری و آفتابی پیشنهاد می دهد). اما حواستان باشد: خیلی ساده امکان دارد break فراموش شود که باعث می شود کدهایی اجرا شود که مورد نظر شما نبوده اند.

استفاده از حروف بزرگ

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

fuzzylittleturtle
fuzzy_little_turtle
FuzzyLittleTurtle
fuzzyLittleTurtle

خواندن سبک اول می تواند سخت باشد. شخصا، روش استفاده از زیرخط (underscore) را می پسندم اگرچه تایپ آن کمی مشکل تر است. توابع استاندارد جاوااسکریپت و بیشتر برنامه نویسان جاوااسکریپت از روش آخر استفاده می کنند – به جز کلمه‌ی اول دیگر کلمات را با حروف بزرگ شروع می کنند. می توان به راحتی به یکی از این روش ها عادت کرد. کدنویسی با سبک‌های نامگذاری متفاوت، مشکلاتی در خوانایی کد ایجاد می کند، بنابراین ما فقط از یک سبک و آن هم سبک آخر استفاده خواهیم کرد.

در موارد کمی، مثل تابع Number حرف اول متغیر به صورت بزرگ نوشته شده است. این کار برای نشانه‌گذاری این تابع به عنوان یک تابع سازنده است. اینکه یک تابع سازنده چیست در فصل 6 روشن خواهد شد. فعلا نکته مهم این است که این عدم یکپارچگی در این گونه موارد شما را ناراحت نکند.

توضیحات

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

یک توضیح، متنی است که بخشی از برنامه محسوب می شود اما کامپیوتر آن را نادیده می گیرد. جاوااسکریپت دو راه برای نوشتن توضیحات دارد. برای نوشتن توضیحات تک-خطی ، می توانید از دو کاراکتر اسلش (//) در ابتدای توضیح استفاده کنید.

let accountBalance = calculateBalance(account);
// It's a green hollow where a river sings
accountBalance.adjust();
// Madly catching white tatters in the grass.
let report = new Report();
// Where the sun on the proud mountain rings:
addToReport(accountBalance, report);
// It's a little valley, foaming like light in a glass.

توضیحی که با // مشخص شده باشد فقط تا پایان همان خط در نظر گرفته می شود. متنی که بین /* و */ محصور شده باشد به طور کامل و بدون توجه به اینکه حاوی چند خط باشد، به عنوان توضیح در نظر گرفته می شود. این ویژگی برای افزودن بلاک‌های اطلاعات در باره‌ی یک فایل یا بخشی از برنامه استفاده می شود.

/*
  I first found this number scrawled on the back of an old notebook.
  Since then, it has often dropped by, showing up in phone numbers
  and the serial numbers of products that I've bought. It obviously
  likes me, so I've decided to keep it.
*/
const myNumber = 11213;

خلاصه

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

قرار دادن دستورات یکی پس از دیگری، برنامه ای را می سازد که از بالا به پایین به ترتیب اجرا می شود. می توان این ترتیب جریان کنترل را با استفاده از دستورات شرطی ( if, else و switch) و حلقه‌ها (while, do, و for) به هم زد.

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

توابع مقدارهایی ویژه هستند که بخشی از برنامه را در درون خود نگه داری می کنند. می توانید آن‌ها را با نوشتن functionName(argument1, argument2) فراخوانی کنید. این گونه فراخوانی کردن یک تابع، یک نوع عبارت محسوب می شود که می تواند مقداری را هم تولید کند.

تمرین‌ها

آگر مطمئن نیستید که چگونه راه حل‌هایی را که برای تمرین ها ارائه می دهید آزمایش و اجرا کنید، به مقدمه رجوع کنید.

هر تمرین با یک شرح مساله شروع می شود. آن را بخوانید و سعی کنید آن را حل کنید. اگر در حل مساله به مشکل برخورد کردید، به قسمت “راهنمایی” که در انتهای هر تمرین قرار گرفته است است رجوع کنید. راه حل کامل برای تمرین‌ها در داخل این کتاب گنجانده نشده است،‌ اما می توانید آن ها را در https://eloquentjavascript.net/code بیابید. اگر واقعا می خواهید که از تمرین‌ها چیزی بیاموزید، فقط زمانی به سراغ راه حل ها بروید که مساله را حل نموده اید یا حداقل به اندازه کافی تلاش و زمان صرف حل آن کرده باشید که کمی احساس سردرد داشته باشید.

حلقه و مثلث

حلقه‌ای را پیاده سازی کنید که هفت بار تابع console.log را فراخوانی کرده و مثلث زیر را تولید کند:

#
##
###
####
#####
######
#######

دانستن این نکته که طول یک رشته را می توان با گذاشتن .length در انتهای آن بدست آورد شاید به دردتان بخورد.

let abc = "abc";
console.log(abc.length);
// → 3

بیشتر تمرین‌ها به همراه‌ کدی می آیند که می توانید برای حل تمرین آن را ویرایش کنید. به یاد داشته باشید که برای ویرایش باید روی بلاک کد کلیک کنید.

// Your code here.

می توانید با برنامه‌ای شروع کنید که اعداد 1 تا 7 را چاپ می کند، که با کمی دستکاری کد مثال چاپ اعداد زوج که در این فصل آمد به دست می آید. جایی که حلقه‌ی for را معرفی کردیم.

اکنون به شباهت بین اعداد و رشته‌هایی که از کاراکتر ‍‍# تشکیل شده اند توجه کنید. با افزودن 1، می شود از عدد 1 به عدد 2 رسید (+= 1). همین کار را می توان برای ایجاد "##" از "#" انجام داد (+= "#"). پس راه حل، شباهت زیادی به مثال چاپ اعداد دارد.

FizzBuzz

برنامه ای بنویسید که با استفاده از console.log تمامی اعداد بین 1 و 100 را چاپ نماید با دو استثنا. برای اعدادی که بر 3 بخش پذیرند به جای عدد عبارت"Fizz"، و برای اعدادی که بر 5 بخش پذیرند (نه بر 3)، مقدار "Buzz" را چاپ نماید.

وقتی برنامه شما موارد بالا را به درستی انجام داد، آن را تغییر داده تا مقدار "FizzBuzz" را برای اعدادی که بر 3 و 5 به طور همزمان بخش پذیرند چاپ کند ( برنامه همچنان باید "Fizz" و "Buzz" را برای اعدادی که به هر یک بخش پذیرند را چاپ کند).

(این مساله در واقع یکی از سوالات مصاحبه شغلی است که ادعا شده که می تواند برای رد افرادی که مناسب برنامه نویسی نیستند استفاده شود. بنابراین اگر شما آن را حل کردید، همین الان قیمت شما در بازار کار بالاتر رفت. )

// Your code here.

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

در نسخه‌ی اول، سه خروجی برای هر عدد ممکن است، بنابراین باید یک زنجیره‌ی if/else if/else ایجاد کنید.

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

صفحه‌ی شطرنج

برنامه ای بنویسید که رشته‌ای را ایجاد می کند که نمایانگر یک صفحه‌ی 8×8 است. از کاراکتر خط جدید برای جداسازی خطوط استفاده کنید. در هر مکان از صفحه می توان فضای خالی (فاصله) یا کاراکتر "#" استفاده کرد. مجموع کاراکترها باید شبیه صفحه شطرنج باشند.

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

 # # # #
# # # #
 # # # #
# # # #
 # # # #
# # # #
 # # # #
# # # #

بعد از ساختن قسمت اول و تولید الگوی بالا، اکنون متغیر size = 8 را تعریف کنید و برنامه را طوری تغییر دهید که برای هر مقدار size به درستی کار کند و صفحه‌ای با آن طول و عرض تولید کند.

// Your code here.

با یک رشته‌ی خالی ("") می توانید ساخت رشته‌ی مورد نظر را شروع کنید و بعد به صورت مداوم کاراکتر‌ها را ضافه نمایید. یک کاراکتر خط جدید به صورت "\n" نوشته می شود.

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

به دو متغیر برای ردگیری پیشرفت برنامه نیاز خواهید داشت. برای دانستن اینکه در یک موقعیت آیا باید فضای خالی قرار دهید یا کاراکتر #، می توانید بررسی کنید که جمع دو شمارنده‌ مقداری زوج باشد (% 2).

پایان دادن یک خط با یک کاراکتر خط جدید ("\n") باید بعد از اینکه هر خط ساخته شد اتفاق بیفتند، بنابراین این کار را بعد از حلقه‌ی درونی اما درون حلقه‌ی بیرونی انجام دهید.