Scope، Hoisting و Closures در جاوا اسکریپت

محدوده
Scope در جاوا اسکریپت به دسترسی متغیرها، توابع و اشیاء در بخش خاصی از کد اشاره دارد. جاوا اسکریپت از محدوده واژگانی استفاده می کند، به این معنی که محدوده یک متغیر یا تابع با مکان آن در کد تعیین می شود.
قبل از ورود به حوزه، باید بدانیم که چگونه می توانیم متغیرها را در جاوا اسکریپت تعریف کنیم. سه روش در جاوا اسکریپت برای اعلام متغیرها وجود دارد. اجازه دهید var و const.
- متغیرهای اعلام شده با var دارای محدوده تابع هستند. مثال
function foo() {
var a = 1;
console.log(a);
}
foo(); // Output: 1
console.log(a); // Error: a is not defined
- اگر متغیری در یک تابع با استفاده از var اعلان شود، فقط در تابع قابل دسترسی است، اما اگر در بلوک تعریف شده باشد، محدوده بلوکی خواهد داشت، میتوان به آن در هر نقطه از برنامه دسترسی داشت.
if(2 < 3){
var a = 1;
console.log(a);
}
console.log(a); // Output: 1
- در جاوا اسکریپت توابع تو در تو می توانند به متغیرهای تابع والد خود دسترسی داشته باشند این توابع تودرتو را closur می نامند. مثال
function outer() {
var a = 1;
function inner() {
var b = 2;
console.log(a, b);
}
inner();
}
outer(); // Output: 1 2
- متغیرهای اعلام شده با استفاده از let و const دارای محدوده بلوکی خواهند بود به این معنی که هر متغیری که در بریس های فرفری اعلام شده است، از خارج از بریس های فرفری قابل دسترسی نیست. مثال
if(2 < 3){
var a = 1;
console.log(a);
}
console.log(a); // Output: undefined
- توابع تو در تو می توانند به متغیرهای اعلام شده در یک تابع بیرونی دسترسی داشته باشند
- اگر متغیری خارج از همه توابع و بلوک ها اعلان شود، دامنه جهانی خواهد داشت به این معنی که از هر نقطه برنامه قابل دسترسی است.
بالا بردن
در جاوا اسکریپت اجرا دو مرحله خواهد داشت یکی ساخت و دیگری اجرا. در مرحله ایجاد، همه متغیرها با مقدار پیش فرض تعریف نشده در حافظه ایجاد می شوند و همه توابع به طور کامل ذخیره می شوند. در اینجا فقط متغیرهای اعلام شده با توابع var و منظم در مرحله ایجاد ایجاد می شوند. این به عنوان بالا بردن شناخته می شود
- در مرحله اجرا، مقداردهی اولیه متغیر صورت خواهد گرفت
console.log(hi) // undefined
console.log(hello) // reference error
var hi =90
let hello=99
var hii=44
hi()
function hi(){
console.log(hii);// output: 44
}
بسته شدن
در جاوا اسکریپت، زمانی که تابعی در داخل تابع دیگری تعریف می شود و تابع درونی به عنوان مرجعی به متغیری خارج از تابع محصور کننده آن، برگردانده یا ارسال می شود، یک بسته ایجاد می شود. بسته شدن مقادیر تمام متغیرهایی را که در زمان ایجاد آن در محدوده بودند، حتی پس از بازگشت تابع بیرونی، جمعآوری کرده و حفظ میکند. بسته شدن به شما امکان می دهد متغیرها و توابعی خصوصی ایجاد کنید که از خارج از تابعی که در آن تعریف شده اند غیرقابل دسترسی هستند.
مثال
function outerFunction(x) {
function innerFunction(y) {
return x + y;
}
return innerFunction;
}
let addFive = outerFunction(5);
console.log(addFive(3)); // Output: 8
در این مثال outerFunction innerFunction را برمیگرداند که سپس به متغیر addFive اختصاص داده میشود. هنگامی که addFive با آرگومان 3 فراخوانی می شود، 8 را برمی گرداند. بسته شدن زمانی ایجاد می شود که innerFunction در داخل outerFunction تعریف شود، و مقدار x را دریافت می کند که 5 است. حتی اگر outerFunction بازگشته باشد و متغیرهای محلی آن دیگر در داخل نباشند. scope، closure مقدار x را حفظ می کند و هنوز هم می تواند در هنگام فراخوانی innerFunction به آن دسترسی داشته باشد.
کاربردهای بسته شدن
- متغیرها و متدهای خصوصی: از بسته شدن میتوان برای ایجاد متغیرهای خصوصی و روشهایی استفاده کرد که از خارج از تابعی که در آن تعریف شدهاند غیرقابل دسترسی هستند. این می تواند برای جلوگیری از تغییرات ناخواسته در کد شما مفید باشد. مثلا:
function counter() {
let count = 0;
function increment() {
count++;
console.log(count);
}
return increment;
}
let c = counter();
c(); // Output: 1
c(); // Output: 2
c(); // Output: 3
- حافظهگذاری: از بستهها میتوان برای پیادهسازی حافظهسازی استفاده کرد، که تکنیکی برای ذخیرهسازی نتایج فراخوانیهای عملکرد گران قیمت برای بهبود عملکرد است. مثلا:
function memoize(func) {
let cache = {};
return function(...args) {
let key = JSON.stringify(args);
if (cache[key]) {
console.log('Retrieving from cache');
return cache[key];
}
else {
console.log('Calculating result');
let result = func(...args);
cache[key] = result;
return result;
}
};
}
function fibonacci(n) {
if (n < 2) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
let memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // Output: 55
console.log(memoizedFibonacci(10)); // Output: Retrieving from cache, 55
- توابع پاسخ به تماس: از بسته ها می توان برای ایجاد توابع برگشت به تماس استفاده کرد که به متغیرهای تابع والد دسترسی دارند. این می تواند برای پیاده سازی کنترل کننده رویداد و سایر عملیات ناهمزمان مفید باشد. مثلا:
function doSomething(callback) {
let data = 'hello world';
setTimeout(function() {
callback(data);
}, 1000);
}
function callbackFunction(data) {
console.log(data.toUpperCase());
}
doSomething(callbackFunction); // Output: HELLO WORLD