Closures در جاوا اسکریپت


“شما میتونید توضیح بدید که Closures در جاوا اسکریپت چی هست؟” شاید این یک سوال عجیب و غریب برای شروع یک مقاله باشه.شما چه برنامه نویس full-stack, frontend, backend با جاوا اسکریپت باشید یا اینکه هر کار دیگه ای با جاوا اسکریپت انجام میدید به احتمال زیاد تا حالا اسم و اصطلاح closure به گوشتون رسیده.اگر شما در حال مصاحبه برای گرفتن شغلی در حوزه برنامه نویسی با جاوا اسکریپت هستید به احتمال زیاد با سوالی مواجه خواهید شد که از شما می خواهند closure را توضیح دهید.
پاسخ دادن به سوال بالا بسیار ساده است.فقط کافی است تعریف closure را به خاطربسپارید و چندین مثال از آن را بیان کنید.
مشکل این اصطلاحات در درک تعریف closure نیست ، بلکه درک این موضوع است که چرا ممکن است بخواهید از آن در پروژه خود استفاده کنید.
قبل از اینکه بدانیم که closure در جاوا اسکریپت چقدر مهم است، اجازه دهید درک کنیم که closure چیست.
closure تابعی است که به scope “بیرونی” تعریف شده خود دسترسی دارد. بنابراین می تواند به مقادیر خارج از scope دسترسی پیدا کند ، حتی اگر تابع terminated شده باشد.
به مثال زیر توجه کنید
1 2 3 4 5 6 |
function takeOne() { let i = 0; return function incrementFunction() { return i++; } } |
قطعه کد بالا تابعی را نشان می دهد که یک تابع دیگر را رreturn می کند.با این حال ، پس از فراخوانی takeOne
و گرفتن incrementFunction ، incrementFunction متغیر محلی takeOne را به خاطر می آورد ، حتی اگر takeOne قبلاً terminated شده باشد.
مزایای closures
اولین فایده closure ، حفظ متغیرهای local در این محدوده است. از آنجا که توابع در جاوا اسکریپت first-class citizens هستند ، توسعه دهندگان غالباً با شباهت نامها روبرو می شوند و این باعث خروجی غیر منتظره خواهد شد. استفاده ازclosure می تواند به حفظ namespace در آن scope به عنوان یک متغیر خصوصی کمک کند.برای این مورد را می توانید یک قطعه کد از jQuery مشاهده کنید ، جایی که method کلیک را تعریف می کند.
1 2 3 4 5 6 |
$(function() { var selections = [] $(".something").click(function() { // this closure has access to the outer variable selections selections.push("something") // this are able to get the outer function selections }) }) |
اگرچه این در واقع یکی از موارد استفاده از closure است ، ممکن است شما را به فکر ببرد که ، “آیا این واقعاً هدف ازclosure این است؟” شما هنوز هم ممکن است این سؤال در ذهن شما باشد که مورد استفاده عمومی از closure چیست؟
دومین مزیت، که بیشتر مورد استفاده عمومی است ، استفاده در یک محیط asynchronous است.
1 2 3 |
for(var i = 0 ; i< 3; i++) { setTimeout(() => console.log(i), 3000) } |
خروجی قطعه کد بالا چیست؟
3 سه بار چاپ می شود. از آنجا که setTimeout ناهمزمان است ، با پایان یافتن حلقه ، scopeبیرونی i نیز به 3 تغییر یافته است ، و فراخوانی بعدی setTimeout در طول حلقه باعث می شود هر بار 3 چاپ شود.
چگونه این مسئله را حل کنیم؟
روش های متفاوتی برای حل این مسئله وجود داره،شامل استفاده از syntax let
به جای var که scope خودش را در محدوده block تعریف می کند و این مشکل حل می شود.با این وجود، اگر شما قصد داشته باشید این مشکل را بدون استفاداز ویژگی Es6 حل کنید، پاسخ شما closure است.
1 2 3 4 5 6 7 |
function printSomething(i) { setTimeout(() => console.log(i), 3000) } for(var i = 0; i<3; i++) { printSomething(i) } |
فقط با ایجاد یک تابع خارجی دیگر در خارج از حلقه و فراخوانی آن درون حلقه، شما در حال تعریف یک closure هستید. مقدار i حتی پس از خاتمه printSomething حفظ می شود. callback سپس 0 1 2 را بر روی کنسول چاپ می کند.
به همین دلیل closure یک ویژگی قدرتمند جاوا اسکریپت است. برای حفظ scope متغیر بیرونی در یک محیط ناهمزمان می توانید از closure استفاده کنید.
یک مثال دیگر از closure
بیایید مثال دیگری را تصور کنیم که در آن شما باید تابعی را ایجاد کنید که باید 3rd Api را فراخوانی کند و نتیجه را جمع کند و آن را به caller برگرداند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function getAPI(cb) { setTimeout(() => cb("a"), 3000) } function getAPIB(cb) { setTimeout(() => cb("b"), 2000) } function getAPIC(cb) { setTimeout(() => cb("c"), 1000) } function aggregateValue() { var aggregateData = [] // your implementation here } |
قبل از خواندن راه حل ، یک لحظه مکث کنید و به این فکر کنید که چگونه می توانید بدون promises و async/await این مسئله را حل کنید
ما می توانیم قclosure را برای حفظ دامنه تابع و متوقف کردن جمع value ها با استفاده از callbacks ، متوقف کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function aggregateValue(cb) { var aggregateData = [] var numberAPICalledSoFar = 0 function callback(value) { aggregateData = [...aggregateData, value] if(numberAPICalledSoFar < 2) { numberAPICalledSoFar++; }else { cb(aggregateData) } } getAPI(callback) getAPIB(callback) getAPIC(callback) } |
از آنجایی که همگی getAPI و getAPIB و getAPIC
از یک تابع callback استفاده می کنند، شما می توانید یک تابع callback ایجاد کنید که تعداد فراخوانی Api ها را افزایش دهد.زمانی که این تعداد بیشتر از 2 شد، callback value را return کنید.
- اگرچه closure در کلاس اول جاوا اسکریپت آموزش داده می شود ، اما آن یکی از ویژگیهای “پیشرفته” javaScript است.
- مشکلی که در مورد Closure وجود دارد ، مفهوم آن نیست ، بلکه فایده آن است ، و به همین دلیل شما نیاز به درک موارد استفاده آن دارید.
- موارد استفاده عمومی از Closure در حل محاسبه در یک محیط asynchronous است.
[منبع ]
دیدگاهتان را بنویسید