توسعه دهنده و طراح رابط کاربری توییتر من: @aliraam / برای ارتباط با من به apirani3784 در جیمیل ایمیل بزنید.
راهنمای استفاده از this در جاوا اسکریپت
در جاوا اسکریپت یکی از خواصی ( properties ) هست که به طور پیشفرض به هر فانکشن ( function ) پاس داده میشود. مقدار آن بستگی به Object داره که توسط فانکشن صدا زده میشه که عمدتا در زمان runtime جاوا اسکریپت تعریف میشه. اما عملکرد this به اون شکلی که تو ذهنتون هست ممکنه نباشه، پس با من همراه باشین تا بهتون بگم زمانی که میخواین مقدار this رو مشخص کنید چه حرکتی باید بزنین :))
یه چند دقیقه وقتتون رو بذارین و کد زیر رو چک کنین، فک میکنین که خروجی چه میشه؟ کد رو تو بخش console مرورگرتون ( رو صفحه کلیک راست کنید و گزینه Inspect Element در فایرفاکس و یا کلیک راست و زدن گزینه Inspect در گوگل کروم و انتخاب گزینه console در کادر باز شده) اجرا کنید و خروجی رو ببینین
let myObject = {
name: "ali",
age: 22,
aboutMe: function(){
return "My name is "+this.name+", I am "+this.age + " years old"
}
}
console.log(myObject.aboutMe());
همونطور که متوجه شدین خروجی به شکل زیر خواهد بود:
My name is ali, I am 22 years old
ما از this برای ارجاع دادن به object داخل فانکشن استفاده کردیم. به نظر ساده اس درست میگم؟ حالا به قسمت پایین توجه کنید:
let myObject2 = {
name: "sara",
age: 24,
aboutMe: function(){
return "My name is "+this.name+", I am "+this.age + " years old"
},
child: {
name: "soheila",
age: 2,
aboutMe: function(){
return "My name is "+this.name+", I am "+this.age + " years old"
}
}
};
console.log(myObject2.aboutMe());
console.log(myObject2.child.aboutMe());
فک کنم انتظار داشتین خروجیتون چیزه دیگه ای باشه، اما متاسفم :)) دستور دوم ما با نمایش خروجی اطلاعات فرزند ( child ) رو در console نمایش میده و نه والد رو. و میتونیم بگیم که this
به صورت پیشفرض اشاره میکنه به نزدیک ترین object ما. اما وقتی که ما فانکشن رو به صورت global تعریف کنیم قصه مون چطور میشه؟
آبجکت عمومی ( یا به قول فرنگیا Global Object)
هر فراخوانی یا تابعی که داخل یک object تعریف نشه وصل میشه به global object، همونطور که آبجکت Window داخل مرورگر هست. شما میتونین برای رسیدن به حرف من this;
رو داخل console مرورگر وارد کنید و اینتر رو بزنین تا نتیجه رو ببینین. این عمل آبجکت Window رو به همراه خاصیت هاش ( properties ) و مقادیرش برمیگردونه.
شما حتی میتونین properties رو از آبجکت تغییر بدین. کد زیر رو داخل console کپی / پیست کنین:
console.log(this.color); //Undefined
this.color = "Green";
console.log(this.color); //Green
خط اول ما مقدار undefined
رو بر میگردونه، بخاطر اینکه آبجکت Window ما هیچ پراپرتی با نام color
رو در داخلش نداره. خط دوم یک پراپرتی با نام color
ایجاد میکنه و مقدار "Green"
بهش نسبت میده. و الان شما میبینید که تو خط سوم کدمون میتونیم به این پراپرتی دسترسی داشته باشیم.
تا الان ما، کل حرفه ما سره این بود که runtime جاوا اسکریپت به ما کمک میکنه تا مقدار this
چی باشه. اما این موضوع برای همه ی مواقع خوب نیست و فقط بعضی وقتا به داد ما میرسه و بعضی جاها کار پیچیده تر میشه. برای نمونه توبع ناهمگام یا ( asynchronous function ) در نظر بگیرید. کد زیرو ببینید و حدس بزنین که خروجی چه خواهد بود؟ تو کنسول مرورگرتون تستش کنید:
let myObject3 = {
firstName: "Michael",
age: 20,
aboutMe: function(){
setTimeout(function(){
console.log(this.firstName + " is " + this.age + " years old")
},5000)
}
}
myObject3.aboutMe();
متد aboutMe
دارای یک فانکشن هست که فقط بعد از 5 ثانیه اجرا میشه، پس زمانی که اجرا میشه فانکشن رو برای بعدا نگه میداره. وقتی که در نهایت اجرا بشه اون در global scope نیست و خروجی بازگشتی ما به شکل زیر هست:
undefined is undefined years old.
دلیل این اتفاق خیلی ساده است و اون این هست که کلمه this
در زمان اجرا به آبجکت Window اشاره میکنه و نه به myObject3 و از اونجایی که هیج کدوم از اون پراپرتی ها در آبجکت Window وجود ندارن مقدار undefined برگشت داده میشه. اما خب ما چطور مشکل رو حل کنیم؟ ما باید به نحوی به Async function مون بگیم که کلمه this
چی هست. نگران نباشین جاوا اسکریپت 3 راه برای راحتی ما گذاشته که عبارتند از call
, apply
وbind
استفاده از متد Call
همونطور که آبجکت ها دارای پراپرتی هایی از پیش تعریف شده است ( prototypes ) مثله toString
، فانکشن ها هم این شکلی هستن. یکی از اون متد ها call
هست. متد call
در بسیاری از موارد استفاده میشه اما در نهایت یک مشکل رو حل میکنه.
بیاین دوتا آبجکت جداگانه تعریف کنیم شبیه همونایی که قبلا داشتیم:
let user1 = {
name: "touhid",
sayHi: function(){
return this.name + " says Hi"
}
}
let user2 = {
name: "mitra",
sayHi: function(){
return this.name + " says Hi"
}
}
console.log(user1.sayHi()); //touhid says Hi
console.log(user2.sayHi()); //mitra says Hi
هر دو آبجکت فانکشن sayHi در درون خودشون دارند. الزامی نیست که ما کدهارو چند بار بنویسیم، ما باید تا جایی که میشه کد هامون رو باز نویسی کنیم تا به بهترین خروجی برسیم. ما میتونیم کد بالا رو به شکل زیر باز نویسی کنیم. ما میتونیم فانکشن رو یه بار بنویسیم و در آبجکت دوم با استفاده از متد call
مقدار دهی کنیم:
let user1 = {
name: "touhid ",
sayHi: function(){
return this.name + " says Hi"
}
}
let user2 = {
name: "mitra "
}
console.log(user1.sayHi()); //touhid says Hi
console.log(user1.sayHi.call(user2)); //mitra says Hi
در اینجا ما از فانکشن user1 که sayHi
بود به همراه متد call
برای مقدار دهی مجدد با استفاده از پاس دادن آرگومان user2 بود استفاده کردیم. اگر فانکشن ما باید پارامتر داشته باشه، ما میتونیم آرگومان هارو در متد call
قرار بدیم
let user1 = {
name: "touhid ",
greet: function(msg, friend){
return this.name + " says " +msg+ " to "+friend
}
}
let user2 = {
name: "mitra "
}
console.log(user1.greet("Hi", "jalal"));
//touhid says Hi to jalal
console.log(user1.greet.call(user2, "Hello", "habib"));
//mitra says Hello to habib
استفاده از متد Apply
بسیار شبیه به متد call
هست، متد apply
دقیقا همینکارو با یک استثنا انجام میده، تنها یک آرگومان به رو میپذیره و زمانی که فانکشن ما بیشتر از یک پارامتر داشته باشه ما باید به صورت آرایه آرگومان هارو پاس بدیم.
console.log(user1.greet.apply(user2, ["Hello", "Habeeb"]));
هر دو متد call
و apply
با اجرای تابع بلافاصله فراخوانی میکنند. این مسئله یکی دیگه از مشکلاتی است که ما می خوایم از آنها در Asynchronous استفاده کنیم .
استفاده از متد Bind
برخلاف call
و apply
، متد bind
فانکشنی رو بازگشت میده که صدا شده باشه اما در حال حاضر اجرا نشده باشه.
let myObject4 = {
firstName: "mohammad",
age: 32,
aboutMe: function(){
setTimeout(function(){
console.log(this.firstName + " is " + this.age + " years old")
}.bind(myObject4),5000)
}
}
myObject4.aboutMe(); //mohammadis 32 years old
این بار، خروجی درست به نظر میاد. متد bind
به درستی به رفرنس آبجکتمون اشاره کرده.
تو این مطلب ما با 3 روش آشنا شدیم که که میتونیم به this
مقدار بدیم. اما راه چهارمی هم هست که همینکارو میکنه و اون استفاده از new
هست اما کمی توضیحش در این مقاله نمیگنجه ( به قول قدیمیا هر سخن جایی و هر نکته مکانی دارد :)) ) و به صورت جداگانه بعدا توضیح میدم خدمتتون.
از اینکه در این مقاله هم همراه من بودین سپاسگذارم، شما هم تجربه تو این موضوع دارین؟ خوشحال میشم نظراتتون رو زیر این پست ببینم و بیشتر باهم در ارتباط باشیم
مطلبی دیگر از این انتشارات
4 کد ادیتور عالی برای جاوا اسکریپت از دیدگاه کاربران
مطلبی دیگر از این انتشارات
خاصیت async و defer در تگ Script
مطلبی دیگر از این انتشارات
مقایسه سطحی و عمیق در جاواسکریپت