آموزش Closure در جاوا اسکریپت

مبحثی به نام Closure در جاوا اسکریپت وجود دارد که در ورژن 6 آن ارائه شده و در این مقاله قصد داریم در مورد این مبحث صحبت کنیم.

اما برای درک مفهوم و کاربرد Closure باید با مبحثی به اسم Scope آشنا باشید. پس بریم که باهاش آشنا بشیم.

 

 Scope چیست؟

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

برای بررسی مبحث Closure در جاوا اسکریپت ابتدا باید مبحثی به نام Scope را بررسی کنیم.

مبحث Scope فقط به جاوا اسکریپت محدود نمی شود و در بقیه زبان ها نیز مطرح هست.

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

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

function showX () {
var x = 10
console.log(x)
}

showX() // 10
console.log(x) // Error

 

همان طور که در کد بالا مشاهده می کنید، با کال کردن(فراخوانی) فانکشن showX مقدار متغیر x برای کاربر log گرفته می شود اما موقع استفاده از متغیر x خارج از فانکشن showX، بهمون خطای undefined داده و این به این معنی هست که متغیر x در خط 7 ناشناخته هست. در این تکه کد scope یا همان منطقه استفاده متغیر x فقط داخل فانکشن showX است و خارج از showX به هیچ وجه نمی توان از متغیر x استفاده کرد.

در این قسمت به ناحیه متغیر x ناحیه local گفته می شود(local scope).

این فقط یک مثال برای local scope بود که شما با این موضوع آشنا بشوید و ناحیه های local دیگری مثل حلقه for نیز وجود دارند.

خب این از loal scope، بریم ببینیم scope دیگه ای هم وجود داره یا نه.

 

آموزش رایگان جاوا اسکریپت

 

Global Scope

خب الان فرض کنید در سند Js پروژتون یک متغیری تحت عنوان y تعریف کردید و تعریف این متغیر شما داخل هیچ حلقه، شرط و فانکشنی انجام نشده.

به دلیل این که متغیر y داخل هیچ ناحیه ای تعریف نشده و به هیچ ناحیه ای محدود نیست، یک متغیر global (سراسری) محسوب می شود که scope آن global نامیده می شود(global scope). متغیر y شما می تواند در سر تا سر سند Js پروژه شما قابل استفاده باشد.

شما می توانید از متغیر y داخل تمامی فانکشن ها، حلقه ها، شرط ها و … استفاده کنین.

برای درک بهتر این موضوع به کد زیر دقت کنید:

var y = 20

function showY () {
console.log(y)
}

showY() // 20
console.log(y) // 20

 

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

سراسری (global) است که در اصطلاح global scope گفته می شود.

تا این قسمت از مقاله آموزش Closure در جاوا اسکریپت با دو مفهوم global scope و local scope آشنا شدیم، حالا بریم یه مثال خوب ببینیم.

 

یه مثال خوب

خب فرض کنید قصد دارید یک متغیر تحت عنوان counter با مقدار اولیه 1 و یک فانکشن با اسم addCounter داشته باشید که وظیفه فانکشن افزایش یک واحد به متغیر counter باشد:

var counter = 0

function addCounter () {
counter++
console.log(counter)
}

console.log(counter) // 0
addCounter() // 1
addCounter() // 2
console.log(counter) // 2

 

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

با توجه به کد بالا برنامه ما بدون هیچ خطایی کار می کند و عملکرد مورد نظر ما (افزایش یک واحدی متغیر counter) رو به راحتی انجام می دهد.

اما با توجه به خط های 8 و 11 در خارج از فانکشن نیز قابل استفاده است و حتی می توان خارج از فانکشن نیز مقدار آن را تغییر داد، چون متغیر y در global scope تعریف شده است.

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

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

 

راه حل…

خب همون طور که گفتیم یه راه حل میتونه این باشه که متغیر counter رو داخل addCounter تعریف کنیم تا در خارج از آن در دسترس نباشد:

function addCounter () {
var counter = 0
counter++
console.log(counter)
}

// console.log(counter) // Error
addCounter() // 1
addCounter() // 1
addCounter() // 1
console.log(counter) // Error

خب خب…

همان طور که در کد بالا مشاهده می کنید به دلیل این که متغیر counter به صورت local تعریف شده و فقط به ناحیه فانکشن addCounter محدود است، با هر دفعه کال کردن این فانکشن کد های آن اجرا می شوند؛

پس با هر دفعه اجرای فانکشن متغیر counter یک بار با مقدار اولیه 0 تعریف میشود، مقدار آن ++ می شود و سپس log گرفته می شود و به این ترتیب با هر تعداد فراخوانی فانکشن addCounter مقدار متغیر counter همچنان 1 خواهد بود.

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

خب حالا راه حل این چیه؟ الان بریم سراغ خود Closure

Closure چیست؟

کلوژر در جاوا اسکریپت

خب مبحث Closure اومده تا این مشکل ما رو برطرف کنه. در صورت کلی Closure فانکشنی هست که داخل یک فانکشن دیگه تعریف و return میشه.

خب الان بریم عملکرد مد نظرمون رو با Closure بنویسیم و نتیجه رو ببینیم.

function makeAdd() {
    var counter = 0;
 
    function addCounter() {
        counter++;
        
        console.log(counter)
    }
 
    return addCounter;
}

var plusCounter = makeAdd()

plusCounter()
plusCounter()
plusCounter()

خب خب …

داخل فانکشن makeAdd یک فانکشن دیگه addCounter رو return کردیم؛

پس با هر دفعه اجرای فانکشن makeAdd، فانکشن addCounter را برای ما برمیگرداند و ما به addCounter دسترسی داریم.

از طرفی چون که متغیر counter داخل فانکشن makeAdd تعریف شده است، فقط داخل همین فانکشن در دسترس است و addCounter نیز به آن دسترسی دارد.

خروجی فانکشن makeAdd را که همان فانکشن addCounter است داخل متغیری به اسم plusCounter ذخیره می کنیم.

حالا مقدار متغیر plusCounter همان فانکشن addCounter می باشد. با اجرای هر دفعه plusCounter (یا همان addCounter) یک واحد به متغیر counter اضافه شده و آن لاگ گرفته می شود و به این ترتیب در هر دفعه اجرای آن مقادیر 1، 2 و 3 لاگ گرفته می شوند.

به این ترتیب Closure مشکل های موجود رو حل کرد و عملکرد مورد نظر ما به درستی اجرا شد.

در مورد Clusure به کد زیر هم دقت کنید:

function makeLog () {
    return function getLog () {
        console.log('Log')
    }
}

makeLog()() // Log
makeLog()() // Log
makeLog()() // Log

یک کوژر می تواند به این صورت هم قابل استفاده باشد. شما با پرانتز اولی فانکشن والد رو کال کرده و با پرانتز دومی کلوژری که return میشه رو کال می کنید.

 

سخن پایانی

مبحث Closure در جاوا اسکریپت یکی از مباحث پیشرفته محسوب می شود که در اکثر مصاحبه های کاری از آن سوال پرسیده می شود.

برای درک عمیق و بهتر این مبحث، ویدئوی مربوط به این مقاله رو هم حتما مشاهده کنید و اگر پیشنهاد، انتقاد یا سوالی داشتین در قسمت کامنت ها بپرسید.

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

دیدگاهتان را بنویسید