Closure در جاوا اسکریپت – آموزش کلوژر به زبان ساده
Closures یک مفهوم قدرتمند و مهم در جاوا اسکریپت است. کلوژر در جاوا اسکریپت به توابع داخلی این امکان را میدهد تا به متغیرهای بیرون از محدودهی خود دسترسی داشته باشند. به عبارت دیگر، کلوژر دامنهی متغیرهای خود را بسته و عملکرد بیرونی را حفظ خواهد کرد. در این مطلب به مفهوم Closure در جاوا اسکریپت و طرز کار آن پرداختهایم.
Closure در جاوا اسکریپت چیست؟
Closures را میتوان ترکیبی از تابع دانست که همراه با ارجاعات به وضعیت پیرامون خود بستهبندی شده است. وقتی که یک Closure در Javascript ایجاد میشود، به حوزهی تابع بیرونی دسترسی پیدا خواهید کرد. کلوژر در Javascript با هربار تعریف یک تابع (Function) در جاوا اسکریپت، ایجاد میشود.
برای درک بهتر مفهوم closure، تصور کنید یک تابع در جاوا اسکریپت دارید که در درون آن تابع، تابع دیگری تعریف شده باشد. تابع داخلی، میتواند به متغیرهایی که در تابع بیرونی تعریف شده است، دسترسی داشته باشد. حتی اگر اجرای تابع بیرونی به پایان رسیده باشد، تابع داخلی همچنان میتواند از متغیرهای تابع بیرونی استفاده کند. علت این امر به وجود کلوژر در جاوا اسکریپت بستگی دارد.
حوزهی واژگانی یا exical Scoping
حوزهی واژگانی به چگونگی تعیین و حل و فصل نامهای متغیرها توسط یک مفسر واقعی زمانی که توابع بهصورت تودرتو باشند، اشاره میکند. مکانی که یک متغیر درون کد منبع اعلام شده است، تعیین میکند که این متغیر در کدام قسمتهای کد قابل دسترس است. توابع تودرتو به متغیرهایی که حوزهی بیرونی آنها اعلام شدهاند، دسترسی دارند.
مثال: در قطعه کد زیر، استفادهی پایهای از Closure در جاوا اسکریپت را نشان دادهایم.
function foo() { let b = 1; function inner() { return b; } return inner; } let get_func_inner = foo(); console.log(get_func_inner()); console.log(get_func_inner()); console.log(get_func_inner());
خروجی:
1 1 1
در این مثال، یک تابع به نام foo داریم که درون آن یک متغیر به نام b تعریف شده است. همچنین درون foo یک تابع داخلی به نام inner داریم که b را چاپ میکند.
وقتی foo اجرا میشود، تابع inner را بازمیگرداند. پس از اینکه foo اجرای خود را تمام کرده است، inner همچنان میتواند به متغیر b دسترسی داشته باشد. این به دلیل وجود بسته (closure) است که حوزهٔ واژگانی foo را حفظ کرده است.
به عبارت دیگر، inner دسترسی به متغیرهای حوزهٔ تابع بیرونی خود را حتی پس از پایان اجرای آن تابع حفظ میکند. این خاصیت به عنوان حوزهٔ واژگانی (lexical scoping) شناخته میشود و یکی از ویژگیهای مهم و قدرتمند در جاوااسکریپت است.
بستهها به شما این امکان را میدهند که توابعی بسازید که بتوانند به متغیرهای تابع محصور کننده و همچنین متغیرهای سراسری دسترسی داشته باشند، بدون اینکه این متغیرها پس از پایان اجرای تابع بیرونی از بین بروند. این ویژگی به طور گسترده در برنامهنویسی جاوااسکریپت استفاده میشود و به شما اجازه میدهد تا کدهای پیچیده و قابل نگهداریتری بنویسید.
پیشنهاد مطالعه: آموزش آرایه چند بعدی در جاوا اسکریپت
ایجاد بستهها یا کلوژر در جاوا اسکریپت
در اینجا میخواهیم یک مثال برای ایجاد کلوژر در javascript ارائه دهیم.
function foo(outer_arg) { function inner(inner_arg) { return outer_arg + inner_arg; } return inner; } let get_func_inner = foo(5); console.log(get_func_inner(4)); console.log(get_func_inner(3));
خروجی مثال فوق به صورت زیر است:
9 8
مثال ۲: در اینجا میخواهیم مثالی از کلوژر در جاوا اسکریپت را درون یک حلقه نشان دهیم. در این کد، یک تابع بدون نام را در هر اندیکس از آرایه ذخیره خواهیم کرد. قطعه کد زیر را ببینید.
// Outer function function outer() { let arr = []; let i; for (i = 0; i < 4; i++) { // storing anonymous function arr[i] = function () { return i; } } // returning the array. return arr; } let get_arr = outer(); console.log(get_arr[0]()); console.log(get_arr[1]()); console.log(get_arr[2]()); console.log(get_arr[3]());
خروجی مثال فوق به صورت زیر است:
4 4 4 4
در کد مثال قبلی، وقتی آرایه functionsArray ایجاد شد، هر یک از توابع بینامی که در این آرایه ذخیره شده بودند، به متغیر i اشاره میکردند. مهم است که متوجه شویم که این توابع، مقدار i را به خاطر نمیسپارند، بلکه به متغیری که در حوزهٔ تابع بیرونی تعریف شده است، اشاره میکنند. به این معنی که هر تابع بینام در آرایه، مرجع متغیر i را نگه میدارد.
وقتی حلقه for اجرا میشود، مقدار i از 0 به 4 افزایش مییابد. هر بار که یک تابع بینام در آرایه ذخیره میشود، آن تابع به متغیر i اشاره میکند. بنابراین، وقتی تابع createArray به پایان میرسد و ما توابع داخل آرایه را اجرا میکنیم، همهٔ آنها به مقدار فعلی i که برابر 5 است، اشاره میکنند. نتیجه این است که هر پنج تابع بینام در آرایه، مقدار 5 را برمیگردانند.
برای رفع این مشکل و اطمینان از اینکه هر تابع بینام مقدار صحیح i را برگرداند، میتوانیم از یک تابع بلافاصله اجراشونده (Immediately Invoked Function Expression یا IIFE) استفاده کنیم. این کار به هر تابع بینام یک نسخه از مقدار i در زمان ایجاد آن میدهد.
برای اطمینان از اینکه هر تابع بینام (anonymous function) در آرایه مقادیر مختلف i را در ایندکسهای مختلف برمیگرداند، میتوانیم از یک تابع بلافاصله اجراشونده (Immediately Invoked Function Expression یا IIFE) استفاده کنیم. این کار به هر تابع یک نسخه از مقدار i در زمان ایجاد آن میدهد. در اینجا کد اصلاح شده را میبینید:
// Outer function function outer() { function create_Closure(val) { return function () { return val; } } let arr = []; let i; for (i = 0; i < 4; i++) { arr[i] = create_Closure(i); } return arr; } let get_arr = outer(); console.log(get_arr[0]()); console.log(get_arr[1]()); console.log(get_arr[2]()); console.log(get_arr[3]());
خروجی:
0 1 2 3
در کد بالا، ما آرگومان تابع create_Closure را با هر فراخوانی بهروزرسانی میکنیم. بنابراین، در ایندکسهای مختلف مقادیر متفاوتی از i دریافت میکنیم.
- نکته: ممکن است درک مفهوم بسته (closure) در ابتدا کمی دشوار باشد، اما با امتحان کردن کلوژرها در سناریوهای مختلف مانند ایجاد getter/setter، استفاده از callbackها و موارد دیگر، میتوانید بهتر آن را درک کنید.
پیشنهاد مطالعه: متد setInterval در جاوا اسکریپت
کاربردهای رایج Closure در جاوا اسکریپت
کلاوژر در جاوا اسکریپت، کاربردهای زیادی دارد. در ادامه در این خصوص صحبت کردهایم.
- حفظ وضعیت بین رویدادها: کلوژر در جاوا اسکریپت کمک میکنند تا بتوانید وضعیت (state) متغیرها را بین رویدادهای مختلف حفظ کنید. به عنوان مثال، در برنامههای جاوااسکریپت که با رویدادها (events) سر و کار دارند، میتوانید از بستهها استفاده کنید تا دادهها را بین رویدادهای مختلف ذخیره کنید بدون اینکه این دادهها در دسترس دیگر قسمتهای برنامه باشند.
- ایجاد متغیرهای خصوصی: با استفاده از بستهها، میتوانید متغیرهایی ایجاد کنید که فقط در دسترس یک تابع خاص یا گروهی از توابع باشند. این کار امنیت دادهها را افزایش میدهد و از تغییرات ناخواسته توسط دیگر قسمتهای کد جلوگیری میکند.
- مدیریت کالبکها و کدهای غیرهمزمان: در برنامهنویسی جاوااسکریپت، کالبکها و کدهای غیرهمزمان نقش مهمی دارند. بستهها کمک میکنند تا توابع کالبک به متغیرهای محیط اطراف خود دسترسی داشته باشند و به درستی با دادهها کار کنند، حتی زمانی که عملیات غیرهمزمان به پایان برسد.
این مفاهیم و کاربردها به شما کمک میکنند تا بستهها را بهتر درک کنید و از آنها به طور مؤثرتری در کدهای خود استفاده کنید. با تمرین و تجربه در سناریوهای مختلف، میتوانید به راحتی این مفهوم را در برنامههای خود پیادهسازی کنید.
مروری بر ویژگیهای خاص در جاوا اسکریپت
در اینجا قصد داریم تا به برخی از ویژگیهای خاص جاوا اسکریپت اشاره کرده و توضیح مختصری از آن را ارائه دهیم. آشنایی شما با این مفاهیم برای برنامهنویسی و کار با جاوا اسکریپت بسیار الزامی و مهم خواهد بود.
Hosting در جاوا اسکریپت
Hoisting در جاوا اسکریپت یک ویژگی است که به موجب آن، اعلان متغیرها و توابع به بالای محدوده (scope) خود منتقل میشوند، بهطوری که میتوانند قبل از جایی که در کد نوشته شدهاند، استفاده شوند. این بدان معناست که شما میتوانید از یک تابع یا متغیر قبل از اینکه در کد بهصراحت تعریف شده باشد، استفاده کنید.
Strict mode در جاوا اسکریپت
مود سختگیرانه (Strict Mode) در جاوااسکریپت یک ویژگی است که با اضافه کردن “use strict”; در بالای یک فایل اسکریپت یا تابع فعال میشود و باعث میشود کد در شرایط سختگیرانهتری اجرا شود. این حالت به رفع اشتباهات رایج در کدنویسی کمک میکند و برخی از رفتارهای غیرمستقیم یا مستعد خطا را تغییر میدهد.
برای مثال، در مود سختگیرانه نمیتوانید از متغیرهای اعلام نشده استفاده کنید، نوشتن بر روی خصوصیات فقط خواندنی (read-only) منجر به خطا میشود، و کلمات کلیدی رزرو شده نمیتوانند به عنوان نام متغیرها استفاده شوند. این ویژگی به توسعهدهندگان کمک میکند تا کدهای امنتر و قابل اعتمادتر بنویسند.
تابع سازنده در جاوا اسکریپت
تابع سازنده در جاوااسکریپت یک تابع ویژه است که برای ایجاد و مقداردهی اولیه اشیاء (objects) استفاده میشود. این توابع معمولاً با حرف بزرگ شروع میشوند و با استفاده از کلمه کلیدی new فراخوانی میشوند.
هنگامی که یک تابع سازنده با new فراخوانی میشود، یک شیء جدید ایجاد شده و بهعنوان this به تابع منتقل میشود. سپس میتوان خصوصیات و متدهای مختلفی را به این شیء اضافه کرد. در نهایت، شیء جدید بهطور خودکار بازگردانده میشود.
استفاده از توابع سازنده به برنامهنویسان امکان میدهد تا الگوهای شیءگرایی را پیادهسازی کنند و اشیاء مشابه با ساختار و رفتارهای مشابه ایجاد کنند.
Promise در جاوا اسکریپت
پرامیس (Promise) در جاوااسکریپت یک شیء است که نمایندهٔ یک عملیات غیرهمزمان میباشد و ممکن است در آینده به یکی از دو حالت تکمیل (fulfilled) یا شکست (rejected) برسد. یک پرامیس میتواند سه حالت داشته باشد: در حال انتظار (pending)، تکمیل شده (fulfilled) با نتیجه موفقیتآمیز، یا شکست خورده (rejected) با یک دلیل خطا.
با استفاده از متدهای then و catch، میتوان عملیاتهای مختلفی را تعریف کرد که پس از تکمیل یا شکست پرامیس اجرا شوند. پرامیسها به مدیریت بهتر و سادهتر کدهای غیرهمزمان کمک میکنند و به جلوگیری از مشکلاتی مانند “جهنم کالبکها” (callback hell) میپردازند، که در آن توابع کالبک تو در توی بسیاری استفاده میشود.
This در جاوا اسکریپت
this در جاوا اسکریپت یک کلمه کلیدی است که به شیء فعلی که در آن کد در حال اجرا است اشاره میکند و بستگی به نحوه فراخوانی تابع دارد. در یک شیء، this به خود شیء اشاره دارد. در یک تابع معمولی، this به شیء سراسری (window در مرورگرها) اشاره میکند، مگر اینکه در حالت strict mode باشد که در آن undefined است.
در توابع سازنده، this به شیء جدیدی که در حال ایجاد است اشاره دارد. در روشهای رویداد (event handlers)، this به شیءای که رویداد را راهاندازی کرده است اشاره میکند. همچنین با استفاده از متدهایی مانند call، apply و bind میتوان مقدار this را به طور صریح تنظیم کرد. این انعطافپذیری میتواند هم قدرتمند و هم گیجکننده باشد، بنابراین درک قواعد آن برای جلوگیری از اشتباهات رایج مهم است.
پیشنهاد مطالعه: آموزش کوکی در جاوا اسکریپت به زبان ساده
Prototype در جاوا اسکریپت
پروتوتایپ (Prototype) در جاوا اسکریپت یک مکانیزم است که به اشیاء امکان میدهد ویژگیها و متدها را از یک شیء دیگر به ارث ببرند. هر شیء در جاوااسکریپت یک پروتوتایپ دارد، که در واقع یک شیء دیگر است و میتوان ویژگیها و متدهای مشترک را در آن تعریف کرد.
زمانی که به یک ویژگی یا متد در یک شیء دسترسی پیدا میکنید، جاوااسکریپت ابتدا آن را در خود شیء جستجو میکند و اگر پیدا نشد، به پروتوتایپ آن شیء مراجعه میکند. این زنجیره جستجو تا جایی ادامه مییابد که به Object.prototype برسد که پایه همه اشیاء در جاوااسکریپت است.
استفاده از پروتوتایپها به صرفهجویی در حافظه و افزایش کارایی کمک میکند، زیرا ویژگیها و متدهای مشترک بین اشیاء مختلف فقط یک بار در پروتوتایپ تعریف میشوند.
آبجکت در جاوا اسکریپت
آبجکت یا Object در جاوا اسکریپت یک ساختار دادهای است که به شما اجازه میدهد مجموعهای از خواص (properties) و مقادیر (values) را ذخیره و مدیریت کنید. هر property یک کلید (key) و یک مقدار (value) دارد، که کلیدها معمولاً رشتهها هستند و مقادیر میتوانند هر نوع دادهای باشند، از جمله اعداد، رشتهها، توابع و حتی دیگر آبجکتها. آبجکتها به شما این امکان را میدهند که دادههای مرتبط را به صورت ساختاریافته و منظم سازماندهی کنید.
Life در جاوا اسکریپت
چرخه حیات در جاوااسکریپت شامل مراحلی است که در طول اجرای یک برنامه رخ میدهند. این مراحل شامل ایجاد، استفاده و از بین بردن متغیرها، توابع و اشیاء میشود. این مراحل، در فهرست زیر نشان داده شدهاند.
- بارگذاری و تفسیر یا Loading and Parsing
- اعلان و مقداردهی اولیه یا Declaration and Initialization
- اجرای کد یا Code Execution
- مدیریت حافظه یا Memory Management
- پایان برنامه یا Program Termination
Scope در جاوا اسکریپت
محدوده یا Scope در جاوا اسکریپت به محدودهای اشاره دارد که در آن میتوان به متغیرها، توابع و اشیاء دسترسی داشت. جاوا اسکریپت دارای دو نوع اصلی حوزه است: حوزه سراسری (Global Scope) و حوزه محلی (Local Scope).
- محدوده سراسری (Global Scope):
متغیرها و توابعی که در بیرون از هر تابع یا بلوک کد تعریف میشوند، در حوزه سراسری قرار دارند و در سراسر برنامه قابل دسترسی هستند.
- محدوده محلی (Local Scope):
متغیرها و توابعی که درون یک تابع یا بلوک کد تعریف میشوند، در حوزه محلی قرار دارند و فقط درون آن تابع یا بلوک قابل دسترسی هستند.
- محدوده کد (Block Scope):
متغیرهایی که با let و const تعریف میشوند، در محدوده بلوکی (block scope) قرار دارند، یعنی فقط درون بلوک کد {} که در آن تعریف شدهاند قابل دسترسی هستند. برخلاف var که حوزه تابعی (function scope) دارد.
- محدوده زنجیرهای (Scope Chain):
هنگامی که یک متغیر یا تابعی مورد دسترسی قرار میگیرد، جاوا اسکریپت ابتدا در حوزه فعلی به دنبال آن میگردد. اگر پیدا نشد، به حوزه بالاتر (محیط بالاتر) مراجعه میکند تا به حوزه سراسری برسد. این فرآیند به نام حوزه زنجیرهای شناخته میشود.
کلام آخر
طبق آنچه که در این مطلب بیان کردیم، بسته (Closure) در جاوا اسکریپت یک ویژگی است که به توابع اجازه میدهد به متغیرهای حوزهٔ بیرونی خود دسترسی داشته باشند، حتی پس از پایان اجرای آن تابع بیرونی. این به این معناست که تابع داخلی میتواند متغیرها و دادههای محیط اطراف خود را ببندد و نگه دارد.
کلوژر در جاوا اسکریپت به توابع امکان میدهند وضعیت (state) را بین اجراهای مختلف حفظ کنند، متغیرهای خصوصی ایجاد کنند، و بهطور مؤثر با کالبکها و کدهای غیرهمزمان کار کنند. این ویژگی یکی از مفاهیم قدرتمند و اساسی در جاوااسکریپت است که به توسعهدهندگان کمک میکند کدهای قابل نگهداری و پیچیدهتری بنویسند.
در این مطلب، با ذکر چند نمونه کد جاوا اسکریپتی، به شما Closure و طرز کار با آن را نشان دادیم. شما میتوانید این کدها را بهصورت عملی خودتان تایپ و اجرا کنید. خروجیها را مشاهده کنید و یکبار دیگر، تفسیر کنید که چه اتفاقی در اجرای هر قطعه کد رخ خواهد داد.
آموزش جاوا اسکریپت در مکتب خونه
دورههای آموزشی مکتب خونه، یکی از بهترین منابعی هستند که میتوانید برای یادگیری صفر تا صد زبان برنامه نویسی جاوا اسکریپت و قابلیتهای خاص آن، دنبال نمایید. در آموزش های جاوا اسکریپت و آموزش های برنامه نویسی مکتب خونه، شما بهطور عملی کار با جاوا اسکریپت را یاد میگیرید. انجام تمرینها و پروژههای گنجانده شده در این آموزش، به شما کمک میکند تا تسلط خوبی را روی مفاهیم ارائه شده کسب کنید. آموزشهای مکتب خونه، پلی میان یادگیری شما و ورود به دنیای حرفهای خواهند بود.
رفرنس: geeksforgeeks