بررسی مبحث scope در جاوا اسکریپت

30 بهمن 1397
javascript-scopes

با سلام، در این قسمت از سری آموزشی برنامه نویسی جاوا اسکریپت قصد داریم در رابطه با مبحث scope صحبت کنیم

scope چیست؟

احتمالا گوشتان به بحث Scope خورده است.

ابتدا باید بگویم scope مربوط به بحث accessibility (دسترسی) است. کلمه ی accessibility به معنی دسترسی و کلمه ی visibility به معنی "پدیداری" یا پدیدار بودن است اما در برنامه نویسی معنی یکسانی دارند.

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

در جاوا اسکریپت دو نوع scope داریم:

  • local scope
  • global scope

این دو مورد در فارسی به شکل های مختلفی ترجمه شده اند؛ به طور مثال local scope به صورت "دامنه ی محلی" و global scope به صورت "دامنه ی سراسری" ترجمه شده اند.

یکی از مهم ترین مباحث مربوط به scope، بحث توابع است. در جاوا اسکریپت هر تابع یک scope جداگانه تعریف می کند، بنابراین متغیرهایی که در یک تابع تعریف شوند یک scope از نوع local نسبت به آن تابع پیدا می کنند. یعنی چه؟ بگذارید نشان‌تان دهم ... .

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

اگر متغیری در یک تابع تعریف شود، دامنه اش نسبت به آن تابع محلی خواهد ماند و به اصطلاح می گوییم چنین متغیر هایی، Function scope (دامنه ی تابعی) دارند. معنی ساده ی آن این است متغیر مورد نظر ما تنها از داخلِ خودِ تابع در دسترس خواهد بود.

به مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript Scope</h2>

<p>Outside myFunction() carName is undefined.</p>

<p id="demo1"></p>

<p id="demo2"></p>

<script>
myFunction();

function myFunction() {
  var carName = "Volvo";
  document.getElementById("demo1").innerHTML = typeof carName + " " + carName;
}
document.getElementById("demo2").innerHTML = typeof carName;
</script>

</body>
</html>

در مثال بالا ابتدا تابعی به نام myFunction تعریف می کنیم. سپس داخل این تابع، متغیری به نام carName تعریف کرده و مقدارش را برابر با رشته ی "Volvo" قرار می دهیم. حالا با دستور typeof و مقدار خود تابع می خواهیم ابتدا نوع داده ای که داخل تابع است را نمایش دهیم و سپس مقدارش را. در کد بالا دو دستور داریم که نقطه ی اصلی بحث هستند:

  document.getElementById("demo1").innerHTML = typeof carName + " " + carName;
document.getElementById("demo2").innerHTML = typeof carName;

دستور اول عنصری با آیدی demo1 را می گیرد و نوع داده ی متغیر carName را به همراه مقدارش در آن می ریزد اما دستور دوم سعی می کند این کار را در demo2 انجام دهد. خروجی مثال ما به این شکل خواهد بود:

string Volvo

undefined

مشاهده ی خروجی کد در ادیتور آنلاین جاوا اسکریپت

چرا دستور اول مقدار "string Volvo" را تولید کرده است اما دستور دوم می گوید چنین متغیری تعریف نشده است (undefined)؟ به این دلیل که دستور اول در خودِ تابع نوشته شده است و همانطور که گفتیم اگر متغیری در تابعی تعریف شود تنها از درونِ خودِ آن تابع قابل دسترسی خواهد بود اما دستور دوم بیرون از تابع قرار دارد و اصلا روحش هم خبر ندارد متغیری به این نام وجود دارد.

سوال: چرا در مثال بالا، تابع را صدا زده ایم؟

پاسخ: بله، اگر دقت کرده باشید تابع myFunction را پس از تگ های <script> صدا زده ایم:

myFunction();

دلیل آن این است که متغیر های محلی (Local variables) زمانی ایجاد می شوند که تابع صدا زده شود و پس از اجرای تابع حذف می شوند. بنابراین اگر تابع را صدا نزنید چنین متغیری اصلا وجود نخواهد داشت. در واقع اگر تابع myFunction را صدا نزنیم نه متغیری ساخته می شود نه دستور های داخل آن اجرا می شوند بنابراین در چنین حالتی تنها خروجی ما عبارت undefined خواهد بود.

برای امتحان کردن این موضوع می توانید به ادیتور آنلاین جاوا اسکریپت بروید و قسمت فراخوانی این تابع را حذف کنید.

نکته: ممکن است پس از اعمال هر گونه تغییر در کد ها، نیاز باشد از سمت راست و بالا دکمه ی Run with JS را بزنید تا کد جدید یک بار اجرا شود و بتوانید تغییرات را مشاهده کنید.

نکته: در جاوا اسکریپت، اشیاء و توابع نیز متغیر به حساب می آیند. بر این اساس می توان گفت scope ها سطح دسترسی متغیر ها (variables)، اشیاء (objects) و توابع (functions) را تعیین می کنند.

تبدیل خودکار scope

باید به یاد داشته باشید که اگر مقداری خاص را به متغیری نسبت دهید که به صورت صریح declare نشده باشد، به شکل خودکار تبدیل به یک متغیر سراسری (GLOBAL variable) می شود.

در مثال زیر یک متغیر سراسری به نام carName ساخته می شود، اگر چه آن را داخل یک تابع تعریف کرده ایم:

<!DOCTYPE html>
<html>
<body>

<p>If you assign a value to a variable that has not been declared, it will automatically become a GLOBAL variable:</p>

<p id="demo"></p>

<script>
myFunction();
// را یک متغیر سراسری می دانند carName کد های این قسمت
document.getElementById("demo").innerHTML = "I can display " + carName;
function myFunction() {
  carName = "Volvo";
}
</script>

</body>
</html>

توضیح کد بالا:

با اینکه متغیر carName داخلِ تابع تعریف شده است، باز هم از بیرون قابل دسترس بوده و خروجی "I can display Volvo" را نمایش می دهد. دلیل آن چیست؟

قبل از توضیح این مسئله باید بین دو کلمه ی مهم تفاوت قائل شوید:

  • اظهار کردن یک متغیر (declare): زمانی که یک متغیر را اظهار می کنیم، یعنی آن را با استفاده از کلیدواژه ی var می سازیم؛ به طور مثال carName.
  • مقدار دهی یا تعریف کردن (define) یک متغیر: زمانی که به یک متغیر مقداری را انتساب می دهیم، به آن مقدار خاصی می دهیم؛ به طور مثال "Volvo".

مثال از declare کردن متغیر:

var carName;

مثال از مقدار دهی به متغیر:

carName = "Volvo";

مثال از انتساب مقدار همزمان با declare کردن متغیر:

var carName = "Volvo";

حالا می توانیم کد بالا را توضیح دهیم...

اگر در کد بالا تابع myFunction را به صورت زیر بنویسیم چه اتفاقی می افتد؟

function myFunction() {
  var carName = "Volvo";
}

حالت اصلی آن که در مثال بالا آمده است به این شکل است:

function myFunction() {
  carName = "Volvo";
}

تنها تفاوت این دو، وجود کلیدواژه ی var است. همانطور که از جلسات اولیه به یاد دارید، کلیدواژه ی var یک متغیر را ایجاد می کند. قانونی که به شما گفتیم به این ترتیب بود : "اگر مقداری خاص را به متغیری نسبت دهید که به صورت صریح declare نشده باشد، به شکل خودکار تبدیل به یک متغیر سراسری (GLOBAL variable) می شود."

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

حالت Strict چیست؟

تمام مرورگر های مدرن و بروز دنیا به شما اجازه می دهند جاوا اسکریپت را در Strict Mode (به معنی "حالت سخت‌گیرانه") اجرا کنید. در قسمت های بعدی در مورد Strict Mode صحبت خواهیم کرد اما فعلا بدانید در این حالت متغیر ها به هیچ عنوان به صورت خودکار تبدیل به Global نمی شوند.

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

در جاوا اسکریپت scope سراسری یا Global همان شیء window است که البته هنوز به آن نرسیده ایم. شیء window شیء ای است که تمام محتوای window (پنجره ی مرورگر شما) را در اختیار دارد و تمام متغیر های سراسری (global variables) به آن تعلق دارند.

به مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript Scope</h2>

<p>In HTML, global variables defined with <b>var</b>, will become window variables.</p>

<p id="demo"></p>

<script>
var carName = "Volvo";

// code here can use window.carName
document.getElementById("demo").innerHTML = "I can display " + window.carName;
</script>

</body>
</html>

اگر به مثال بالا خوب دقت کنید متوجه می شوید به جای دستور:

document.getElementById("demo").innerHTML = "I can display " + carName;

می توانیم از دستور زیر استفاده کنیم:

document.getElementById("demo").innerHTML = "I can display " + window.carName;

چرا؟ به این دلیل که گفتیم تمام متغیر های Global به شیء window تعلق دارند که همان محیط کامل جاوا اسکریپت است.

هشدار: بدون حساب و کتاب، متغیر ها یا توابع Global ایجاد نکنید چرا که این متغیر ها و توابع می توانند متغیر ها یا توابعِ شیء window را overwrite کنند. تنها زمانی از متغیر های سراسری استفاده کنید که کاملا نسبت به شرایط آگاهی داشته باشید.

طول عمر متغیر های جاوا اسکریپت

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

نکته: پارامتر ها یا آرگومان های توابع نیز تنها از داخل تابع در دسترس هستند.

بحث امروز، یعنی scope، از بحث های بسیار با اهمیت و اساسی در برنامه نویسی است و بسیاری از اشتباهات برنامه نویسان مبتدی به این بحث مربوط می شود. شما با مرور کردن این مطلب می توانید از چنین اشتباهاتی دوری کنید. به هر حال امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جاوا اسکریپت | Javascript توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (2 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

رضا
09 تیر 1398
سلام پروژه برای مبتدیا دارین؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
09 تیر 1398
سلام دوست عزیز، اگر وقت کنم حتما یه پروژه رو کار می کنیم که خیلی سنگین نباشه. خیلی هم ایده ی خوبی هست.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مصطفی
10 اسفند 1397
باسلام ممنون از توضیحات خوبتون خب شما فرمودید که اگر یک متغیر رو به شکل window.variable تعریف کنیم به صورت global در هر جایی میونیم استفاده کنیم خب سوال من این هست که در پلتفرم node js چطوری میشه به این متغیر ها بصورت global دسترسی داشت؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
12 اسفند 1397
سلام دوست عزیز، بحث ما در این دوره جاوا اسکریپت به عنوان ابزار front end هست و Node.js که پلتفرم جداگانه ای هست تخصص بنده نیست و آشنایی باهاش ندارم. میتونید از دوستانی که در زمینه ی Node.js فعالیت می کنن بپرسید.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.