۲۵ ژوئیه ۲۰۲۲

اعلان تابع Expression

در جاوااسکریپت، تابع یک “ساختار جادویی زبان” نیست، بلکه یک نوع خاصی از مقدار است.

سینتکسی که ما قبلا استفاده کردیم یک Function Declaration نامیده می‌شود:

function sayHi() {
  alert( "سلام" );
}

یک سینتکس دیگر هم برای ساخت تابع وجود دارد که Function Expression نامیده می‌شود.

این سینتکس به ما این امکان را می‌دهد که بین هر عبارتی یک تابع جدید بسازیم.

برای مثال:

let sayHi = function() {
  alert( "سلام" );
};

اینجا می‌توانیم متغیر sayHi را ببینیم که مقداری را دریافت می‌کند، تابع جدید، که به صورت function() { alert("Hello"); } ساخته شده است.

چون ایجاد تابع با زمینه (context) عبارت مقداردهی (سمت راست =) رخ می‌دهد، این یک Function Expression است.

لطفا در نظر داشته باشید، هیچ اسمی بعد از کلمه کلیدی function وجود ندارد. حذف کردن اسم برای Function Expressionها مجاز است.

اینجا ما بلافاصله آن را به متغیر اختصاص می‌دهیم پس معنی این قطعه‌های کد یکسان است: «تابعی بساز و آن را درون متغیر sayHi قرار بده».

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

تابع یک مقدار است

بیایید تکرار کنیم: مهم نیست که تابع چگونه ساخته شده است، یک تابع همیشه یک مقدار است. هر دو مثال بالا تابعی را درون متغیر sayHi ذخیره می‌کنند.

ما حتی می‌توانیم آن مقدار را با استفاده از alert چاپ کنیم:

function sayHi() {
  alert( "سلام" );
}

alert( sayHi ); // کد تابع را نشان می‌دهد

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

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

مسلما، تابع یک مقدار خاص است، به همین دلیل ما می‌توانیم آن را مثل sayHi() صدا بزنیم.

اما تابع همچنان یک مقدار است. پس ما می‌توانیم با آن مثل انواع دیگر مقدارها کار کنیم.

ما می‌توانیم یک تابع را در یک متغیر دیگر کپی کنیم:

function sayHi() {   // (1) ساختن
  alert( "سلام" );
}

let func = sayHi;    // (2) کپی کردن

func(); // سلام     // (3) کپی را اجرا می‌کنیم (کار می‌کند!)
sayHi(); // سلام    //     هنوزم کار می‌کند (چرا نکند)

چیزی که بالا اتفاق می‌افتد با جزییات اینجا هست:

  1. Function Declaration (1) تابع را می‌سازد و آن را داخل متغیر sayHi قرار می‌دهد.
  2. خط (2) آن را داخل متغیر func کپی می‌کند. لطفا دوباره در نظر داشته باشید: هیچ پرانتزی بعد از sayHi وجود ندارد. اگر وجود داشت، سپس func = sayHi() نتیجه صدا زدن sayHi() را در func می‌نوشت، نه خود تابع sayHi.
  3. حالا تابع می‌تواند با sayHi() و func() صدا زده شود.

همچنین می‌توانستیم از یک Function Expression برای تعریف sayHi در خط اول، استفاده کنیم:

let sayHi = function() { // (1) ایجاد
  alert( "سلام" );
};

let func = sayHi;
// ...

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

چرا یک نقطه ویرگول در انتها وجود دارد؟

شاید برای شما سوال باشد، چرا Function Expression در انتها نقطه ویرگول ; دارد، اما Function Declaration ندارد:

function sayHi() {
  // ...
}

let sayHi = function() {
  // ...
};

جواب ساده است: یک Function Expression به صورت function(…) {…}، در داخل دستور مقداردهی استفاده می‌شود: let sayHi = ...;. نقطه ویرگول ; در انتهای دستور پیشنهاد می‌شود، این علامت جزء سینتکس تابع نیست.

نقطه ویرگول برای مقداردهی ساده‌تر وجود دارد مثل let sayHi = 5; و همچنین برای مقداردهی تابع نیز وجود دارد.

تابع‌های Callback

بیایید به مثال‌های بیشتری درباره استفاده کردن از تابع ها به عنوان مقدار و استفاده کردن از function expressions نگاه کنیم.

ما یک تابع ask(question, yes, no) با سه پارامتر می‌نویسیم:

question
متن سوال
yes
تابعی برای اجرا کردن اگر جواب “Yes” باشد
no
تابعی برای اجرا کردن اگر جواب “No” باشد

تابع باید question را بپرسد، و بر اساس جواب کاربر، yes() یا no() را صدا بزند:

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

function showOk() {
  alert( "شما موافقت کردید." );
}

function showCancel() {
  alert( "شما اجرا شدن را لغو کردید." );
}

// نحوه استفاده: تابع‌های showOk، showCancel به عنوان آرگومان به ask داده شده‌اند
ask("آیا موافق هستید؟", showOk, showCancel);

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

آرگومان‌های showOk و showCancel داخل ask callback functions یا فقط callbacks نامیده می‌شوند.

ایده اینطور است که ما یک تابع را می‌دهیم و از آن توقع داریم که بعدا اگر نیاز شد “دوباره صدا زده شود”. در مورد ما، showOk تبدیل به callback برای جواب “yes” می‌شود، و showCancel برای چواب “no”.

ما می توانیم از Function Expressions برای نوشتن همان تابع به صورت بسیار کوتاه‌تر استفاده کنیم:

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

ask(
  "آیا موافق هستید؟",
  function() { alert("شما موافقت کردید."); },
  function() { alert("شما اجرا شدن را لغو کردید."); }
);

اینجا، تابع‌ها دقیقا درون صدا زدن ask(...) تعریف شده اند. آنها هیچ اسمی ندارند، و بنابراین anonymous نامیده می شود. چنین تابع هایی بیرون از ask قابل دسترسی نیستند (چون آنها به متغیری تخصیص داده نشده اند)، اما این چیزی است که ما اینجا می‌خواهیم.

چنین کدی در اسکریپت‌های ما به طور طبیعی نمایان می شوند، این در ذات جاوااسکریپت است.

یک تابع مقداری است که یک “عمل” را نمایش می‌دهد