من میدانم که هیچ نمیدانم.
بررسی توابع کلوژر (Closure) در جاوااسکریپت
سلام دوستان. یک موضوع مهم و کاربردی توی جاوا اسکریپت وجود داره و معمولاً توی مصاحبهها هم پرسیده میشه کلوژر یا Closure هست که امروز مفصل با اون آشنا میشیم.
کلوژر چیه؟ ?
کلوژر (Closure) چیزی نیست جز یک تابع معمولی که داخل یک تابع دیگه تعریف و return
میشه:
function outer() {
return function inner() {
}
}
توی این کد تابع inner
یک کلوژر هست و به این صورت استفاده میشه:
function outer() {
return function inner() {
alert("Hi");
}
}
const x = outer();
x(); //Hi
توی خط ۷ تابع outer
رو اجرا کردیم و خروجی اون که تابع inner
هست رو ریختیم توی متغیر x
. پس الان مقدار متغیر x
یک تابع هست و باید مثل یک تابع فراخونی بشه. مثل خط ۸.
اما خب تعریف کردن یک تابع داخل یک تابع دیگه چه مزایایی داره؟ کلوژر دو مشکل رو حل میکنه:
۱. پایداری اطلاعات
۲. امنیت اطلاعات
۱. پایداری اطلاعات یعنی چی؟ ?
کد زیر رو در نظر بگیرین. فرض کنیم یک تابع داریم که تعداد کلیکها رو میخواد بشماره:
function clicked() {
let counter = 0;
counter++;
return counter;
}
alert(clicked()); // 1
alert(clicked()); // 1
alert(clicked()); // 1
ما توی خطهای آخر ۳ بار این تابع رو اجرا کردیم و انتظار داریم شمارنده عدد 3 رو به ما نشون بده. اما با اجرای این کد میبینیم که شمارنده ۳ بار عدد 1 رو نشون داد. چرا؟
⚠ با تموم شدن کار یک تابع، متغیرهایی که توی توابع وجود دارن هم از بین میرن. همچنین هر بار که تابع رو صدا میزنیم، متغیرهایی فقط مخصوص همون فراخونی ساخته میشن.
اینجا به محض اینکه تابع clicked
فراخونی میشه، یک متغیر counter
مخصوص همون فراخونی تولید میشه و به محض تموم شدن کار تابع، اون متغیر از بین میره.
توی فراخونی اول (خط ۸) یک counter
اختصاصی تولید شد و وقتی کار تابع به پایان رسید از بین رفت. همین قضیه برای فراخونیهای خط ۹ و ۱۰ هم صدق میکنه. این یعنی متغیر counter
پایدار نیست.
ادامه رو بخونید. شاید این کار رو بتونیم با ساختن متغیرهای سراسری حل کنیم!
۲. امنیت اطلاعات یعنی چی؟ ?
حالا کد زیر رو در نظر بگیرید. متغیر counter
رو بیرون و توی گلوبال اسکوپ تعریف کردیم:
let counter = 0;
function clicked() {
counter++;
}
clicked();
clicked();
clicked();
alert(counter); // 3
همونطور که میدونیم متغیرهایی که توی گلوبال اسکوپ تعریف میشن، از زمان شروع اجرای برنامه تا آخر، زنده و توسط همه اسکوپهای داخلی قابل دسترسی هستن. توی این کد، هر بار که تابع clicked
اجرا بشه، مقدار متغیر counter
تغییر میکنه و خروجی مد نظر ما برامون نمایش داده میشه.
تا اینجا همه چیز خوب پیش میره. حالا میخوایم یک تابع دیگه بسازیم که تعداد دابلکلیکها رو میشماره:
let counter = 0;
function clicked() {
counter++;
}
function dblClicked() {
counter++;
}
clicked();
clicked();
clicked();
dblClicked();
dblClicked();
alert(counter); // 5
تابع dblClicked
هم (بدون اینکه از این نکته آگاه باشه که یک تابع دیگه داره از counter
استفاده میکنه) وابسته به متغیر counter
هست. یعنی تغییری که تابع clicked
به counter
میده، روی خروجی تابع dblClicked
هم تأثیر میذاره! ما ۳ بار تابع clicked
رو صدا زدیم و انتظار داریم که مقدار counter
عدد 3 باشه. اما 5 هست!
متغیر counter
به دلیل اینکه تو بیرونیترین قسمت برنامه تعریف شده، از همه جای برنامه قابل دسترسی و تغییر هست و بنابراین نمیتونه شامل مقدار قابل اعتمادی باشه. یعنی هر قسمتی از برنامه (مثل دو تابع بالا) هر وقت که دلشون بخواد میتونن مقدار این متغیر رو تغییر بدن. این یعنی متغیر counter
امن و قابل اتکاء نیست.
پس راه حل چیه؟ چطوری میتونیم متغیرهایی داشته باشیم که هم امن هستن و هم پایدار؟ جواب استفاده از کلوژرهاست ?
و اما دوباره به این پرسش میرسیم که کلوژر چیه؟ ?
همونطور که گفتیم کلوژر تابعی هست که توی یک تابع دیگه تعریف و return
میشه:
function outer() {
const msg = "Hello"
return function inner() {
}
}
اینجا inner
یک تابع داخلی هست که به اون میگیم کلوژر. خاصیت توابع داخلی اینه که میتونن به اعضای تابع بیرونی دسترسی داشته باشن. اینجا تابع inner
به متغیر تابع بیرونی یعنی msg
دسترسی داره.
خب این به چه دردی میخوره؟
بالاتر گفتیم که وقتی کار یک تابع به پایان میرسه، اعضای اون هم از بین میرن. اما این اعضا برای کلوژر همچنان قابل دسترس هست! به بیان سادهتر، تابع بیرونی هنگام خداحافظی، میراث خودش رو برای تابع داخلی به جا میذاره و تابع داخلی میتونه راهِ تابع بیرونی رو ادامه بده:
function outer() {
const msg = "Hello"
return function inner() {
alert(msg);
}
}
const show = outer();
show(); // Hello
show(); // Hello
اینجا توی خط ۹ تابع outer
اجرا شد و قبل از خداحافظی تابعی رو return
کرد. الان مقدار متغیر show
همون تابع inner
هست. هر بار که show
رو مثل یک تابع صدا میزنیم، میبینیم که Hello
برای ما نمایش داده میشه. پس متغیر msg
هنوز زنده هست با این نکته که کار تابع outer
قبلاً به پایان رسیده!
گفتیم که متغیر msg
هنوز زنده هست. اما به دلیل اینکه داخل تابع تعریف شده، از بیرون قابل دسترسی نیست:
function outer() {
const msg = "Hello"
return function inner() {
alert(msg);
}
}
const show = outer();
show(); // Hello
alert(msg); // Error: msg is not defined
پس متغیر msg
هم پایداری داره و هم امنیت ?
بیاین مثال شمارنده رو با کلوژرها بنویسیم:
function makeCounter() {
let counter = 0;
return function () {
return ++counter;
}
}
const clicked = makeCounter();
alert(clicked()); // 1
alert(clicked()); // 2
alert(clicked()); // 3
همونطور که میبینیم با هر بار فراخونی تابع clicked
یک واحد به counter
اضافه شد و در نتیجه 1 و 2 و 3 به ما نمایش داده میشه. متغیر counter
فقط برای تابع (متغیر) clicked
ساخته شده و همچنین از بیرون قابل تغییر نیست. یعنی فقط و فقط تابع clicked
میتونه مقدار اون رو تغییر بده.
با هر بار فراخونی تابع makeCounter
، یک متغیر counter
و یک کلوژر اختصاصی درست میشه. پس با این کد خیلی راحت میتونیم یک شمارنده دیگه درست کنیم:
function makeCounter() {
let counter = 0;
return function () {
return ++counter;
}
}
const clicked = makeCounter();
alert(clicked()); // 1
alert(clicked()); // 2
const dblClicked = makeCounter();
alert(dblClicked()); // 1
alert(dblClicked()); // 2
توی خط ۱۳ یک شمارنده دیگه ساختیم. با اجرای این کد همونطور که میبینیم هر دو شمارنده، counter
های اختصاصی خودشون رو دارن که هیچ کس جز خودشون نمیتونن مقدار اون رو تغییر بدن.
یک مثال دیگه
فرض کنیم میخوایم با پیامهای مختلفی مثل Welcome و یا Hello به کاربرها خوشآمد بگیم:
function greeting(type) {
return function (user) {
alert(`${type} ${user}! This is our application.`);
}
}
const welcome = greeting('Welcome');
welcome('Mario'); // Welcome Mario! This is our application.
welcome('Emily'); // Welcome Emily! This is our application.
const hello = greeting('Hello');
hello('Mario'); // Hello Mario! This is our application.
hello('Emily'); // Hello Emily! This is our application.
خب دوستان با کلوژر آشنا شدیم و دیدیم که میتونیم با اون پایداری و امنیت اطلاعات داشته باشیم. هر وقت نیاز به اطلاعاتی پایدار داشتین اما موضوع امنیت اطلاعات مطرح بود، میتونین از کلوژرها استفاده کنین ??
مطلبی دیگر از این انتشارات
پلتفرمی آزمایشی در بازار رمز ارزها!
مطلبی دیگر از این انتشارات
نسل سوم وب چیست و قرار است چه کند؟ (بخش دوم)
مطلبی دیگر از این انتشارات
توکن سوزی چیست و چگونه اتفاق میافتد؟