در جاوااسکریپت توابع ساختار جادویی ندارند بلکه تنهای نوع خاصی از value ها هستند.
syntax ی که قبلا برای تعریف تابع ازش استفاده میکردیم که Function Declaration نام داره:
۱ ۲ ۳ |
function sayHi() { alert( "Hello" ); } |
syntax دیگه ای به نام Function Expression برای تعریف تابع وجود داره:
۱ ۲ ۳ |
let sayHi = function() { alert( "Hello" ); }; |
اینجا تابع ساخته شده مثل متغیر های دیگه در یه متغیر ریخته شده .
مهم نیست که چجوری تابع تعریف شده فقط یک value رو توی متغیر sayHi ریختیم.
در اصل این دو یکسان هستند.
ما یه تابع رو تعریف کردیم و اونو توی یه متغیر sayHi ریختیم.
حتی میتونیم مقدارش رو با alert نمایش بدیم.
۱ ۲ ۳ ۴ ۵ |
function sayHi() { alert( "Hello" ); } alert( sayHi ); // shows the function code |
دقت کنید که خط آخر تابع رو اجرا نمیکنه چون که بعد sayHi پرانتزی وجود ندارد.
در زبان های برنامه نویسی دیگر هر جا که تابع نوشته شده باشه اون تابع اجرا میشه اما میبینیم تو جاوااسکریپت اینجوری نیست.
تو جاوااسکریپت تابع یه value هست بنابراین میتونیم به عنوان یه متغیر باهاش باهاش کنار بیاییم.
در کد بالا بجای اجرای تابع کد های تابع نمایش داده میشه.
مطمنا یک تابع یه value ویژه ای هست بنابر این میتونیم اون رو با اضافه کردن پرانتز اجرا کنیم sayHi()
.
اما هنوز یه value هست و میتونیم باهاش مثل value های دیگه کار کنیم.
میتونیم یه تابع رو توی یه متغیر دیگه کپی کنیم:
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ |
function sayHi() { // (۱) create alert( "Hello" ); } let func = sayHi; // (۲) copy func(); // Hello // (3) run the copy (it works)! sayHi(); // Hello // this still works too (why wouldn't it) |
توضیحات اتفاقاتی که در کد پالا میافته:
- ایجاد تابع Function Declaration و ریختن در متغیری به نام sayHi.
- کپی کردن در متغیر func در قسمت دوم. به یاد داشته باشید که بعد از sayHi هیچ پرانتزی وجود ندارد.
اگر پرانتز داشته باشه نتیجه تابع sayHi توی func ریخته میشه نه خود تابع sayHi. - حالا میتونیم دو تا تابع رو فراخوانی کنیم.
دقت داشته باشید ما برای تعریف sayHi میتونستیم تابع را به صورت Function Expression تعریف کنیم:
۱ ۲ ۳ ۴ ۵ ۶ |
let sayHi = function() { alert( "Hello" ); }; let func = sayHi; // ... |
همه چی به همون صورت کار میکنه.
چرا باید ; رو در انتها بذاریم ؟
احتمالا تعجب میکنید ، چرا Function Expression باید ; داشته باشه اما Function Declaration نه:
۱ ۲ ۳ ۴ ۵ ۶ ۷ |
function sayHi() { // ... } let sayHi = function() { // ... }; |
جواب ساده س:
- چون بلاک های کد نیازی به ; ندارند مثلا {}if یا {}for و یا {}function f .
- در یک Function Expression ما عملا داریم یه چیزی رو توی یه متغیر میریزیم.
قبلتر گفتیم که نیازه در پایان دستورات توصیه میشه که ; رو استفاده کنیم.
بنابر این ; مربوط به تابع نیست مربوط به انتهای دستور هست.
توابع Callback
نمونه های بیشتری که توابع به عنوان value ها(Function Expression) به تابعی ارسال شدند.
ما تابع ask(question, yes, no) رو با سه پارامتر نوشتیم:
question
متن سوال
yes
تابعی که اگر جواب سوال yes باشد اجرا میشود.
no
تابعی که اگر جواب سوال 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); |
در عمل چنین تابع های مفید هستن.
توابع بالا طول عمر طولانی دارند و به همین خاطر این روش توصیه نمیشه.
بهتره که توی پارامتر های ورودی توابع را anonymous تعریف کنیم.
آرگومان های showOk
، showCancel
را توابع callback میگوییم.
یه ایده خوبیه وقتی ما یه تابعی رو به تابع دیگه ارسال میکنیم تا در صورت لزوم اجرا بشه.
تو این مثال اگه جواب yes باشه showOk
اجرا میشه و اگر no باشه showCancel
اجرا میشه.
ما میتونی توابع رو به صورت Function Expressions کوتاهتر بنویسیم.
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۱۰ |
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."); } ); |
در اینجا ما توابع رو درست در داخل فراخوانی تابع ask(…) به صورت توابع بدون نام( anonymous) تعریف کردیم.
چنین توابعی رو بیرون از تابع ask بهش دسترسی نداریم.
دقیقا این چیزیه که ما میخواهیم.
چنین کدی در اسکریپت ما طبیعی به نظر میرسه . این روح جاوا اسکریپته .
یه تابع یه value هست که یه عمل رو ارائه میده.
value های منظم مانند strings و numbers که داده ها رو نشون میدن.
یک تابع رو میشه به عنوان یه عمل درک کرد.
میتونیم اونها رو بین متغیر ها ارسال کنیم و هر جا خواستیم اون رو اجرا کنیم.
Function Expression در مقابل Function Declaration
بیایید تفاوت های عمده شون رو ببینیم.
اول ، syntax:نحوه کد نویسی این دو.
Function Declaration: به عنوان دستور جدا در کد اصلی تعریف میشه.
۱ ۲ ۳ ۴ |
// Function Declaration function sum(a, b) { return a + b; } |
Function Expression:به عنوان یه تابع بدون نام تعریف و در یک متغیر ریخته میشه.
۱ ۲ ۳ ۴ |
// Function Expression let sum = function(a, b) { return a + b; }; |
تفاوت عمده اینه که این توابع رو موتور جاوااسکریپت چجوری ایجاد میکنه.
زمانی که به اجرای تابع میرسه تابع Expression ساخته میشه و در اون لحظه استفاده میشه.
زمانی که ما تابع رو مینویسیم و در یک متغیر میریزیم تا زمانی که اجرا نشه تابع ساخته نمیشه.
ابتدا باید تابع نوشته بشه و توی متغیر ریخته بشه بعد از اون متغیر برای فراخوانی تابع استفاده کنیم.
اما این توی Function Declarations متفاوت هست.
یک Function Declaration رو میشه قبلتر از تعریف تابع فراخوانی کرد.
یک تابع Declaration رو همه جای اسکریپت در دسترس هست مهم نیست که اون رو کجا نوشتیم.
این یه الگوریتم داخلی هست.
جاوااسکریپت قبل از اجرای اسکریپت ابتدا توابع Declarations رو پیدا میکنه و اونها رو ایجاد میکنه.
میتونیم تو تصوراتمون اسمش و بذاریم مرحله آماده سازی.
بعد از اون اسکریپت اجرا میشه و به این توابع دسترسی داریم.
برای درک بهتر مثال زیر رو ببنیم:
۱ ۲ ۳ ۴ ۵ |
sayHi("John"); // Hello, John function sayHi(name) { alert( `Hello, ${name}` ); } |
تابع قبلتر از تعریف فراخوانی شده .
اما اگه تابع Expression باشه این کار نمیکنه:
۱ ۲ ۳ ۴ ۵ |
sayHi("John"); // error! let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` ); }; |
یکی دیگه از ویژگی های Function Declarations دامنه بلاک(block scope) هست.
در حالت strict mode یک Function Declaration در داخل {} میتونیم ازش استفاده کنیم. بیرون از اون نه.
اگر از تابع Declaration استفاده کنیم بیرون از block کار نمیکنه:
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۱۰ ۱۱ ۱۲ ۱۳ ۱۴ ۱۵ ۱۶ ۱۷ ۱۸ ۱۹ |
let age = prompt("What is your age?", ۱۸); // conditionally declare a function if (age < ۱۸) { function welcome() { alert("Hello!"); } } else { function welcome() { alert("Greetings!"); } } // ...use it later welcome(); // Error: welcome is not defined |
یه نمونه دیگه :
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۱۰ ۱۱ ۱۲ ۱۳ ۱۴ ۱۵ ۱۶ ۱۷ ۱۸ ۱۹ ۲۰ ۲۱ ۲۲ |
let age = ۱۶; // take 16 as an example if (age < ۱۸) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs) } else { function welcome() { 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 |
برای این مشکل باید چیکار کنیم ؟
درست ترین راه حلش اینه که از توابع Expression استفاده کنیم.
ابتدا متغیر welcome
رو تعریف کنیم بعد توی شرط ها مقدار های متفاوتی بهش بدیم یا ساده تر اینکه تابع رو هرجوری که میخواییم بنویسیم.
اینجوری دیگه بیرون از کد به welcome
دسترسی داریم طبق شرط تابع اجرا میشه.
تغییرات نسبت به کد قبلی:
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۱۰ ۱۱ ۱۲ ۱۳ ۱۴ ۱۵ ۱۶ ۱۷ ۱۸ ۱۹ |
let age = prompt("What is your age?", ۱۸); let welcome; if (age < ۱۸) { welcome = function() { alert("Hello!"); }; } else { welcome = function() { alert("Greetings!"); }; } welcome(); // ok now |
یا به طور ساده تر میتونیم از if خطی (؟) استفاده کنیم:
۱ ۲ ۳ ۴ ۵ ۶ ۷ |
let age = prompt("What is your age?", ۱۸); let welcome = (age < ۱۸) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); }; welcome(); // ok now |
چه وقتی توابع Declaration را در مقابل توابع Expression انتخاب کنیم؟
خلاصه
- توابع value های هستن که میتونیم در هر کجا خواستیم اوننا رو کپی یا تعریف کنیم.
- اگر توابع رو به عنوان یه عبارت جداگانه با واژه function شروع بشه تعریف کنیم اون رو Function Declaration می نامیم.
- اگر تابع به عنوان بخشی از عبارت تعریف و در یک متغیر ریخته بشه رو Function Expression مینامیم.
- Function Declaration ها قبل از اجرای کد اجرا شده و در هر جایی از کد فراخوانی بشن اجرا میشن.
- Function Expression زمانی که اجرا شوند ساخته میشن.