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

scope در جاوا اسکریپت مشخص می کند که شما به چه متغیرهایی دسترسی دارید. همچنین، هر زمان که یک تابع را در یک تابع دیگر ایجاد می کنید، یک closure ایجاد کرده اید که در این حالت تابع درونی closure است.

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

محدوده (Scope)

scope یا محدوده در جاوا اسکریپت مشخص می کند که شما به چه متغیرهایی دسترسی دارید. دو نوع scope وجود دارد – محدوده سراسری (global scope) و محدوده محلی (local scope).

Scope in JavaScript

محدوده سراسری (Global scope)

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

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

Global scope

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

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

اگرچه می توانید متغیرها را در global scope اعلام کنید، توصیه می شود این کار را نکنید. این به این دلیل است که احتمال naming collisions وجود دارد که در آن دو یا چند متغیر یکسان نامگذاری می شوند. (collision یک event است از دو یا چند رکورد که به یک شناسه یا مکان یکسان در حافظه اختصاص داده می شود). اگر متغیرهای خود را با const یا let اعلام کرده باشید، هر زمان که یک name collision رخ دهد، یک خطا دریافت خواهید کرد که نامطلوب است.

collision in JavaScript

اگر متغیرهای خود را با var اعلام کنید، متغیر دوم بعد از اعلام، متغیر اول را بازنویسی می کند. این نیز نامطلوب است زیرا اشکال زدایی کد خود را سخت می کنید.

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

بنابراین، شما همیشه باید متغیرهای محلی را اعلام کنید، نه متغیرهای سراسری.

محدوده محلی (Local scope)

متغیرهایی که فقط در قسمت خاصی از کد شما قابل استفاده هستند در محدوده محلی در نظر گرفته می شوند. به این متغیرها متغیر محلی نیز گفته می شود.

در جاوا اسکریپت، دو نوع محدوده محلی وجود دارد: محدوده تابع (function scope) و محدوده بلوک (block scope)

بیایید ابتدا در مورد محدوده تابع یا function scope صحبت کنیم.

محدوده تابع (Function scope)

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

در مثال زیر، متغیر hello در محدوده sayHello قرار دارد:

Function scope in JavaScript

محدوده بلاک (Block scope)

در JavaScript هنگامی که متغیری را با const یا let درون براکت های باز و بسته ({}) اعلام می کنید، می توانید به این متغیر فقط در آن پرانتز دسترسی داشته باشید.

در مثال زیر، می توانید ببینید که hello به پرانتز محدود شده است:

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

محدوده بلاک زیر مجموعه ای از محدوده تابع است زیرا توابع باید با براکت ها اعلان شوند (مگر اینکه از توابع پیکان با بازگشت ضمنی - arrow functions with an implicit return - استفاده کنید).

Function hoisting and scopes

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

Function hoisting and scopes in JavaScript

هنگامی که با یک عبارت تابع اعلان می شود، توابع در بالای محدوده فعلی بالا نمی روند.

توابع و اسکوپ ها در جاوا اسکریپت

به دلیل این دو تغییر، بالا بردن تابع (function hoisting) به طور بالقوه می تواند گیج کننده باشد و نباید استفاده شود. همیشه عملکردهای خود را قبل از استفاده از آنها اعلام کنید.

توابع به اسکوپ یکدیگر دسترسی ندارند

وقتی توابع را جداگانه تعریف می کنید، حتی اگر یک تابع در دیگری استفاده شود، به محدوده یکدیگر دسترسی ندارند.

در مثال زیر، second به firstFunctionVariable دسترسی ندارد.

دسترسی به اسکوپ توابه در زبان جاوا اسکریپت

دامنه های تو در تو (Nested scopes)

وقتی یک تابع در تابع دیگری تعریف می شود، تابع داخلی به متغیرهای تابع بیرونی دسترسی دارد. به این رفتار در جاوا اسکریپت lexical scoping یا محدوده واژگانی می گویند.

با این حال، تابع خارجی به متغیرهای تابع داخلی دسترسی ندارد.

Nested scopes in JavaScript

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

اسکوپ ها در توابع مانند یک شیشه یک طرفه رفتار می کنند. شما می توانید بیرون را ببینید، اما افراد بیرون نمی توانند شما را ببینند.

اگر اسکوپ هایی در اسکوپ دارید، چندین لایه شیشه یک طرفه را تجسم کنید.

چندین لایه تابع به معنای چندین لایه شیشه یک طرفه است.

پس از درک همه چیز در مورد دامنه ها تا کنون، به خوبی می توانید بفهمید کهclosures چیست.

Closures آموزش

Closures

هر زمان که یک تابع را در یک تابع دیگر ایجاد می کنید، یک closure ایجاد کرده اید. تابع درونی closure است. این closure معمولاً برگردانده می شود، بنابراین می توانید بعداً از متغیرهای تابع خارجی استفاده کنید.

Closures in JavaScript

از آنجایی که تابع درونی برگردانده شده است، می توانید با نوشتن یک عبارت return در حین اعلام تابع، کد را کمی کوتاه کنید.

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

از آنجایی که closure ها به متغیرهای تابع خارجی دسترسی دارند، معمولاً برای دو مورد استفاده می شوند:

1- برای کنترل عوارض جانبی

2- برای ایجاد متغیرهای خصوصی

کنترل عوارض جانبی با closures

عوارض جانبی (side effects) زمانی اتفاق می افتد که به غیر از برگرداندن مقداری از یک تابع، کاری را انجام دهید. بسیاری از چیزها می توانند عوارض جانبی باشند، مانند درخواست Ajax، مهلت زمانی (timeout) یا حتی یک عبارت console.log:

کنترل عوارض جانبی با closures

وقتی از closures برای کنترل عوارض جانبی استفاده می کنید، معمولاً نگران مواردی هستید که می توانند جریان کد شما را مانند Ajax یا تایم اوت ها مختل کنند.

بیایید این موضوع را با یک مثال مرور کنیم تا همه چیز واضح تر شود.

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

من از ES6 arrow functions در اینجا استفاده می کنم تا مثال را کوتاه تر و قابل فهم تر کنم.

timeout

همانطور که می بینید، این تابع کیک سازی یک عارضه جانبی دارد: timeout.

فرض کنید می خواهید دوستتان طعمی را برای کیک انتخاب کند. برای انجام این کار، می توانید بنویسید که یک طعم به تابع makeCake خود اضافه کنید.

side effects in JavaScript

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

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

مشکل اینجا این است که شما نمی خواهید کیک را بلافاصله پس از دانستن طعم آن درست کنید. می خواهید بعداً در زمان مناسب آن را بسازید.

برای حل این مشکل، می توانید یک تابع prepareCake بنویسید که طعم شما را ذخیره می کند. سپس، makeCake closure را داخل prepareCake برگردانید.

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

Closures in JavaScript

اینگونه است که از closures برای کاهش عوارض جانبی استفاده می شود - شما تابعی ایجاد می کنید که closure داخلی را به میل شما فعال می کند.

متغیرهای خصوصی با closures

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

با این حال، گاهی اوقات شما نیاز به دسترسی به چنین متغیر خصوصی دارید. شما می توانید این کار را با کمک closures انجام دهید.

متغیرهای خصوصی با closures

saySecretCode در مثال بالا تنها تابعی است (a closure) که SecretCode را خارج از تابع مخفی اصلی آشکار می کند. به این ترتیب، به آن یک تابع ممتاز (privileged function) نیز می گویند.

اشکال زدایی scope ها با DevTools

DevTools در Chrome و Firefox اشکال زدایی متغیرهایی را که می توانید در اسکوپ فعلی به آنها دسترسی داشته باشید را برای شما ساده می کند. دو راه برای استفاده از این قابلیت وجود دارد.

اولین راه این است که کلمه کلیدی دیباگر (debugger) را به کد خود اضافه کنید. این باعث می شود اجرای جاوا اسکریپت در مرورگرها متوقف شود تا بتوانید اشکال زدایی کنید.

در اینجا یک مثال با prepareCake نشان داده شده است:

اشکال زدایی scope ها با DevTools

اگر DevTools خود را باز کنید و به تب Sources در کروم (یا تب Debugger در فایرفاکس) بروید، متغیرهای در دسترس را مشاهده خواهید کرد.

اشکال زدایی اسکوپ prepareCake

شما همچنین می توانید کلمه کلیدی دیباگر را به closure تغییر دهید. توجه کنید که چگونه متغیرهای اسکوپ این بار تغییر می کنند:

اشکال زدایی اسکوپ در جاوا

خلاصه مطالب این مقاله

درک scope ها و closure ها در JavaScript خیلی سخت نیست. زمانی که بدانید چگونه آنها را از طریق یک شیشه یک طرفه ببینید، بسیار ساده هستند.

وقتی متغیری را در یک تابع اعلام می کنید، فقط می توانید به آن در تابع دسترسی داشته باشید. گفته می شود که این متغیرها در اسکوپ تابع هستند.

اگر هر تابع درونی را در یک تابع دیگر تعریف کنید، این تابع درونی closure نامیده می شود که در واقع دسترسی به متغیرهای ایجاد شده در تابع خارجی را حفظ می کند.

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

مقالات این دسته بندی