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

مروری بر scope در زبان جاوا اسکریپت نوشته شده در   javascript ۲۵ شهریور , ۱۳۹۷ توسط  عباس حسینی

یکی از مسائل مهم در تمام زبان های برنامه نویسی از جمله جاوا اسکریپت ذخیره یک مقدار در یک محل و دسترسی دوباره به آن میباشد، scope بطور کلی قوانینی است که تعیین میکند چطور یک مقدار در یک محل ذخیره شود و چگونه در صورت نیاز به آن دسترسی پیدا کرد.

لیست مطالب

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

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


function foo(a) {
	console.log( a ); // 2
}

foo( 2 );

وقتی engine جاوا اسکریپت با این کد مواجه میشود قبل از هر چیز مکاتبه ای با scope انجام میدهد تا بر اساس آن تصمیم بگیرد چگونه آن را اجرا کند، به این مکاتبه توجه کنید

engine : سلام، scope من اینجا یک چیزی به نام foo دارم تو اطلاعی ازش داری ؟
scope : سلام، آره foo یک تابع هست
engine : مرسی الان اجراش میکنم
engine : اسکوپ جان ، من داشتم تابع foo رو اجرا میکردم داخلش یک آرگومان به نام a دیدم چیزی ازش میدونی ؟
scope : آره a به عنوان پارامتر برای تابع a تعریف شده و مقدارش ۲ هست
engine : آقا من یکم رفتم جلوتر console.log چیه ؟
scope : اون جزو توابع پیش فرض جاوا اسکریپته
engine : اووو آره یادم اومد زیاد دیدمش
engine : داخل تابع console.log یه چیزی به نام a میبینم این همون آرگومانه که گفتی برای foo تعریف شده ؟ یه چکی بکن ببین مقدارش تغییر نکرده ؟
scope : آره این همونه و مقدارشم تغییر نکرده همون ۲ هست
engine : خیلی خیلی ممنونم – کد رو با موفقیت اجرا کردم و تو در تعیین وضعیت داده ها خیلی بهم کمک کردی
scope : خواهش میکنم وظیفه بود کار من همینه

در مثال بالا دیدید که چگونه تعامل scope و engine منجر به اجرای کد میشود، البته مدل بالا فقط یک مثال بود برای اینکه بهتر متوجه کارکرد آن بشوید ، در زبان های برنامه نویسی مدل کارکرد scope به دو دسته lexical scope و dynamic scope تقسیم میشوند که هرکدام را بطور کامل توضیح خواهم داد.

بررسی lexical scope در جاوا اسکریپت

قبل از اینکه به lexical scope بپردازم باید توضیح مختصری درباره فرآیند lexing بدهم تا فهم موضوع برای شما آسان شود، lexing فرآیندی است که در آن یک رشته کد تبدیل به بخش های معنا دار میشود، مثلا عبارت var a = 2; به var= a و a=2 تقسیم میشود.

زبان جاوا اسکریپت قبل از اجرای یک کد ابتدا آن را کامپایل کرده و آماده اجرا میکند و در حین کامپایل اولین چیزیکه اتفاق میفته عمل lexing هست که وضعیت اسکوپ ها را نیز مشخص میکند ، بنابراین میتوان گفت lexical scope اسکوپ ای است که در حین فرآیند lexing وضعیت آن مشخص میشود که به آن static scope نیز می گویند.

برای فهم روش کار lexical scope به کد زیر توجه کنید، دقت کنید که در زبان جاوا اسکریپت هر تابع یک اسکوپ برای متغیر هایی که داخل آن تعریف میشود ایجاد میکند، پس در اینجا ۳ اسکوپ داریم که عبارتند از A ، B و global


    //Gobal scope
 function A(x) {
     //Scope of A function
     var y = x + 1;

     function B() {
         //Scope of B function
       	 var z = 2;
         console.log(x, y, z)
     }
     B()
 }
 A(2)

باتوجه به کد اول از همه تابع A اجرا میشود و داخل آن تابع B اجرا میشود که داخلش عبارت console.log(x,y,z) را دارد که باید اجرا شود پس جاوا اسکریپت از اسکوپ کمک میگیرید تا مقادیر x,y,z را پیدا کند، روش کار به این شکل است که از درونی ترین اسکوپ یافتن یک مقدار داده را آغاز میکند و این کار را آنقدر ادامه میدهد تا به global scope ختم شود.

اولین مقدار x است که داخل اسکوپ تابع B موجود نیست پس به سمت اسکوپ بالاتر حرکت میکند و مقدار x را داخل اسکوپ تابع A میابد و از آن استفاده میکند

دومین مقدار y است که داخل اسکوپ تابع B موجود نیست پس به سمت اسکوپ بالاتر حرکت میکند و مقدار x را داخل اسکوپ تابع A میابد و از آن استفاده میکند

سومین مقدار z است که داخل اسکوپ تابع B  موجود است و از آن استفاده میکند

بررسی dynamic scope در جاوا اسکریپت

dynamic scope اسکوپی است که هنگام اجرای کد به وجود میاید ولی lexical scope در زمان تعریف کد به وجود می آید ، زبان جاوا اسکریپت بر اساس lexical scope میباشد و از dynamic scope پشتیبانی نمیکند اما من در اینجا میخواهم توسط یک مثال نشان بدهم اگر جاوا اسکریپت از dynamic scope پشتیبانی میکرد روش کار آن به چه شکلی میبود.

به کد زیر توجه کنید


function A() {
    var a = 2;
    console.log( a ); 
}
    
function B() {
    var a = 3;
    A();
}  
B();

در اینجا وقتی تابع B اجرا میشود داخل آن تابع A اجرا میشود ، اگر dynamic scope در اینجا وجود داشت اجرای تابع A منجر به چاپ شدن خروجی ۳ میشد چون داخل تابع B یک اسکوپ برای متغیر a تشکیل شده و مقدار ۳ به آن تخصیص داده شده است اما چون پشتیبانی نمیکند اجرای تابع A مقدار ۲ را چاپ میکند چون در اسکوپ خودش متغیر a وجود دارد و مقدار ۲ به آن تخصیص داده شده است

تفاوت function scope و block scope در جاوا اسکریپت

function scope اسکوپی است که یک function آن را به وجود می آورد  ولی block scope اسکوپی است که یک {} آن را به وجود می آورد مثل (for if switch)

برای اینکه فرق این دو اسکوپ را درک کنید به مثال های زیر توجه کنید


var x =1;
function A (){
    var x = 2;
    console.log(x)
}
A()

در این کد مقدار x به صورت global مقدار ۱ به آن تخصیص داده شده است اما در تابع A یک function scope جدید تشکیل شده و مقدار ۲ به متغیر x تخصیص داده شده است بنابراین خروجی اجرای تابع عدد ۲ میباشد


var x =1;
if(true) {
    let x =2;
    console.log(x)
}

در این کد مقدار x به صورت global مقدار ۱ به آن تخصیص داده شده است اما داخل یک شرط توسط {} یک block scope جدید برای متغیر x تشکیل شده و مقدار ۲ به متغیر x تخصیص داده شده است بنابراین خروجی اجرای کد عدد ۲ میباشد

همانطور که در مثال های بالا مشاهده کردید مهمتری تفاوت block scope و function scope در نحوه ایجاد اسکوپ میباشد یک تابع و بلاک هر کدام داخل محدوده خودشان این اسکوپ را به وجود می آورند

اما نکته دیگری که وجود دارد در زبان جاوا اسکریپت کلمه کلیدی let است که این اسکوپ را به وجود می آورد اگر به جای let از var استفاده کنیم اسکوپی ای برای بلاک مورد نظر ساخته نمیشود