انواع توابع در زبان جاوا اسکریپت

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


انواع توابع

در جاوا اسکریپت یک تابع یک ساختار جادویی زبان نیست. در واقع تابع یک نوع مقدار است. قاعده نوشتاری که قبلاً برای تعریف یک تابع استفاده می‌کردیم Function Declaration نام داشت : 

function sayHi() {
  alert( "Hello" );
}

قاعده نوشتاری دیگری برای ایجاد یک تابع وجود دارد که Function Expression نامیده می شود :

let sayHi = function() {
  alert( "Hello" );
};

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

حتی می‌توان مقدار این متغیر را با استفاده از تابع alert نشان داد :

function sayHi() {
  alert( "Hello" );
}

alert( sayHi ); // shows the function code

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

در جاوا اسکریپت تابع یک مقدار است و ما با یک مقدار سر و کار داریم در واقع کد بالا سورس کد تابع را در پنجره alert نمایش می‌دهد. می‌توان یک تابع را در متغیری دیگر کپی کرد:

function sayHi() {   // (1) create
  alert( "Hello" );
}

let func = sayHi;    // (2) copy

func(); // Hello     // (3) run the copy (it works)!
sayHi(); // Hello    //     this still works too (why wouldn't it)

در اینجا به توضیح مثال بالا می پردازیم :

  1. در خط مشخص شده با (1) تابع تعریف شده و در متغیر sayHi قرار گرفته است.
  2. در خط (2) تابع در متغیر دیگری به نام func کپی شده است. دقت داشته باشید که در کپی کردن نباید از پرانتز برای توابع استفاده کرد. در غیر اینصورت نتیجه فراخوانی تابع در متغیر مربوطه کپی خواهد شد
  3. اکنون می‌توان تابع را با استفاده از  هر دو نام ()sayHi و ()func فراخوانی کرد.

در نمونه مثال بالا می توانستیم در تعریف تابع از قاعده نوشتاری دوم (Function Expression) نیز استفاده کنیم :

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

let func = sayHi;
// ...

فراخوانی توابع Callback

بیایید مثال های بیشتری را در مورد ارسال توابع به عنوان مقدار به یک تابع بررسی کنیم. ما می خواهیم تابعی را با سه پارامتر به شکل (ask(question, yes, no تعریف کنیم :

  • Question - متن سوال را مشخص میکند.
  • Yes - اگر جواب کاربر “Yes” باشد این تابع باید فراخوانی شود.
  • No - اگر جواب کاربر “No” باشد این تابع باید فراخوانی شود.

تابع باید سوالی را از کاربر به بپرسد و سپس بسته به پاسخ کاربر (کلیک روی Yes یا No ) توابع ()yes یا ()no را فراخوانی کند : 

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

function showOk() {
  alert( "You agreed." );
}

function showCancel() {
  alert( "You canceled the execution." );
}

// usage: functions showOk, showCancel are passed as arguments to ask
ask("Do you agree?", showOk, showCancel);

در اینجا آرگومان های تابع ask تابع های Callback نامیده  میشوند. ایده اصلی به این صورت است که ما تابعی را به عنوان آرگومان تابع دیگر ارسال می‌کنیم و انتظار داریم بعدا در صورت لزوم فراخوانی شود. در این مورد showOk تابع Callback  برای جواب yes و showCancel تابع Callback برای جواب no است.

می توان با استفاده از قاعده ی نوشتاری دوم مثال بالا را ساده تر نوشت :

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

ask(
  "Do you agree?",
  function() { alert("You agreed."); },
  function() { alert("You canceled the execution."); }
);

در مثال فوق پیاده سازی توابع Callback در خود تابع ask انجام شده است. در این جا نامی برای این توابع در نظر گرفته نشده است، بنابراین توابع بی نام نامیده می شوند. این توابع خارج از تابع  ask قابل استفاده نیستند زیرا که به متغیری منتسب نشده اند.


مقایسه بین Function Expression و Function Declaration

همانطور که قبلا توضیح داده شد از نظر ظاهری تفاوت بین تعریف توابع با دو قاعده نوشتاری به شکل زیر است :

 Function Declaration : در این مورد یک تابع به عنوان یک عبارت جداگانه تعریف می شود :

// Function Declaration
function sum(a, b) {
  return a + b;
}

Function Expression : در این مورد یک تابع در داخل یک عبارت دیگر تعریف می‌شود :

// Function Expression
let sum = function(a, b) {
  return a + b;
};

یک Function Expression زمانی که اجرا به آن میرسد ایجاد شده و سپس بعد از آن قابل استفاده است اما Function Declaration متفاوت است.

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

sayHi("John"); // Hello, John

function sayHi(name) {
  alert( `Hello, ${name}` );
}

اما در مورد Function Expression این مورد صحیح نیست :

sayHi("John"); // error!

let sayHi = function(name) {  // (*) no magic any more
  alert( `Hello, ${name}` );
};

در مورد Function Declaration ها بلاکی که تابع در آن تعریف می‌شود اهمیت دارد و در خارج از آن بلاک نمی‌توان به تابع دسترسی داشت برای مثال :

let age = prompt("What is your age?", 18);

// conditionally declare a function
if (age < 18) {

  function welcome() {
    alert("Hello!");
  }

} else {

  function welcome() {
    alert("Greetings!");
  }

}

// ...use it later
welcome(); // Error: welcome is not defined

در مثال فوق در فراخوانی تابع welcome با خطا روبرو می‌شویم و این به این خاطر است که Function Declaration ها تنها در بلاک کدی که تعریف شده اند قابل استفاده اند. مثالی دیگر در این باره :

let age = 16; // take 16 as an example

if (age < 18) {
  welcome();               // \   (runs)
                           //  |
  function welcome() {     //  |
    alert("Hello!");       //  |  Function Declaration is available
  }                        //  |  everywhere in the block where it's declared
                           //  |
  welcome();               // /   (runs)

} else {

  function welcome() {     //  for age = 16, this "welcome" is never created
    alert("Greetings!");
  }
}

// Here we're out of curly braces,
// so we can not see Function Declarations made inside of them.

welcome(); // Error: welcome is not defined

همانطور که مشخص است دو فراخوانی اول تابع صحیح هستند اما فراخوانی سوم با خطا روبرو می شود. در این مورد برای صحیح کار کردن مثال فوق می توان از Function Expression به شکل زیر استفاده کرد :

let age = prompt("What is your age?", 18);

let welcome;

if (age < 18) {

  welcome = function() {
    alert("Hello!");
  };

} else {

  welcome = function() {
    alert("Greetings!");
  };

}

welcome(); // ok now

مثال فوق را با عملگر شرطی سه تایی می توان به صورت زیر بازنویسی کرد :

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
  function() { alert("Hello!"); } :
  function() { alert("Greetings!"); };

welcome(); // ok now

توابع فلش دار

قاعده ی نوشتاری دیگری برای توابع وجود دارد که هم ساده تر و هم مختصر تر است. این قاعده در اغلب اوقات از قاعده ی Function Expression بهتر است . این قاعده نوشتاری “arrow functions” تابع فلش دار نامیده می‌شود. قاعده نوشتاری آن به شکل زیر است :

let func = (arg1, arg2, ...argN) => expression

نمونه مثال بالا تابعی به نام func را ایجاد میکند که آرگومانهای آن عبارتند از  arg1..argN. سپس بدنه تابع بعد از فلش قرار می گیرد و نتیجه باز می گردد. به عبارت دیگر نمونه کد بالا عملکرد یکسان نسبت به کد زیر را دارد :

let func = function(arg1, arg2, ...argN) {
  return expression;
}

اما خیلی مختصر تر.

به مثال زیر توجه کنید :

let sum = (a, b) => a + b;

/* The arrow function is a shorter form of:

let sum = function(a, b) {
  return a + b;
};
*/

alert( sum(1, 2) ); // 3

اگر تنها یک آرگومان داشته باشیم می توان پرانتزها را نیز حذف کرد :

// same as
// let double = function(n) { return n * 2 }
let double = n => n * 2;

alert( double(3) ); // 6

اگر هیچ آرگومانی نداشته باشیم باید پرانتزها خالی نوشته شوند :

let sayHi = () => alert("Hello!");

sayHi();

از توابع فلش دار همانند توابع Function Expression می توان استفاده کرد. برای مثال بازنویسی مثالی که در فراخوانی ()welcome وجود داشت به شکل زیر است :

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
  () => alert('Hello') :
  () => alert("Greetings!");

welcome(); // ok now

توابع فلش دار چند خطی

گاهی اوقات بدنه توابع بیش از یک خط است ، در این صورت بدنه باید داخل { } قرار بگیرد و سپس نتیجه به صورت معمولی با کلمه return برگشت داده شود :
 

let sum = (a, b) => {  // the curly brace opens a multiline function
  let result = a + b;
  return result; // if we use curly braces, use return to get results
};

alert( sum(1, 2) ); // 3

 

منتشر شده در ۲۵ خرداد ۱۳۹۷ حمید رضا ملکی ۳۴۱۹ بازدید
دیدگاه ها
میلاد سه شنبه ۸ آبان ۱۳۹۷

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

سعید شنبه ۲۷ مرداد ۱۳۹۷

سلام سلام.... در قسمت فراخوانی توابع Callback در مثال اول من متوجه یک چیزی نمیشم. وقتی که تابع ask فراخوانی میشه، سه پارامتر question و yes و no به ترتیب با مقادیر "Do you agree?" و showOk و showCancel مقدار دهی میشوند و که showOk و showCancel هر دو توابعی تعریف شده هستند. سوال اول نباید هنگام ارسال تابع showOk و showCancel جلوشون پرانتزهای معرف تابع "()"را قرار داد؟ به عنوان مثال: ()ask("Do you agree?", showOk(), showCancel); سپس وقتی درون تابع ask متدهای yes() و no() را فراخوانی کردیم در بدنه تابع ask در خط if متدهای yes() و no() را فراخوانی کردیم نباید جلوی متد yes() هم همانند no() از ";" نقطه ویرگول استفاده کنیم؟

حمید رضا ملکی شنبه ۲۷ مرداد ۱۳۹۷

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

برای ارسال دیدگاه لازم است ابتدا وارد سایت شوید