mikoloism
mikoloism
خواندن ۷ دقیقه·۲ سال پیش

تابع های با پارامتر ممنوع!

تابع های با پارامتر ممنوع!
تابع های با پارامتر ممنوع!


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

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


قرارداد تعیین میکنه که تابع «پارامتر های X, Y و Z» رو دریافت کنه و پاسخ R رو برگردونه.

و نکته قابل توجه این هست که این قرارداد همواره تغییر میکنه.


مشکل کجاست؟

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


تعریف تابع greet نسخه ۱
تعریف تابع greet نسخه ۱


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

بعد از تقریبا مدت زیادی که ما از این تابع با استفاده میکنیم، متوجه میشیم که به دریافت نام‌خانوادگی به صورت جداگانه هم نیاز داریم تا پیام خوش‌آمدی گویی ما کامل‌تر بشه. چون در خیلی از کشور ها و فرهنگ ها نام‌خانوادگی قبل‌تر از نام کوچک هست به همین دلیل ما ابتدا نام رو از طریق `firstName`، نام‌خانوادگی رو با کمک lastName در تابع دریافت میکنیم و یه متغیر از نوع Boolean لازم داریم که به ما وضعیت ترتیب نام و نام‌خانوادگی رو مشخص میکنه که ما اون رو isLastNameFirst (آیا نام خانوادگی اول میاد؟) نامگذاری و مقدار پیشفرض اون رو false قرار میدیم.

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


تابع greet نسخه ۲
تابع greet نسخه ۲


خب در داخل تابع بالا ما شرطی گذاشتیم که اگر پارامتر دوم یعنی lastName به تابع ارسال نشد به روش قبلی عمل کنه و در غیر این صورت مشخص بشه که ترتیب نام و نام‌خانوادگی به چه شکل هست.

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


شبیه سازی استفاده از تابع greet
شبیه سازی استفاده از تابع greet


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

مشکل خیلی بزرگ و دردسر سازی نیست، چرا که ما در بیشتر محیط های توسعه (IDE) و ویرایشگر های کد (Code Editors) میتونیم به وسیله autocompletion اون رو تشخیص بدیم.


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

و حالا ترتیب پارامتر ها .... بهتر هست راجع بهش حرفی نزنیم.

به هر حال بریم برای اضافه کردن پارامتر های جدید به تابع، و این یعنی باید تابع رو به شکل زیر تعریف کنیم :


تابع greet نسخه ۳
تابع greet نسخه ۳


خب از اون جهت که ما با خروجی کاری نداریم، پس با ساختار داخلی تابع هم کاری نداریم و صرفا با پارامتر ها (Parameters) و ورودی ها سروکار داریم، بنابراین، از تغییر داخل تابع صرف نظر میکنیم.

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


شبیه سازی تابع greet نسخه ۲
شبیه سازی تابع greet نسخه ۲


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


شبیه سازی تابع greet نسخه ۳
شبیه سازی تابع greet نسخه ۳


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

و اما راه حل

همه این مشکل به راحتی حل میشه اگه ما یه آبجکت (object) رو به عنوان پارامتر تابع استفاده کنیم و از ویژگی object destructing در جاوا اسکریپت استفاده کنیم.

قبل از اینکه بخواهیم شروع کنم خیلی خلاصه به object destructing (ساختار شکنی) نگاهی بندازیم :


ساختارشکنی object ها در جاوا اسکریپت
ساختارشکنی object ها در جاوا اسکریپت


حالا وقت اینه که مثال خودمون رو تکرار کنیم.

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


تعریف تابع greet همراه با interface نسخه ۴
تعریف تابع greet همراه با interface نسخه ۴


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


نسخه ۵
نسخه ۵


و حالا طبق مراحل قبل ما مقادیر «نام میانی» و «پیشوند» هم اضافه میکنیم :

نظرتون چیه؟ بهتر نشد؟!

اما خب، معایبش چی؟

انعطاف پذیری

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

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

به عنوان مثال، اگر ما برای lastName و isLastNameFirst پیوندی ایجاد کردیم، باید این پیوند داخل قراردادی که برای تابع تعریف کردیم هم منعکس بشه. مثل این روش نمونه (یا هر روش دیگه‌ای) :

پارامتر های Rest

البته باید قبلش ذکر کنم که این استفاده از این روش خیلی ویژه است.

در اصل، قابلیت rest parameters به ما این اجازه رو میده تا به بینهایت آرگومان به شکل آرایه دسترسی داشته باشیم. مثلا تابع sum(...numbersToSum) یا اگر ما بخواهیم تابعی داشته باشیم که تمامی اعدادی که به اون ارسال میشه رو جمع کنه و برگردونه باید از این ساختارشکنی در آرایه ها استفاده کنیم.


کارایی و بهینه سازی

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

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

و نکته دوم اینکه، برای اطمینان بیشتر، بهتر هست که امتحانش کنیم و مشاهده کنیم که تفاوت چقدر زیاد هست (البته اگر وجود داشته باشه)

من برای این کار از ابزار PerfLink استفاده میکنم تا برای مقایسه روش پارامتر های ترتیبی و پارمتر های نامگذاری شده در یک حلقه از تکرار نتایج مشخص شود، این هم نتایج : (ops/s بخوانید عملیات در ثانیه)

  • در 10,000 بار فراخوانی : 1,698 ops/s برای روش ترتیبی (بدون آبجکت) در مقایسه با 1,339 ops/s برای روش نامگذاری (ارسال آبجکت برای هر بار فراخوانی) که ۲۲٪ تفاوت ایجاد میکنه
  • در 100,000 بار فراخوانی : 252 ops/s برای روش ترتیبی در مقایسه با 233 ops/s در روش نامگذاری تنها ۸٪ تفاوت
  • در 1,000,000 بار فراخوانی : 33 ops/s در روش ترتیبی در مقابل 32 ops/s در روش نامگذاری میشود ۳٪ تفاوت
  • در 10,000,000 بار فراخوانی : 2 ops/s روش ترتیبی در مقابل 2 ops/s روش نامگذاری که هیچ تفاوتی ندارد

اما برداشت من از این موضوع (لطفا اگر اشتباه میکنم، تصحیح کنید) :

هر چه عملکرد برنامه فشرده‌تر باشه، تفاوت خیلی کمی رو ایجاد میکنه که نشون دهنده این هست که تاثیر ساختن شئ در برنامه یک عدد ناچیز به شمار میاد.


اما در نهایت نتیجه میگیریم که

استفاده از روش پارامتر های ترتیبی :

  • خوانایی کمتری داره
  • قابلیت نگهداری کمتری داره
  • احتمال خطا رو افزایش میده

استفاده از روش تک پارامتری و ساختارشکنی :

  • واضح و خواناتر هست
  • نسخه پذیری بهتری داره (میشه با شیوه های قبل هم کار کرد)
  • به دلیل خوانا بودن، احتمال خطا رو هم کاهش میده


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

خوش باشید!




«رایانامه» | «تلگرام» | «گیت هاب» | «کدپن»




برگرفته از مقاله : "Stop Using Function Parameters Now" و یه مقاله دیگه‌ای که متاسفانه لینکش رو فراموش کردم!! :|

جاوا اسکریپتjavascriptتایپ اسکریپتtypescript
هدیه‌ی نوشتن...
شاید از این پست‌ها خوشتان بیاید