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

۵۶ بازدید
آخرین به‌روزرسانی: ۰۷ شهریور ۱۴۰۲
زمان مطالعه: ۸ دقیقه
سازماندهی کد جاوا اسکریپت با توابع — راهنمای کاربردی

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

تعریف توابع

در این قسمت نحوه تعریف توابع را بیان کرده‌ایم.

برای تعریف کردن یک تابع، می‌توان به روش زیر عمل کرد:

1function add(a, b){
2  return a + b;
3}

تابع فوق دو عدد را با هم جمع کرده و نتیجه مجموع را بازگشت می‌دهد. a و b پارامترهایی هستند که امکان ارسال آرگومان به درون تابع و محاسبه و ترکیب کردن آن‌ها را می‌دهند. add نام تابع است که در جاوا اسکریپت اختیاری است. گزاره return اجازه می‌دهد که نتیجه تابع به متغیر دیگری ارسال شود یا به عنوان یک آرگومان به تابع دیگری فرستاده شود. البته این تنها روش تعریف کردن تابع نیست. روش دیگری نیز وجود دارد که به نام تابع arrow خوانده می‌شود:

1const add = (a, b) => a + b;

هر دو توابع فوق معادل هم هستند. با این وجود اگر لازم باشد this را در تابع دستکاری کنیم، در این صورت این دو معادل هم نخواهند بود، زیرا تابع arrow مقدار this را درون تابع تغییر نمی‌دهد و این وضع خلاف تابع تعریف شده با کلیدواژه function است. ما می‌توانیم تابع را به یک متغیر انتساب دهیم، زیرا تابع‌ها در جاوا اسکریپت شیء محسوب می‌شوند. توجه داشته باشید که تابع فوق تنها در صورتی کار می‌کند که در یک خط قرار داشته باشد، چون return به صورت ضمنی در صورتی کار می‌کند که تابع تنها در یک خط باشد. اگر بیش از یک خط باشد، در این حالت می‌توانیم از براکت به صورت زیر استفاده کنیم:

1const add = (a, b) => {
2  return a + b;
3}

در این روش باید گزاره return را به صورت صریح بنویسیم. در غیر این صورت یک مقدار بازگشت نخواهد داد.

روش سومی نیز برای نوشتن تابع وجود دارد که به صورت زیر است:

1const add = new Function('a', 'b', 'return a + b');

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

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

اگر به یک تابع ارجاع بدهیم و از آن استفاده کنیم، در این صورت تابع را فراخوانی (call) کرده‌ایم. برای فراخوانی یک تابع باید به صورت زیر عمل کنیم:

1add(1, 2) // 3

از آنجا که تابع ما یک مقدار بازگشت می‌دهد، می‌توانیم مقدار بازگشتی تابع add را در کنسول لاگ کنیم:

1console.log(add(1, 2)) // logs 3

همچنین می‌توانیم مقدار بازگشتی را در یک متغیر قرار دهیم:

1const sum = add(1, 2);
2console.log(sum) // logs 3

اجزای تابع

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

  • کلیدواژه function که اختیاری است.
  • نام تابع که اختیاری است.
  • پرانتز که برای هر نوع تابعی ضروری است.
  • پارامترهای درون پرانتز که اختیاری است.
  • براکت‌های آغازین و پایانی که برای همه تابع‌ها به جز تابع‌های تک‌خطی arrow الزامی است.
  • گزاره return که اختیاری است. اگر تابعی گزاره return نداشته باشد، مقدار undefined بازگشت می‌دهد. گزاره return که در پشت خود هیچ چیزی نداشته باشد، موجب خاتمه اجرای تابع می‌شود و از این رو برای کنترل کردن گردش کار تابع مفید است.

استفاده از آرگومان‌ها

چنان که دیدیم تابع‌های زیادی آرگومان دارند. آرگومان‌ها داده‌هایی هستند که به تابع ارسال می‌شوند تا مورد محاسبه قرار گیرند، بنابراین اگر یک فراخوانی تابع به صورت add(1, 2) داشته باشیم، در این صورت 1 و 2 آرگومان آن هستند. از سوی دیگر پارامترها چیزی هستند که درون پرانتزهای تابع در زمان تعریف کردن آن نوشته می‌شوند تا چیزهایی که می‌توان به تابع ارسال کرد مشخص شوند. ما مجبور نیستیم پارامترهای تابع را بنویسیم تا بتوانیم آرگومان‌هایی به آن ارسال کنیم، چون شیء arguments در هر تابعی وجود دارد. با این حال، توصیه شده که این کار انجام یابد، زیرا موجب روشن شدن چیزهایی که باید به تابع ارسال شوند می‌شود. اما می‌توانیم از arguments برای چیزهای اختیاری که می‌خواهیم به تابع ارسال شوند بهره بگیریم.

برای نمونه اگر به تابع add که در بخش فوق تعریف کردیم، بازگردیم:

1function add(a, b){
2  return a + b;
3}

a و b پارامترهای تابع هستند که در زمان تعریف شدن تابع می‌آیند. با این حال عموماً بهتر است بیش از 5 پارامتر برای تابع تعریف نکنیم، زیرا خواندن آن دشوار می‌شود.

ارسال آرگومان‌ها با مقدار

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

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

1const a = 1;
2const addOne = (num) => num + 1
3const b = addOne(a);
4console.log(b);

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

ارسال آرگومان‌ها با ارجاع

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

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

1let changeObj = (obj) => obj.foo = 'bar'
2const obj = {
3  foo: 'baz'
4}
5changeObj(obj);
6console.log(obj); // logs {foo: "bar"}

شیء obj اصلی به صورت { foo: 'baz'‎ } تعریف می‌شود. با این حال زمانی که obj درون تابع changeObj ارسال می‌شود، آرگومان obj در جا مورد تغییر قرار می‌گیرد. به این ترتیب شیء اصلی obj تغییر پیدا می‌کند. obj.foo به جای 'baz' به صورت 'bar' درمی‌آید، زیرا در ابتدا تعریف شده است.

آرگومان‌های مفقود

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

1function add(a, b){
2  return a + b;
3}

اگر add را بدون آرگومان دوم و با نوشتن add(1) فراخوانی کنیم در این صورت مقدار NaN به دست می‌آوریم، زیرا مقدار زیر یک عدد نیست:

1 + undefined

مقادیر پارامتر پیش‌فرض تابع

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

1function add(a, b = 1){
2  return a + b;
3}

در کد فوق، مقدار b را برابر با 1 قرار می‌دهیم و از این رو اگر هیچ چیزی برای b ارسال نکنیم، مقدار آن به صورت خودکار برابر با 1 تعیین می‌شود. بنابراین وقتی دستور add(1) را اجرا کنیم، به جای نتیجه NaN مقدار 2 به دست می‌آید.

یک روش قدیمی‌تر دیگر برای انجام این کار بررسی undefined بودن نوع پارامتر به صورت زیر است:

1function add(a, b){
2  if (typeof b === 'undefined'){
3    b = 1;
4  }
5  return a + b;
6}

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

فراخوانی توابع با آرگومان‌ها و پارامترهای بیشتر

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

اگر آرگومان‌ها را بدون گرفتن از شیء argument ارسال کنیم، پارامترها نادیده گرفته می‌شوند. همچنین با استفاده از شیء argument می‌توانید مواردی را که در پارامترها نیستند نیز گرفته و مورد استفاده قرار دهید. شیء argument پارامترهایی با کلیدهای عددی دارد که مانند اندیس‌های آرایه هستند.

برای نمونه اگر تابع add را با پارامترهای اضافی فراخوانی کنیم:

1function add(a, b){
2  console.log(arguments);
3  return a + b;
4}
5add(1,2,3)

نتیجه زیر به دست می‌آید:

0: 1
1: 2
2: 3

در فراخوانی console.log علاوه بر length که باید مقدار 3 لاگ شود، مقادیر فوق به نمایش درمی‌آیند.

دامنه متغیر در تابع‌ها

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

1function add(a, b){
2  let sum = a + b;
3  return sum;
4}

در این حالت، sum را داریم که تنها درون تابع در دسترس است زیرا با کلیدواژه let تعریف شده است.

تابع‌های بی‌نام

تابع‌های «بی‌نام» (Anonymous) توابعی هستند که نام ندارند. از آنجا که این توابع هیچ نامی ندارند، نمی‌توانند از جاهای دیگر مورد ارجاع قرار گیرند. این توابع عموماً به صورت تابع‌های callback به توابع دیگر ارسال می‌شوند و زمانی که تابع به یک آرگومان ارسال می‌شود، مورد فراخوانی قرار می‌گیرند. با این حال می‌توانید تابع‌های بی‌نام را به متغیرها نیز انتساب دهید تا به توابع نامدار تبدیل شوند.

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

1const sum = (function(a, b){
2  return a + b;
3})(1, 2);
4console.log(sum) // log 3

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

بازگشت

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

برای نوشتن یک تابع بازگشتی به صورت زیر عمل می‌کنیم:

1function sumOfSquares(num) {
2  let sum = Math.pow(num, 2);
3  if (num == 1) {
4    return 1
5  } else {
6    return sum + sumOfSquares(num - 1)
7  }
8  return sum
9}

در مثال فوق تابعی برای محاسبه مجموع مربعات یک عدد مفروض نوشته‌ایم. ما مربع num را محاسبه کرده و سپس اگر num برابر با 1 بود مقدار 1 بازمی‌گردانیم، در غیر این صورت مجموع sum به علاوه نتیجه فراخوانی sumOfSquares روی num — 1 بازگشت می‌یابد. بدین ترتیب با کاهش مقدار num به جایی می‌رسیم که حالت مبنا با مقدار 1 وجود دارد. در طی این فرایند نیز نتایج را با هم جمع می‌کنیم.

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

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

1function convertToChicken(name){
2  function getChickenName(name){
3    return `Chicken ${name}`;
4  }
5  return getChickenName(name)
6}

در این حالت، getChickenName را درون فراخوانی convertToChicken فرا می‌خوانیم. بنابراین اگر بنویسیم convertToChicken('chicken') نتیجه 'Chicken chicken' را به دست می‌آوریم، زیرا getChickenName را فراخوانی کرده‌ایم و نتیجه را بازگشت داده‌ایم. دامنه متغیرها یکسان است. let و const دارای دامنه بلوکی هستند و لذا نمی‌توانند خارج از تابع اصلی تعریف شوند اما در تابع تودرتو به آن‌ها دسترسی داریم لذا اگر کد زیر را داشته باشیم:

1function convertToChicken(name) {
2  let originalName = name;
3  function getChickenName(newName) {
4    console.log(originalName)
5    return `Chicken ${newName}`;
6  }
7  return getChickenName(name)
8}

در این صورت originalName همچنان درون console.log تعریف می‌شود.

تعریف کردن توابع در یک شیء

ما می‌توانیم به چند روش تابعی را در یک شیء تعریف کنیم.

می‌توانیم از کلیدواژه function یا تابع arrow استفاده کنیم، اما یک روش دیگر نیز این است که از اختصاری که برای کلیدواژه function وجود دارد استفاده کنیم. برای نمونه اگر یک شیء bird داشته باشیم و بخواهیم تابع chirp را درون آن بنویسیم می‌توانیم به صورت زیر عمل کنیم:

1const bird = {
2 chirp: function(){
3   console.log('chirp', this)
4  }
5}

همچنین می‌توانیم از اختصارنویسی زیر بهره بگیریم:

1const bird = {
2 chirp(){
3   console.log('chirp', this)
4  }
5}

هر دو تابع فوق یکسان هستند، زیرا تابع chrip شیء bird را به عنوان مقدار this دارد.

از سوی دیگر اگر از تابع arrow استفاده کنیم:

1const bird = {
2 chirp: ()=>{
3   console.log('chirp', this)
4  }
5}

This به عنوان شیء سراسری window لاگ می‌شود، چون تابع‌های arrow مقدار this را برابر با شیئی که تابع در آن قرار دارد تغییر نمی‌دهند.

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *