Scope انواع مختلفی دارد که اگر حتی یک خط کد جاوااسکریپت نوشته باشیم، بدون اینکه متوجه باشیم از یکی از آنها استفاده کردهایم. در این مقاله قصد داریم تا با انواع سطوح مختلف Scope بیشتر آشنا شویم و نحوه ارتباط با کد و چگونگی استفاده از آنها را بررسی کنیم.
اولین سوالی که باید به آن بپردازیم این است که Scope چیست. در جاوااسکریپت و تقریباً هر زبان برنامه نویسی دیگری، کد در Scope مشخصی اجرا میشود. این Scope تعیین میکند که کد ما به چه متغیرهایی دسترسی دارد، متغیرهای جدید چگونه با سایر بخشهای کد تعامل خواهند داشت و چندین مورد دیگر. میتوانیم Scope را به عنوان یک پارتیشن در نظر بگیریم که به ما کمک میکند تا بخشهای مختلف کد خود را از یکدیگر جدا کنیم.
const outer = "Out" function test() { const inner = "In" console.log(outer, inner) // Prints: "Out", "In" } test() console.log(outer, inner) // Throws Uncaught Reference Error: inner is not defined
در مثال بالا چند خط کد ساده داریم که یک متغیر را خارج از یک تابع و دیگری را داخل تابع تعریف میکنیم. سپس داخل تابع هر دو متغیر را در کنسول نمایش میدهیم. این کد در داخل تابع به خوبی کار میکند، اما در خارج از تابع خطا رخ میدهد زیرا ما به متغیر داخلی خارج از محدوده تابع دسترسی نداریم.
این مثال از دو Scope از چهار نوع Scope موجود در جاوااسکریپت استفاده میکند تا کدی که داریم را به گونهای جدا کند که فقط بخشهای خاصی از کد به متغیرهای خاصی دسترسی داشته باشند. این موضوع دلیل اصلی وجود سطوح مختلف Scope است.
چهار سطح Scope وجود دارد که عبارتند از:
در ادامه هر کدام از این سطوح را باهم بررسی خواهیم کرد.
برای شروع درمورد سادهترین Scope صحبت خواهیم کرد. بسیاری از توسعهدهندگان از آن در تمام کدهای خود استفاده میکنند، زیرا به نوعی تمام محدودیتهایی را که Scopeهای دیگر اعمال میکنند نادیده میگیرد. برای درک Global Scope تصور کنید که کد HTML و JS زیر را داریم.
<script src="script.js"></script>
// script.js const a = 1 console.log(a) // Prints: 1
این مثال ساده از Global Scope برای تعریف متغیرهای استفاده میکند. طبیعتا زمانی که متغیری را در سطح بالاتر از یک فایل تعریف کنیم (خارج از هر تابع و یا {}
ای) آن را Global Scope در نظر میگیریم و میتوانیم در هر قسمت برنامه به آن متغیرها دسترسی داشته باشیم. این دسترسی سراسری در ابتدا نوشتن کد را آسانتر میکند، زیرا نیازی نیست نگران بلاک شدن متغیرها توسط انواع Scope های مختلف باشیم، اما با پیچیدهتر شدن کدها مدیریت آن به شدت دشوار میشود. این مشکل زمانی که چندین فایل داریم بیشتر خواهد بود.
<script src="script-1.js"></script> <script src="script-2.js"></script>
// script-1.js const a = 1
// script-2.js console.log(a) // Prints: 1
همانطور که در مثال بالا می بینیم یک متغیر را در script-1.js
تعریف کردیم و میتوانیم از آن متغیر در script-2.js
استفاده کنیم. دلیل این موضوع این است که متغیر script-1.js
به صورت سراسری تعریف شده است و میتواند در هر قسمتی از کد مورد استفاده قرار بگیرد.
Module Scope بسیار شبیه به Global Scope است اما یک تفاوت جزئی با آن دارد. متغیرهای Scopeهای سطح پایین فقط در فایلی که تعریف شدهاند در دسترس هستند. این به این معنی است که نمیتوانیم از آن متغیرها در فایلهای دیگری استفاده کنیم. برای ورود به Module Scope باید از type="module"
در تگهای اسکریپت خود استفاده کنیم.
<script src="script-1.js" type="module"></script> <script src="script-2.js" type="module"></script>
// script-1.js const a = 1 console.log(a) // Prints: 1
// script-2.js console.log(a) // Throws Uncaught Reference Error: a is not defined
با این تغییر، بزرگترین مشکل موجود در Global Scope برای متغیرها حذف شده و در عین حال مزایای استفاده از هر متغیر در هر قسمت از فایلی که در آن تعریف شده است حفظ میشود. به این دلیل است که Module Scope یکی از انواع Scope های محبوب به شمار میآید.
Block Scope یکی از سادهترین مفاهیم است زیرا با {}
مطابقت دارد. اساساً زمانی که ما کدی را داخل {}
قرار میدهیم Block Scope مربوط به خود را ایجاد میکند. این بدان معناست که مواردی مانند توابع، دستورات if، حلقهها و غیره همگی Block Scope مخصوص به خود را دارند.
function test() { const funcVar = "Func" if (true) { const ifVar = "If" console.log(funcVar, ifVar) // Prints: "Func", "If" } console.log(funVar, ifVar) // Throws Uncaught Reference Error: ifVar is not defined }
مثال بالا دارای دو Block Scope جداگانه میباشد. هر Block Scope به عنوان کد بین {}
تعریف میشود و هر بلاک به تمام متغیرهایی که بین دو آکولاد قرار دارند دسترسی دارد. اما این دسترسی در مجموعه دیگری از آکولادها و همچنین تمام متغیرهای Scope اصلی آن وجود ندارد.
در این مثال ما دو بلاک داریم که شامل تابع test
و دستور if
میباشند. اسکاپ مربوط به test
به تمام متغیرهای تابع که در مجموعه دیگری از {}
های تودرتو قرار ندارند دسترسی دارد، بهطور واضحتر یعنی این که دسترسی به متغیر funcVar
وجود دارد. Scope تابع test
همچنین به هر متغیر سطح Global و یا Madule نیز دسترسی خواهد داشت زیرا که این متغیرها، parent اسکاپ test
هستند.
Scope دوم ما دستورif
است و دسترسی آن Scope شامل ifVar
و هر موردی که تابع test
به آن دسترسی دارد میباشد، زیرا تابع test
اسکاپ parent برای دستورif
محسوب میشود.
نکته مهمی که هنگام استفاده از {}
وجود دارد این است که Scope داخلی به متغیرهای اسکاپ اصلی که در داخل آن قرار گرفته است دسترسی دارد اما Scope اصلی به متغیرهای Scopeای که در داخل خود دارد دسترسی ندارد. این قانون در واقع برای همه Scopeهای موجود صادق است.
{ const a = 10 } console.log(a) // Throws Uncaught Reference Error: a is not defined
حتی میتوانیم در هر قسمت از کد خود از آکولاد استفاده کنیم و یک Scope ایجاد کنیم. استفاده از این روش رایج نیست اما گاها میتواند مفید باشد.
آخرین Scopeای که آن را بررسی خواهیم کرد Function Scope میباشد. این Scope به کلمه کلیدی var
مربوط میشود به این صورت که متغیرهایی که با کلمه کلیدی var
تعریف میشوند به جای سطح بلاک در سطح تابع قرار میگیرند، به این معنی که آنها فقط به {}
های یک تابع اهمیت میدهند.
function test() { var funcVar = "Func" if (true) { var ifVar = "If" console.log(funcVar, ifVar) // Prints: "Func", "If" } console.log(funVar, ifVar) // Prints: "Func", "If" }
کدی که اینجا داریم دقیقاً مشابه کد مثال سطح بلاک است، اما این بار برای تعریف متغیرها به جای const
از var
استفاده میکنیم. در این مثال خواهیم دید که کد ما به خوبی کار میکند و دلیل آن این است که کلمه کلیدی var
محدوده سطح بلاک را نادیده میگیرد، بنابراین اگر چه ifVar
در بلاک if
تعریف شده است، اما برای Function Scope اهمیتی ندارد.
یکی از نکات مهمی که باید در مورد این Scoping درک کنیم این است که کد ما هنگامی که چندین متغیر با نام یکسان داریم چگونه عمل میکند.
function test() { const a = "Func" if (true) { const a = "If" console.log(a) // Prints: "If" } console.log(a) // Prints: "Func" }
در این مثال، متغیر a
را هم در داخل تابع test
و هم در بلاک if
تعریف میکنیم. هنگامی که a
را از داخل بلاک if
در کنسول نمایش میدهیم، مقدار a
را از Scope مربوط به if
دریافت میکنیم. اما وقتی که وقتی a
را خارج از بلاک if
در کنسول نمایش میدهیم، مقدار آن را از Scope مربوط به تابع test
دریافت میکنیم.
نکته بسیار مهمی که در این کد وجود دارد این است که وقتی دو متغیر با نام یکسان داریم که در Scopeهای مختلف هستند، در واقع این دو متغیر کاملاً مجزا از یکدیگر هستند. آنها هیچ ارتباطی با یکدیگر ندارند و مقادیرشان دچار مشکل نمیشود و دقیقاً مانند دو متغیر با نامهای مختلف کار میکنند. تنها تفاوت این است که اگر متغیرها نام یکسانی داشته باشند، اکنون دسترسی به مقدار متغیر Scope خارجی غیرممکن است زیرا همیشه دسترسی a
به داخلیترین متغیر Scope موجود برقرار میباشد.
مفهوم Scope در زبانهای برنامه نویسی به بخشی اشاره دارد که میتوانیم در آن محدوده به متغیرها دسترسی داشته باشیم. Scope در جاوااسکریپت دارای انواع مختلف است که در این مقاله سعی کردیم آنها را بررسی کرده و با نحوه کارکردشان آشنا شویم. درک این مفهوم میتواند به ما در نوشتن کدهای تمیزتر کمک کند.
دیدگاهها: