حوزه تعریف متغیر در جاوااسکریپت و شرح کامل Hoisting

حوزه تعریف متغیر در جاوااسکریپت و شرح کامل Hoisting
در این مقاله ما با حوزه تعریف متغیرها در جاوااسکریپت آشنا خواهیم شد و به طور مفصل در مورد مبحث Hoisting بحث می‌کنیم و موضوع رو با هم پیش می‌بریم.
ما باید به طور کامل با موضوع حوزه تعریف(Scope) متغیر در جاوااسکریپت آشنا باشیم و موضوع Hoisting رو به طور کامل درک کنیم تا بتونیم به سمت دولوپر شدن در زبان برنامه نویسی جاوااسکریپت بریم. شاید در ابتدا این موضوع کمی برای شما مبهم به نظر برسه ولی سعی میکنیم با ذکر مثال‌های کامل و جامع این موضوع رو بهتر توضیح بدیم.
خرید شارژ ایرانسل
خرید شارژ ایرانسل، همراه اول، رایتل | خرید بسته های اینترنت ایرانسل | etore.ir
ردیاب
ردیاب آهنربایی پیام بهترین وسیله برای ردیابی وسیله نقلیه 09121394944
بلیط هواپیما
خرید آنلاین بلیط هواپیما داخلی و خارجی
خودتان را اینجا معرفی کنید

حوزه تعریف متغیر ( Variable Scope )

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

متغیرهای محلی (Local Variables)

برخلاف سایر زبان‌های برنامه نویسی، جاوااسکریپت حالت بلوک‌بندی کردن کدها رو نداره(Block-level Scope) و درمقابل حالت حوزه سطح تابع(Function-Level Scope) رو داره به این معنا که درون تابعی که تعریف میکنیم میتونه نحوه دسترسی متفاوت باشه.
متغیرهایی که درون یک تابع تعریف می‌شوند متغیرهای محلی نامیده میشن و فقط از درون تابعی که تعریف شده و یا توابع تودرتو قابل دسترسی هستن.
مبحث دسترسی متغیرها در توابع تودرتو رو در مبحثی تحت نام Closures بیشتر و کامل‌تر توضیح خواهیم داد.

برای نمایش حالت دسترسی متغیر در حوزه سطح-تابع (Function-Level Scope)
var name = "Majidonline";
​
​function showName () {
var name = "wsschool"; // متغیر محلی و قابل دسترسی فقط درون این تابع
console.log (name); // wsschool
}
console.log (name); // majidonline: متغیر جامع (global)
مثالی برای نمایش حالتی که هیچ حوزه‌ای نداشته باشیم :
var name = "Majidonline";
​// در اینجا ما هیچ حوزه‌ای رو برای متغیر تعریف نکرده ایم و یک متغیر جامع هست
​if (name) {
name = "Wsschool"; // مقدار جدید رو درون متغیر در اینجا عوض کردیم
console.log (name); // Wsschool
}
​
​// همانطور که مشاهده می‌کنید در اینجا هم بعد از تغییر مقدار جدید رو نمایش میده چونکه یک متغیر جامع هست
console.log (name); // Wsschool

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

همیشه قبل از استفاده از متغیر، اون رو تعریف کنید. در واقع شما می‌تونید از وب‌سایت JSHint استفاده کنید تا هم خطاهای سینتاکسی رو براتون مشخص کنه و هم در بعضی مواقع راهنمای خوبی می‌تونه باشه. در این قسمت مثالی رو ذکر کردیم که متغیر رو قبل از استفاده تعریف نکردیم :

enlightenedاگر شما متغیر محلی را با کلمه کلیدی(var) تعریف نکنید در واقع به یک متغیر جامع(global) تبدیل می‌شود.
var name = "Majidonline";
​
​function showCelebrityName () {
console.log (name);
}
​
​function showOrdinaryPersonName () {
name = "Wsschool";
console.log (name);
}
showCelebrityName (); // Majidonline
​
​// name is not a local variable, it simply changes the global name variable​
showOrdinaryPersonName (); // Wsschool
​
​// The global variable is now Johnny Evers, not the celebrity name anymore​
showCelebrityName (); // Wsschool
​
​// راه حل این موضوع اینه که متغیر محلی را به صورت زیر تعریف کنیم
​function showOrdinaryPersonName () {
var name = "Wsschool"; // اکنون این متغیر، یک متغیر محلی هست و فقط در حوزه تعریفش قابل دسترسی می‌باشد
console.log (name);
}

متغیرهای محلی در توابع بر متغیرهای جامع اولویت دارند.

اگر شما یک متغیر محلی و جامع را با نام یکسان تعریف کرده باشید، هنگام استفاده از متغیر محلی درون تابع، متغیر محلی اولویت دارد و از اون استفاده خواهد شد. برای مثال :
 
var name = "Majidonline";
​
​function users () {
// اینجا یک متغیر به صورت محلی تعریف شده و با متغیر جامع نامشون یکسانه ولی متغیر محلی درون این تابع اولویت بیشتری دارد
​var name = "Wsschool";
console.log (name);
}
users (); // Wsschool

متغیرهای جامع (Global Vsriables)

تمام متغیرهایی که بیرون از توابع تعریف می‌شوند متغیرهای جامع هستند، متغیرهای جامع (Global) در تمام بخش های برنامه و توابع داخلی نیز دردسترس هستند.
شما میتونید با هرکدام از حالت‌های زیر در بیرون توابع متغیر خود را تعریف کنید :
var myName = "Majidonline";
​
​// or 
firstName = "Majidonline";
​
​// or ​
​var name; //​
name;
گاهی وقتها نیاز به متغیرهایی داریم که بیشترین استفاده رو درون تابع مورد نظرمون دارند و لازمه بیرون از تابع هم در دسترس باشند و یا اینکه مایل باشید بعضی متغیرها رو درون توابع تعریف کنید و بیرون از توابع هم در دسترس باشند به این صورت عمل می‌کنیم که موقع تعریف متغیر، کلمه کلیدی var  رو نمی‌نویسیم!
برای مثال :
function showAge () {
// Age is a global variable because it was not declared with the var keyword inside this function​
age = 94;
console.log(age);// ​
}
​
showAge (); // 94​
​
​// با توجه به اینکه متغیری که تعریف کردیم یک متغیر جامع هست در این قسمت هم در دسترسه و میتونیم ازش استفاده کنیم
console.log(age); // 94
باهم مثال رو بررسی می‌کنیم که به نظر میرسه یه کم عجیب باشه! ولی جواب این ابهام یک قانون و یا بهتره بگم یک ویژگی زبان جاواسکریپته که :
enlightenedدر زبان برنامه نویسی جاوااسکریپت حالت تعریف بلوک بندی کدها (block-level scope) رو نداریم.
 
// هر دو متغیر تعریف شده متغیرهای جامع هستند حتی با وجود اینکه متغیر دوم درون بلوک قرار دارد
​var firstName = "Wsschool";
{
​var firstName = "Majidonline";
}
​
​// آخرین مقدار متغیر بر روی مقدار اول اوررایت می‌شود
console.log (firstName); // Majidonline
و مثالی جذابتر cool
for (var i = 1; i <= 10; i++) {
console.log (i); // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10​
};
​
​// The variable i is a global variable
// متغیر برای تابع زیر آخرین مقداری و که داره نمایش میده
​function aNumber () {
console.log(i);
}
​// متغیر قبل از اینکه از حلقه خارج بشه مقدار 11 رو داره و بعد از چک کردن شرط، چونکه شرط حلقه برقرار نیست از حلقه خارج میشه
aNumber (); // 11​

متغیرهای setTimeout از متغیرهای جامع () استفاده می‌کنند!

این نکته که تمام توابع در setTimeout در حوزه میدان جامع ( global ) هستند شاید کمی مبهم باشه ولی با تفکر و بررسی مثال زیر می‌تونید به طور کامل موضوع رو درک کنید :
enlightenedاستفاده از آبجکت "this" درون تابع  setTimeout اشاره میکنه به آبجکت window ، نه myObj !
//The use of the "this" object inside the setTimeout function refers to the Window object, not to myObj​
​
​var highValue = 200;
​var constantVal = 2;
​var myObj = {
  highValue: 20,
  constantVal: 5,
  calculateIt: function () {
     setTimeout (function () {
         console.log(this.constantVal * this.highValue);
     }, 2000);
}
}
​// The "this" object in the setTimeout function used the global highValue and constantVal variables
​
myObj.calculateIt(); // 400​
​// این موضوع نکته ی مهمی هست که لازمه به یاد داشته باشیم

حوزه جامع (Global Scope) را با متغیرهای بیهوده نامرتب و غیرخوانا نکنید!

الان که ما تصمیم داریم به سمت حرفه‌ای شدن در زمینه‌ی جاوااسگریپت بریم لازمه نکات زیادی رو رعایت کنیم که یکی از اونها انتخاب مکان درست متغیرهاست.
با  بررسی مثال زیر میتونید منظورم رو بفهمید :
// دو متغیری که در حوزه میدان جامع یا گلوبال تعریف شده اند که در واقع نباید اینجا باشند
​var firstName, lastName;
​
​function fullName () {
console.log ("Full Name: " + firstName + " " + lastName );
}
اگر کد بالا رو مرتب تر بنویسیم به طوری که بیخودی حوزه میدان جامع (global) رو نامرتب نکنیم، داریم :
// تعریف متغیرها به صورت محلی 
​
​function fullName () {
var firstName = "majid", lastName = "online";
​
console.log ("Full Name: " + firstName + " " + lastName );
}

متغیرهای Hoisting

کلمه‌ی Hoisting به معنای "بالا بردن، عمل بالا بردن،..." می‌باشد. (باید این موضوع رو قبول کنیم که نمیتونیم همه‌ی لغات تخصصی و... رو در زبان‌های برنامه نویسی ترجمه کرد حتی دوتا لغاتی که در بالا ترجمه کردم خود کلمه انگلیسیش راحتتر قابل درکه مثل global , local )
هر متغیری که در تابع تعریف میشه یک متغیر Hoisting هست به این معنی که متغیر به بالاترین قسمت از تابع موردنظر منتقل میشه، حتی اگر شما یک متغیر را در پایین‌ترین قسمت تابع تعریف کنید یعنی قبل از بستن آکولاد، به صورت اتوماتیک به بالاترین قسمت تابع منتقل خواهد شد به این صورت که شما میتونید در هرجای تابع بهش دسترسی داشته باشید. اما نکته‌ی مهم اینه که :
enlightenedفقط و فقط تعریف متغیر به بالاترین قسمت تایع منتقل میشه و مقداری که بهش نسبت داده میشه فقط بعد از مکان تعریف در دسترس خواهد بود.
مثال زیر رو با هم بررسی کنیم تا کامل درکش کنیم :
function showName () {
console.log ("First Name: " + name);
​var name = "Ford";
console.log ("Last Name: " + name);
}
​
showName ();
​// First Name: undefined : دلیل این موضوع اینه که ما تاکید کردیم که متغیر به بالاترین سطح تابع منتقل میشه ولی مقدارش بعد از تعریف در دسترسه 
​// Last Name: Ford​
​
​// در بخش زیر مشخص میکنه که موتور جاوااسکریپت به چه نحوی کدها رو پردازش میکنه :​
​
​function showName () {
var name; // name is hoisted (توجه کنید که مقدارش نامشخصه تا وقتی که مقداری بهش نسبت داده بشه)​
console.log ("First Name: " + name); // First Name: undefined​
​
name = "Ford"; // مقداری بهش نسبت داده شد
​
​// هم اکنون مقدار متغیر در دسترسه
console.log ("Last Name: " + name); // Last Name: Ford​
}

تعریف تابع اورراید( Overrides ) میشه روی تعریف متغیرها، زمانی‌که Hoisting صورت می‌گیره !

هر دو مورد تعریف تابع و متغیر به بالاترین سطحشون میرن موقعی که Hoisting  میشن و تعریف تابع اولویت بیشتری بر تعریف متغیر داره. همانطور که در بالا تاکید کردیم فقط تعریف متغیربه بالاترین سطح میره، این موضوع برای تابع  هم صدق میکنه.
مثال زیر رو باهم بررسی کنیم :
// هم متغیر و هم تابع اسم یکسانی دارند
​var myName;
​function myName () {
console.log ("Majidonline");
}
​
​// The function declaration overrides the variable name​
console.log(typeof myName); // function

enlightenedاما در مثال زیر، مقدار متغیر باعث میشه که اورراید بشه بر روی تعریف تابع
​var myName = "Majidonline"; // چون به متغیرمون مقدار داده ایم اولویتش بیشتر میشه نسبت به تعریف تابع
​
​function myName () {
console.log ("Majidonline");
}
​
console.log(typeof myName); // string


و در نهایت مهمه که :
همیشه شاد باشید و از تک تک لحظاتتون لذت ببرید wink
کدنویسی کنید تا شاد باشید.

میکائیل اندیشه هستم، چند سالی میشه در حوزه Front-End کار میکنم و بیشتر علاقه ی من در زمینه برنامه نویسی JavaScript , jQuery و تکنولوژی های مرتبط هست و فعلا نیز در این حوزه مطالعه میکنم ... :)

نظرات و سوالات کاربران

ارسال پاسخ محمد رضا شهسواری
محمد رضا شهسواری
شنبه ۱۳ خرداد ۱۳۹۶ ۲۳:۱۶
داداش خوب بود ممنون کاش می شد تمام برنامه نویس های جاوا اسکریپت می توانستند دور هم جمع بشوند و اطلاعاتشان را به اشتراک بگذارند یا کلاس آموزشی داشتید من حتما شرکت می کنم اگر خبر کنید
ارسال پاسخ myfirst
myfirst
دوشنبه ۲۰ دی ۱۳۹۵ ۲۰:۴۸
var highValue = 200;
var constantVal = 2;
var myObj = {
highValue: 20,
constantVal: 5,
calculateIt: function () {
setTimeout (function () {
console.log(this.constantVal * this.highValue);
}, 2000)
},
anotherFunc:function() {
var innerFunc = function() {
console.log(this.constantVal * this.highValue);
}
innerFunc();
}
}

//myObj.calculateIt(); // 400

myObj.anotherFunc();

اگه این کد اجرا بشه نتیجه همون 400 میشه.
ارسال پاسخ myfirst
myfirst
دوشنبه ۲۰ دی ۱۳۹۵ ۲۰:۴۷
سلام دوست من. ممنون از تلاشت... فقط یه نکته! یه جا گفتی "متغیرهای setTimeout از متغیرهای جامع () استفاده می‌کنند!" این بحث ربطی به setTimeout نداره. کلا شما وقتی توی متد یه آبجکت فانکشن میذاری scope رو گم میکنه و میچسبه به آبجکت window. این در مورد همه فانکشن هایی که به این صورت استفاده میشن صادقه. تو کامنت بعد کد میذارم (چپ چین راست چین مشکل داره :|)
ارسال پاسخ ایمان
ایمان
شنبه ۰۸ آبان ۱۳۹۵ ۱۱:۰۷
شما که اینقدر اموزشهاتو عالیه چرا دوره های اموزشی کامل نمیزارین ؟ که تکنیک هارو و مفاهیم رو مفصل توش بگین من حاظرم بخاطرشون پول هم بدم!!!
ارسال پاسخ iman
iman
شنبه ۰۸ آبان ۱۳۹۵ ۱۰:۵۸
عالی عالی عالی