در این مطلب this و reference توابع در جاوااسکریپت را بررسی خواهیم کرد. این دو مفهوم یکی از کاربردیترین مفاهیم زبان برنامه نویسی جاوااسکریپت هستند. برای درک بهتر this و reference توابع تا انتهای مطلب همراه ما باشید.
آیا تا به حال، کد جاوااسکریپت شبیه به کد زیر را مشاهده کردهاید؟
button.addEventListener('click', this.addItem.bind(this))
در این کد، bind چیست؟ و چرا پرانتزهای تابع وجود ندارند؟
بهتر است سوالمان را اینگونه بپرسیم، چرا کد بالا را به شکل کد زیر نمینویسیم؟
button.addEventListener('click', this.addItem())
پاسخ کاملاً ساده است. زیرا این کد جایگزین مناسبی برای کد قبلی نیست. اما سوال اصلی اینجاست که چرا جایگزین مناسبی نیست؟
بدیهی است که شما عملکردی مانند زیر را در جاوااسکریپت فراخوانی کرده باشید:
function someFunction() { // do something here ... } someFunction()
و در داخل کلاس یا شئ، یک متد را به روش مشابهی فراخوانی کنید:
class MyClass { constructor() { this.myMethod() } myMethod() { // do something here } }
زمانی که کد بالا برای اولین بار اجرا می شود، با استفاده از آن متدها نیز بلافاصله اجرا میشوند. در این کلاس، به محض فراخوانی سازنده، متد myMethod نیز اجرا میشود.
new MyClass()
با این حال، گاهی اوقات شما نمیخواهید که یک تابع یا متد به سرعت اجرا شود.
یک event listener را بر روی یک دکمه (button) در نظر بگیرید:
function someFunction() { ... } const button = document.querySelector('button'); button.addEventListener('click', someFunction());
در این قطعه کد، بعضی از توابع برای اجرا شدن، منتظر کلیک کردن نیستند.
این چیزی نیست که ما میخواهیم. ما فقط میخواهیم که به جاوااسکریپت یا مرورگر بگوییم که هنگام کلیک بر روی دکمه، باید someFunction را اجرا کند. با این کار، مطمئن میشویم که تابع میتواند در هر بار کلیک دکمه، یک یا چندین بار اجرا شود.
شما میتوانید به جای اینکه فوراً و به صورت دستی کدها را اجرا کنید، آدرس چیزهای مورد نظرتان را به جاوااسکریپت یا مرورگر بدهید. این کار از طریق ارایه مرجع (reference) صورت میگیرد. در شبه کد زیر یک مرجع به تابع قابل اجرا در داخل event listener داده شده است.
function someFunction() { ... }; const button = ...; button.addEventListener('click', someFunction);
نکتهای که در کد بالا به چشم میخورد، حذف پرانتزها پس از تابع someFunction است. حذف پرانتز به این معناست که ما دیگر تابع را فراخوانی نکرده و به جای آن یک اشارهگر را به تابع تخصیص میدهیم. این اشارهگر در نقش reference برای event listener ظاهر شده و در قالب یک دکمه ظاهر میشود.
در کلاسهای جاوااسکریپت نیز کدی بسیار مشابه با کد بالا مورد استفاده قرار میگیرد. برای مثال به نمونه زیر توجه کنید:
class MyClass { constructor() { const button = ...; button.addEventListener('click', this.myMethod); } myMethod() { ... }
کلمه کلیدی this در کد فوق نقش مهمی را ایفا میکند. این کلمه اساساً به یک شیء اشاره دارد که توسط کلاس ایجاد شده است. از طرف دیگر از آنجایی که myMethod یک متد از کلاس یا شیء به شمار میرود، تنها از طریق this در دسترس خواهد بود.
به این ترتیب با به کارگیری چنین کدی، به جای فراخوانی بلافاصله یک متد، میتوانید یک اشارهگر بر روی آن تنظیم کنید. دقیقاً به همین دلیل است که در این موارد از گذاشتن پرانتز در مقابل تابع اجتناب میکنیم.
[button class=”github-btn” href=”http://frontcast.ir/oop-in-javascript”]ویدیوی آموزشی: برنامه نویسی شیگرا در جاوااسکریپت[/button]
همانطور که میدانید، از this برای دسترسی به متدها و خصوصیات یک کلاس از هر قسمتی از داخل یک کلاس یا شیء استفاده میشود. اما گاهی این کلیدواژه رفتار عجیبی نشان میدهد. به مثال زیر توجه کنید:
class CreateProduct { constructor() { const btn = document.querySelector('button') this.products = ['Book 1', 'Book 2', 'Book 3'] this.count = 0 btn.addEventListener('click', this.addTitle) } addTitle() { const title = new Product(this.products[this.count]) this.count++ if (this.count >= this.products.length) { this.count = 0 } } }
اگر متوجه نقش و عملکرد Product نشدهاید، به هیچ وجه نگران نباشید. در حقیقت نقش این مورد رندر کردن یک <li> جدید به همراه یک متن در داخل DOM است. با این حال اگر نگرانی شما در مورد موفقیت یا عدم موفقیت این شبه کد باشد، باید بگوییم که کاملاً حق دارید! زیرا احتمال قوی با یک خطا مواجه خواهید شد.
دلیل به وجود آمدن این خطا در این قسمت است:
const title = new Product(this.products[this.count])
همانطور که مشخص است، در این شبه کد دسترسی به this.products و this.count با خطا مواجه شده است. اما دلیل این اتفاق چیست؟ آیا این خطا به اشارهگر کلاس یا شیء مربوط میشود؟
جواب این سوال منفی است. در واقع this به گونهای تعریف نشده که تنها به شیء مورد نظر شما اشاره کرده و در این چهارچوب مشخص محصور بماند. در واقع this اشاره به «هر چیزی که کد را برای استفاده خاصی فراخوانی میکند» اشاره دارد.
در مثال بالا، دکمه مسئولیت اجرای addTitle را دارد. در نتیجه اگر ما به جای addTitle مقدار this را بخواهیم، میتوانیم به این شیوه عمل کنیم:
addTitle() { console.log(this); ... }
بنابراین در این حالت this اشاره به همان تگ <button> دارد که ما آن را در event listener اضافه کردهایم. این دقیقاً رفتار پیشفرض جاوااسکریپت را به ما نمایش میدهد. به عبارت دیگر this اشاره به چیزی دارد که متد دارای this را فراخوانی کرده است.
مشخصاً ما خواهان چنین رفتاری نیستیم. اما خوشبختانه به سادگی میتوانیم با ایجاد تغییراتی کوچک، به هدف خود دست یابیم. شما میتوانید به جای addTitle، کلیدواژه this را به چیز دیگری به غیر از دکمه متصل کنید. برای مثال همانند کد زیر میتوانید this را به یک شیء یا کلاس وصل کنید:
btn.addEventListener('click', this.addTitle.bind(this))
bind یک متد پیشفرض در جاوااسکریپت است که میتوان آن را در توابع و یا متدهای مورد نظر فراخوانی کرد. با استفاده از این متد میتوانید به جای اتصال this به متد یا تابع، آن را به هر مقدار منتخبی متصل کنید.
در شبه کد فوق، ما به جای اتصال this به addTitle، آن را به مقدار مشابهی متصل کردهایم که this در تابع سازنده به آن اشاره دارد. در این سازنده، this اشاره به کلاس یا شیء دارد. چرا که خود ما اقدام به اجرای این کد کردهایم. در واقع سازنده توسط شی تعریف شده توسط شما اجرا میشود. از همین رو کلیدواژه this به جای اشاره به سازنده، به همان شیء اجرا کننده آن اشاره میکند.
[button class=”github-btn” href=”http://frontcast.ir/course/javascript-advanced”]دوره جامع و پیشرفته جاوااسکریپت[/button]