توابع یکی از مهم ترین بلوک های سازنده کد در جاوا اسکریپت هستند.

توابع شامل مجموعه ای از دستورالعمل ها هستند و معمولاً یک کار خاص را انجام می دهند (مثلاً جمع اعداد، محاسبه ریشه و غیره).

کد قرار داده شده در یک تابع تنها پس از فراخوانی صریح آن تابع اجرا خواهد شد.

اعلام عملکرد

1.نحو:

// تابع اعلان تابع FunctionName(var1, var2)( کد تابع ) // فراخوانی تابع FunctionName(var1,var2);

2. نحو:

//اعلان یک تابع var functionname=function(var1, var2)(کد تابع) // فراخوانی تابع نام تابع(var1,var2);

نام تابعنام تابع را مشخص می کند. هر تابع در صفحه باید یک نام منحصر به فرد داشته باشد. نام تابع باید با حروف لاتین مشخص شود و نباید با اعداد شروع شود.

در هر 1و در هر 2متغیرها یا مقادیری هستند که می توانند در داخل یک تابع ارسال شوند. تعداد نامحدودی از متغیرها را می توان به هر تابع ارسال کرد.

توجه داشته باشید:حتی اگر هیچ متغیری به تابع ارسال نشده باشد، فراموش نکنید که پرانتز "()" را بعد از نام تابع درج کنید.

توجه داشته باشید:نام توابع در جاوا اسکریپت به حروف بزرگ و کوچک حساس هستند.

مثال تابع جاوا اسکریپت

تابع messageWrite() در مثال زیر تنها پس از کلیک روی دکمه اجرا می شود.

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

انتقال متغیرها به توابع

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

توجه داشته باشید:تمام دستکاری‌ها روی متغیرها در داخل توابع در واقع نه بر روی خود متغیرها، بلکه روی کپی آنها انجام می‌شود، بنابراین محتوای خود متغیرها در نتیجه اجرای توابع تغییر نمی‌کند.

/* تابعی را تعریف کنید که 10 را به متغیر پاس شده اضافه کرده و نتیجه را به صفحه */ تابع plus(a) (a=a+10; document.write("خروجی تابع: " + a+" می دهد.
"); ) var a=25; document.write("مقدار متغیر قبل از فراخوانی تابع: "+a+"
")؛ // تابعی را که به آن متغییر ارسال می کند، plus(a) صدا بزنید؛ document.write("مقدار متغیر پس از فراخوانی تابع: "+a+"
");

مشاهده سریع

برای دسترسی به یک متغیر سراسری از یک تابع به جای کپی از آن، استفاده کنید window.variable_name.

تابع plus(a)( window.a=a+10; ) var a=25; document.write("مقدار متغیر قبل از فراخوانی تابع: "+a+"
"); plus(a); document.write("مقدار متغیر پس از فراخوانی تابع: "+a+"
");

مشاهده سریع

دستور بازگشت

با دستور برگشتمی توانید مقادیر را از توابع برگردانید.

مشاهده سریع

توابع داخلی

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

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

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("این یک رشته است")+"
");

مشاهده سریع

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

متغیرهای محلی و جهانی

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

پس از اجرای کد تابع، چنین متغیرهایی از بین می روند. این بدان معناست که متغیرهایی با نام یکسان را می توان در توابع مختلف تعریف کرد.

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

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

متغیرهای سراسری تنها زمانی از بین می روند که صفحه بسته شود.

مشاهده سریع

توجه داشته باشید:هنگامی که نمایش داده می شود، var2 null خواهد بود زیرا func1 در "نسخه" محلی var2 عمل می کند.

استفاده از توابع ناشناس

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

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

تابع arrMap(arr,func)(var res=آرایه جدید؛ برای (var i=0;i ");

مشاهده سریع

خودتان آن را انجام دهید

تمرین 1. خطاهای کد زیر را برطرف کنید:

تمرین 1

خطای کد را برطرف کنید.

وظیفه 2.

  1. کد توابع 1-3 را با بررسی رفتار آنها هنگام عبور پارامترهای مختلف بازتولید کنید.
  2. کلمه کلیدی را با تعامل با تابع 4 تعیین کنید.

وظیفه 2

//اولین تابع مخفی را فراخوانی کنید document.write(secfunc1(4,12) + "
"); // فراخوانی دومین تابع مخفی document.write(secfunc2(100,10) + "
")؛ //سومین تابع مخفی را فراخوانی کنید secfunc3(23,10); document.write("
")؛ // فراخوانی تابع مخفی چهارم secfunc4("p");

لطفاً جاوا اسکریپت را برای استفاده از سیستم نظردهی Disqus فعال کنید.

مردم فکر می کنند که علم کامپیوتر یک هنر برای نوابغ است. در واقعیت، برعکس است - فقط بسیاری از مردم کارهایی را انجام می دهند که روی هم قرار می گیرند، گویی دیواری از سنگریزه های کوچک درست می کنند.

دونالد کنوت

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

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

یک فرد بالغ روسی زبان به طور متوسط ​​حدود 10000 کلمه می داند. یک زبان برنامه نویسی کمیاب شامل 10000 دستور داخلی است. و واژگان یک زبان برنامه نویسی با وضوح بیشتری تعریف شده است، بنابراین انعطاف پذیری کمتری نسبت به زبان انسانی دارد. بنابراین، معمولاً برای جلوگیری از تکرار بی مورد، باید کلمات خود را به آن اضافه کنیم.

تعریف تابع

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

var Square = تابع (x) ( بازگشت x * x; ); console.log(square(12)); // → 144

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

یک تابع ممکن است چندین پارامتر یا اصلاً نداشته باشد. در مثال زیر، makeNoise لیستی از پارامترها ندارد، در حالی که power دارای دو پارامتر است:

Var makeNoise = function() ( console.log("Fuck!"); ); makeNoise(); // → مزخرف! قدرت var = تابع (پایه، توان) (نتیجه var = 1؛ برای (تعداد var = 0؛ تعداد< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

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

پارامترها و دامنه

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

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

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

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

var x = "خارج"; var f1 = function() ( var x = "inside f1"; ); f1(); ورود به سیستم کنسول (x); // → var f2 = function() ( x = "inside f2"; ); f2(); ورود به سیستم کنسول (x); // → داخل f2

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

دامنه های تو در تو

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

به عنوان مثال، تابع نسبتاً بی‌معنی زیر حاوی دو تابع دیگر است:

var landscape = function() (var result = ""; var flat = function(size) ( for (var count = 0; count< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

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

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

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

var چیزی = 1; ( var something = 2; // کاری را با متغیر چیزی انجام دهید... ) // خروج از بلوک...

اما چیزی در داخل بلوک همان متغیر خارجی است. اگرچه چنین بلوک هایی مجاز هستند، اما استفاده از آنها فقط برای دستورات if و حلقه ها منطقی است.

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

به عنوان ارزش عمل می کند

نام توابع معمولاً به عنوان نام یک قطعه از یک برنامه استفاده می شود. چنین متغیری یک بار تنظیم می شود و تغییر نمی کند. بنابراین به راحتی می توان یک تابع را با نام آن اشتباه گرفت.

اما اینها دو چیز متفاوت هستند. فراخوانی تابع را می توان مانند یک متغیر ساده استفاده کرد - برای مثال، آنها را می توان در هر عبارتی استفاده کرد. امکان ذخیره فراخوانی تابع در یک متغیر جدید، ارسال آن به عنوان پارامتر به تابع دیگر و غیره وجود دارد. همچنین، متغیری که فراخوانی تابع را ذخیره می کند، یک متغیر معمولی باقی می ماند و مقدار آن قابل تغییر است:

Var launchMissiles = تابع (مقدار) ( ‎missileSystem. launch("لطفا!"); if (safeMode) launchMissiles = function(value) (/* release */);

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

اعلام عملکرد

یک نسخه کوتاه تر از عبارت "var Square = تابع ..." وجود دارد. کلمه کلیدی تابع را می توان در ابتدای یک عبارت استفاده کرد:

تابع مربع (x) (برگردان x * x؛ )

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

Console.log("آینده می گوید:"، future()); تابع future() ( بازگشت "ما هنوز هیچ ماشین پرنده ای نداریم."؛)

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

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

مثال تابع () ( تابع a() () // هنجار اگر (چیزی) ( تابع b() () // Ay-yy-yy! ) )

پشته تماس
نگاهی دقیق تر به نحوه عملکرد دستور اجرا با توابع مفید است. در اینجا یک برنامه ساده با چند فراخوانی تابع وجود دارد:

تابع greet(who) ( console.log("Hi, " + who); ) greet("Semyon"); console.log ("Pokeda");

چیزی شبیه به این پردازش می شود: فراخوانی greet باعث می شود که پاس به ابتدای تابع بپرد. تابع داخلی console.log را فراخوانی می کند که کنترل را در دست می گیرد، کار خود را انجام می دهد و کنترل را برمی گرداند. سپس به آخر سلام می رسد، و به جایی که از آنجا خوانده شده برمی گردد. خط بعدی دوباره console.log را فراخوانی می کند.

به صورت شماتیک، این را می توان به صورت زیر نشان داد:

Top greet console.log سلام top console.log بالا

از آنجا که تابع باید به جایی که از آن فراخوانی شده است برگردد، کامپیوتر باید زمینه ای را که تابع از آن فراخوانی شده است به خاطر بسپارد. در یک مورد، console.log باید به حالت سلام برگردد. در دیگری به انتهای برنامه برمی گردد.

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

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

تابع chicken() ( return egg(); ) function egg() ( return chicken(); ) console.log(chicken() + " first first."); // → ??

استدلال های اختیاری
کد زیر کاملا قانونی است و بدون مشکل اجرا می شود:

هشدار ("سلام"، "عصر بخیر"، "سلام به همه!");

به طور رسمی، تابع یک آرگومان می گیرد. با این حال، هنگامی که این گونه به چالش کشیده می شود، او شکایت نمی کند. بقیه استدلال ها را نادیده می گیرد و «سلام» را نشان می دهد.

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

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

نکته مثبت این است که می توانید توابعی ایجاد کنید که آرگومان های اختیاری را دریافت کنند. به عنوان مثال، در نسخه بعدی تابع توان، می توان آن را با دو و یک آرگومان فراخوانی کرد - در حالت دوم، توان برابر با دو خواهد بود و تابع مانند یک مربع عمل می کند.

توان تابع (پایه، توان) (اگر (شار == ​​تعریف نشده) توان = 2؛ نتیجه var = 1؛ برای (تعداد var = 0؛ تعداد< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

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

Console.log("R"، 2، "D"، 2); // → R 2 D 2

بسته شدن

توانایی استفاده از فراخوانی تابع به عنوان متغیر، همراه با این واقعیت که هر بار که یک تابع فراخوانی می شود، متغیرهای محلی دوباره ایجاد می شوند، ما را به نکته جالبی می رساند. وقتی یک تابع از کار بیفتد چه اتفاقی برای متغیرهای محلی می افتد؟

مثال زیر این موضوع را نشان می دهد. تابع wrapValue را اعلام می کند که یک متغیر محلی ایجاد می کند. سپس تابعی را برمی گرداند که آن متغیر محلی را می خواند و مقدار آن را برمی گرداند.

تابع wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); ورود به سیستم کنسول (wrap1()); // → 1 console.log(wrap2()); // → 2

این معتبر است و همانطور که باید کار می کند - دسترسی به متغیر باقی می ماند. علاوه بر این، چندین نمونه از یک متغیر می‌توانند به طور همزمان وجود داشته باشند، و این واقعیت را تأیید می‌کند که متغیرهای محلی با هر فراخوانی تابع دوباره ایجاد می‌شوند.

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

با کمی تغییر، مثال خود را به تابعی تبدیل می کنیم که اعداد را در هر عدد داده شده ضرب می کند.

ضریب تابع (ضریب) ( تابع بازگشتی (عدد) (عدد بازگشتی * ضریب؛ ) var double = ضرب (2); console.log(twice(5)); // → 10

یک متغیر جداگانه مانند localVariable از مثال wrapValue دیگر مورد نیاز نیست. از آنجایی که پارامتر خود یک متغیر محلی است.

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

در مثال ما، ضریب یک قطعه کد منجمد شده را که در متغیر double ذخیره می کنیم، برمی گرداند. آخرین خط تابع موجود در متغیر را فراخوانی می کند که کد ذخیره شده را فعال می کند (شماره بازگشت * فاکتور؛). همچنان به متغیر فاکتوری که هنگام فراخوانی ضریب تعریف شده بود دسترسی دارد و همچنین به آرگومان ارسال شده در زمان unfreeze (5) به عنوان پارامتر عددی دسترسی دارد.

بازگشت

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

تابع قدرت(پایه، توان) (اگر (نما == 0) برگرداند 1؛ در غیر این صورت پایه * توان (پایه، توان - 1) را برگرداند؛ ) console.log(power(2, 3)); // → 8

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

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

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

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

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

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

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

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

در اینجا یک معما برای شما وجود دارد: می توانید تعداد نامتناهی اعداد را به دست آورید که با عدد 1 شروع می شوند و سپس 5 را با هم جمع می کنیم یا در 3 ضرب می کنیم. جمع و ضرب هایی که به عدد معینی منتهی می شود؟ برای مثال می توان عدد 13 را با ضرب 1 در 3 و سپس دوبار جمع کردن 5 بدست آورد. و عدد 15 به طور کلی غیرممکن است که به آن دست پیدا کنید.

راه حل بازگشتی:

تابع findSolution(target) ( تابع find(شروع، تاریخچه) (اگر (شروع == هدف) تاریخچه را برمی گرداند؛ در غیر این صورت اگر (شروع > هدف) را برگرداند null؛ در غیر این صورت بازگشت find(start + 5، "(" + history + " + 5)") || find(start * 3, "(" + history + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

این مثال لزوما کوتاه ترین راه حل را پیدا نمی کند - هر کدام را برآورده می کند. من انتظار ندارم که شما فوراً نحوه عملکرد برنامه را درک کنید. اما بیایید به انتهای این تمرین عالی در تفکر بازگشتی بپردازیم.

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

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

برای درک بهتر نحوه دستیابی تابع به اثر مورد نظر، اجازه دهید به فراخوانی های آن که در جستجوی راه حلی برای عدد 13 رخ می دهد نگاه کنیم.

Find(1, "1") find(6, "(1 + 5)") find(11, "((1 + 5) + 5)") find(16, "((1 + 5) + 5 (( 3، "(1 * 3)") find(8، "((1 * 3) + 5)") find(13، "(((1 * 3) + 5) + 5)") یافت شد!

تورفتگی عمق پشته تماس را نشان می دهد. اولین باری که تابع Find دوبار خود را فراخوانی می کند تا راه حل هایی را که با (1 + 5) و (1 * 3) شروع می شوند بررسی کند. اولین فراخوانی به دنبال راه حلی می گردد که با (1 + 5) شروع می شود و از بازگشت برای بررسی همه راه حل هایی که عددی کمتر یا مساوی با عدد مورد نظر به دست می دهند، استفاده می کند. آن را پیدا نمی کند و null برمی گرداند. سپس اپراتور || و به فراخوانی تابعی می‌پرد که گزینه (1 * 3) را بررسی می‌کند. در اینجا ما خوش شانس هستیم، زیرا در سومین فراخوانی بازگشتی 13 دریافت می کنیم. این فراخوانی یک رشته را برمی گرداند و هر یک از || این رشته را در طول مسیر از بالا عبور می دهد و در نتیجه راه حل را برمی گرداند.

توابع رشد

دو روش کم و بیش طبیعی برای معرفی توابع به یک برنامه وجود دارد.

ابتدا کدهای مشابه را چندین بار می نویسید. از این کار باید اجتناب شود - کد بیشتر به معنای فضای بیشتر برای خطاها و مطالب خواندن بیشتر برای کسانی است که سعی در درک برنامه دارند. بنابراین ما یک عملکرد تکرارشونده را انتخاب می کنیم، آن را مطابقت دهیم اسم قشنگی داریو آن را در یک تابع قرار دهید.

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

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

007 گاو 011 مرغ

بدیهی است که ما به یک تابع با دو آرگومان نیاز داریم. بیایید شروع به کدنویسی کنیم.
// printFarmInventory تابع printFarmInventory(گاو، مرغ) ( var cowString = String(cows)؛ while (cowString.length< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

اگر .length را به یک رشته اضافه کنیم، طول آن را بدست می آوریم. معلوم می شود که حلقه های while صفرهای ابتدایی را به اعداد اضافه می کنند تا زمانی که رشته ای از 3 کاراکتر به دست آورند.

آماده! اما به محض ارسال کد برای کشاورز (البته به همراه یک چک سنگین)، او زنگ می‌زند و به ما می‌گوید که در مزرعه خوک دارد و آیا می‌توانیم خروجی تعداد خوک‌ها را اضافه کنیم. به برنامه؟

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

// outputZeroPaddedWithLabel تابع printZeroPaddedWithLabel(number, label) ( var numberString = String(number)؛ while (numberString.length< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

آثار! اما نام printZeroPaddedWithLabel کمی عجیب است. این سه چیز - خروجی، لایه صفر و یک برچسب - را در یک تابع ترکیب می کند. به جای پر کردن کل قطعه تکراری در یک تابع، بیایید یک مفهوم را برجسته کنیم:

// اضافه کردن تابع Zeros zeroPad(عدد، عرض) ( var string = string(number)؛ while (string.length< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

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

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

یک قانون سرانگشتی خوب این است که فقط عملکردی را اضافه کنید که قطعاً به آن نیاز دارید. گاهی اوقات وسوسه انگیز است که برای هر نیاز کوچکی چارچوب هایی با هدف کلی ایجاد کنید. در مقابل او مقاومت کنید. شما هرگز کار را تمام نخواهید کرد، بلکه فقط یک سری کد بنویسید که هیچ کس از آن استفاده نکند.

توابع و عوارض جانبی

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

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

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

با این حال، نباید از نوشتن توابعی که کاملاً تمیز نیستند خجالتی بود یا پاکسازی مقدس کد را از چنین توابعی آغاز کرد. عوارض جانبی اغلب مفید هستند. راهی برای نوشتن وجود ندارد نسخه تمیزتابع console.log، و این تابع بسیار مفید است. بیان برخی از عملیات ها با استفاده از عوارض جانبی آسان تر است.

نتیجه

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

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

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

تمرینات

کمترین
در فصل قبل تابع Math.min ذکر شد که کوچکترین آرگومان های خود را برمی گرداند. حالا ما می توانیم چنین تابعی را خودمان بنویسیم. یک تابع min بنویسید که دو آرگومان را بگیرد و حداقل آنها را برگرداند.

Console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10

بازگشت
دیدیم که عملگر % (باقیمانده) می تواند برای تعیین زوج بودن یک عدد (% 2) استفاده شود. در اینجا روش دیگری برای تعیین وجود دارد:

صفر زوج است.
واحد فرد است.
هر عدد N برابر با N-2 است.

مطابق این قوانین یک تابع isEven بازگشتی بنویسید. باید یک عدد بگیرد و یک مقدار بولی برگرداند.

آن را روی 50 و 75 تست کنید. سعی کنید به آن -1 بدهید. چرا او این گونه رفتار می کند؟ میشه یه جوری درستش کرد؟

آن را روی 50 و 75 تست کنید. ببینید در -1 چگونه رفتار می کند. چرا؟ میتوانیراهی برای رفع این مشکل فکر می کنید؟

Console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

لوبیا را می شماریم.

کاراکتر شماره N یک رشته را می توان با اضافه کردن .charAt(N)("string".charAt(5)) به آن، به روشی مشابه به دست آوردن طول یک رشته با طول . بدست آورد. مقدار بازگشتی یک رشته کاراکتری خواهد بود (به عنوان مثال، "k"). اولین کاراکتر رشته دارای موقعیت 0 است، به این معنی که آخرین کاراکتر دارای string.length - 1 موقعیت است. به عبارت دیگر، یک رشته از دو کاراکتر دارای طول 2 است و موقعیت کاراکترهای آن 0 و 1 خواهد بود.

یک تابع countBs بنویسید که یک رشته را به عنوان آرگومان می گیرد و تعداد کاراکترهای "B" در رشته را برمی گرداند.

سپس یک تابع countChar بنویسید که بسیار شبیه به countB ها عمل می کند، با این تفاوت که یک پارامتر دوم را می گیرد، کاراکتری که ما در رشته به دنبال آن خواهیم بود (به جای اینکه فقط تعداد کاراکترهای "B" را بشماریم). برای انجام این کار، تابع countBs را بازنویسی کنید.

دستورات پرش و مدیریت استثنا

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

دستور return باعث می شود که مفسر از تابع فراخوانی شده به نقطه ای که در آن فراخوانی شده است بپرد و مقدار فراخوانی را برگرداند. دستور throw یک استثنا ایجاد می کند و طوری طراحی شده است که در ارتباط با دستورات try/catch/finally که بلوک را تعریف می کند کار کند. کد برنامهبرای رسیدگی به استثنا. این یک نوع نسبتاً پیچیده از دستورات پرش است: هنگامی که یک استثنا رخ می دهد، مفسر به نزدیکترین کنترل کننده استثنای محصور کننده می پرد، که ممکن است در همان تابع یا بالاتر، در پشته برگشتی تابع فراخوانی شده باشد.

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

برچسب های دستورالعمل

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

شناسه: دستورالعمل

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

با نام گذاری حلقه، می توان از آن در دستورات break و continue استفاده کرد، در داخل حلقه برای خروج از آن، یا برای پرش به ابتدای حلقه، به تکرار بعدی. عبارات break و continue تنها عباراتی در زبان جاوا اسکریپت هستند که می‌توانند حاوی برچسب‌ها باشند - بعداً با جزئیات بیشتر در مورد آنها بحث خواهیم کرد. در زیر نمونه ای از دستور while با یک برچسب و یک عبارت continue با استفاده از آن برچسب است:

حلقه اصلی: while (token != null) ( // کد برنامه حذف شد... ادامه حلقه اصلی؛ // رفتن به تکرار بعدی حلقه نامگذاری شده )

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

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

بیانیه شکست

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

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

Var arr = ["a"،"b"، "c"، "d"،"e"], result; برای (var i = 0; i

در جاوا اسکریپت مجاز است نام برچسب را بعد از آن مشخص کنید کلمه کلیدیشکستن (شناسه بدون کولون):

شکستن label_name;

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

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

دستور break با برچسب تنها زمانی مورد نیاز است که بخواهید اجرای دستوری را که نزدیکترین حلقه محصور کننده یا دستور سوئیچ نیست، شکست دهید.

ادامه بیانیه

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

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

    در حلقه while دوباره عبارت مشخص شده در ابتدای حلقه بررسی می شود و اگر درست باشد بدنه حلقه از ابتدا اجرا می شود.

    حلقه do/while به انتهای حلقه می‌پرد، جایی که قبل از تکرار حلقه، شرط دوباره بررسی می‌شود.

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

    در حلقه for/in، حلقه دوباره با انتساب موارد مشخص شده شروع می شود متغیر نامملک بعدی

به تفاوت رفتار دستور continue در حلقه‌های while و for توجه کنید. حلقه while مستقیماً به شرایط خود باز می گردد و برای حلقهابتدا عبارت افزایشی را ارزیابی می کند و سپس به شرط باز می گردد. مثال زیر استفاده از دستور ادامه بدون برچسب را برای خروج از تکرار فعلی یک حلقه برای اعداد زوج نشان می دهد:

مجموع var = 0; // محاسبه مجموع اعداد فرد از 0 - 10 برای (var i = 0; i

دستور continue، مانند break، می‌تواند در حلقه‌های تودرتو به شکلی استفاده شود که شامل یک برچسب باشد، در این صورت حلقه‌ای که مجدداً راه‌اندازی می‌شود، لزوماً حلقه‌ای نیست که بلافاصله حاوی دستور continue باشد. همچنین، مانند شکست، خطوط جدید بین کلمه کلیدی ادامه و نام برچسب مجاز نیستند.

بیانیه بازگشت

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

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

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

تابع myFun(arr) ( // اگر آرایه حاوی اعداد منفی است، تابع برای (var i = 0; i) را لغو کنید

بیانیه پرتاب

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

در جاوا اسکریپت، زمانی که یک خطای زمان اجرا رخ می دهد و زمانی که برنامه به طور صریح آن را با دستور throw مطرح می کند، استثنا ایجاد می شود. استثناها با استفاده از دستورات try/catch/finally که بعدا توضیح داده می‌شوند، شناسایی می‌شوند.

دستور throw دارای نحو زیر است:

بیان پرتاب;

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

// تابع فاکتوریل یک تابع عدد factorial(number) ( // اگر آرگومان ورودی نباشد مقدار معتبر، // یک استثنا مطرح شده است! if (شماره 1; i *= عدد، عدد--); /* بدنه حلقه خالی */ return i; ) کنسول log("5! = ", factorial(5)); log("-3! = ", factorial(-3));

هنگامی که یک استثنا پرتاب می شود، مفسر جاوا اسکریپت بلافاصله اجرای برنامه عادی را لغو می کند و به نزدیکترین کنترل کننده استثنا می پرد. کنترل کننده های استثنا از دستور catch ساخت try/catch/finally استفاده می کنند که در بخش بعدی توضیح داده شده است.

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

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

تلاش/گرفتن/در نهایت ساخت

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

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

قطعه زیر نحو و هدف ساخت try/catch/finally را نشان می دهد:

سعی کنید ( // به طور معمول، این کد از ابتدا تا انتها به آرامی اجرا می شود. // اما در برخی موارد، ممکن است یک استثنا در آن // یا به طور مستقیم با دستور throw یا غیرمستقیم، // با فراخوانی متدی که استثنا را پرتاب می کند. ) catch (ex) ( // عبارات این بلوک در صورتی اجرا می شوند که استثنایی در بلوک try // رخ دهد. این عبارات می توانند از یک متغیر محلی ex استفاده کنند که // به شی Error یا ارجاع می دهد. به مقدار دیگری که در دستور throw مشخص شده است. // این بلوک می تواند استثنا را به نحوی مدیریت کند، یا // آن را نادیده بگیرد و کار دیگری انجام دهد، یا // استثنا را با یک دستور throw دوباره پرتاب کند. ) در نهایت ( // This block حاوی عباراتی است که همیشه اجرا می شوند، صرف نظر از اینکه، // در بلوک try چه اتفاقی افتاده است، اگر بلوک try به پایان برسد اجرا می شوند: // 1) طبق معمول، رسیدن به انتهای بلوک // 2) به دلیل شکستن، ادامه یا عبارات را برگردانید // 3) با یک استثنا که توسط در بلوک catch در بالا // 4) با یک استثنا غیرقابل کشف که ادامه می یابد // به سطوح بالاتر انتشار می یابد)

توجه داشته باشید که کلمه کلیدی catch با یک شناسه در پرانتز دنبال می شود. این شناسه مشابه یک پارامتر تابع است. هنگامی که یک استثنا گرفته می شود، این پارامتر روی یک استثنا (به عنوان مثال، یک شی Error) تنظیم می شود. بر خلاف یک متغیر عادی، شناسه مرتبط با دستور catch فقط در بدنه بلوک catch وجود دارد.

مثال زیر یک مثال واقعی تر از یک ساختار try/catch است. متد factorial() تعریف شده در مثال قبلی و را فراخوانی می کند روش های سریع() و alert() JavaScript سمت کلاینت برای سازماندهی ورودی و خروجی:

سعی کنید ( // از کاربر یک عدد بخواهید var n = Number(prompt("یک عدد مثبت را وارد کنید"، "")); // فاکتوریل یک عدد را محاسبه کنید، با فرض اینکه // ورودی معتبر باشد var f = factorial( n)؛ // چاپ هشدار نتیجه (n + "! = " + f)؛ ) catch (ex) ( // اگر داده ها نادرست است، کنترل به اینجا منتقل می شود alert(ex); // به کاربر اطلاع دهید خطا )

اگر کاربر یک عدد منفی وارد کند، یک پیام هشدار نمایش داده می شود:

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

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

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

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

اپراتور برگشتتابع فعلی را خاتمه می دهد و مقدار آن را برمی گرداند.

کد منبع این مثال تعاملی در یک مخزن GitHub ذخیره می شود. اگر می خواهید در پروژه شرکت کنید نمونه های تعاملیلطفا https://github.com/mdn/interactive-examples را کلون کنید

نحو

بازگشت [[بیان]]; express عبارتی که مقدار آن برگردانده خواهد شد. اگر مشخص نشده باشد، undefined به جای آن برگردانده می شود.

شرح

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

تابع square(x) ( x * x; ) var demo = square(3); // مقدار دمو 9 خواهد بود

اگر مقدار بازگشتی مشخص نشده باشد، undefined به جای آن برگردانده می شود.

عبارات زیر همیشه اجرای یک تابع را خاتمه می دهند:

برگشت؛ بازگشت درست؛ بازگشت نادرست؛ بازگشت x; بازگشت x + y / 3;

نقطه ویرگول خودکار

تابع magic(x) ( function return calc(x) ( return x * 42 ); ) var answer = magic(); پاسخ(1337); // 56154

مشخصات فنی

مشخصات وضعیت اظهار نظر
ECMAScript نسخه اول (ECMA-262) استاندارد تعریف اصلی
ECMAScript 5.1 (ECMA-262)
استاندارد
ECMAScript 2015 (نسخه ششم، ECMA-262)
تعریف "بیانیه بازگشت" در این مشخصات.
استاندارد
آخرین پیش نویس ECMAScript (ECMA-262)
تعریف "بیانیه بازگشت" در این مشخصات.
پیش نویس

سازگاری مرورگر

جدول سازگاری در این صفحه از داده های ساخت یافته تولید می شود. اگر می‌خواهید در داده‌ها مشارکت کنید، لطفاً آن را از https://github.com/mdn/browser-compat-data مخزن بررسی کنید و برای تغییرات خود درخواستی برای ما ارسال کنید.

داده های سازگاری را در GitHub به روز کنید

کامپیوترهاسیارسرور
کرومحاشیه، غیرمتمرکزفایرفاکساینترنت اکسپلورراپراسافارینمای وب اندرویدکروم برای اندرویدفایرفاکس برای آندرویداپرا برای اندرویدسافاری در iOSاینترنت سامسونگNode.js
برگشتکروم حمایت کامل 1 حاشیه، غیرمتمرکز حمایت کامل 12 فایرفاکس حمایت کامل 1 IE حمایت کامل 3 اپرا حمایت کاملآرهسافاری حمایت کاملآرهوب ویو اندروید حمایت کامل 1 کروم اندروید حمایت کامل 18 فایرفاکس اندروید حمایت کامل 4 اپرا آندروید حمایت کاملآرهسافاری iOS حمایت کاملآرهاینترنت سامسونگ اندروید حمایت کامل 1.0 nodejs حمایت کاملآره