سلام، در اونجا یعنی مقاله قبلی قصد داشتم تا this keyword رو توضیح بدم. ولی در اینجا قصد خاصی ندارم گفتم دوره هم هستیم ببینیم اگر یه روز خواستیم به زور به یه function بگیم که this شما به چی ارجاع داده بشه باید چه کار کنیم ؟ خب javascript در این مواقع برای ما ۳ تا متد مرتب و مجلسی فراهم کرده. به اسمایی که در تایتل میبینید.
پیشنیاز : فهمیدن this keyword در javascript
به کد زیر توجه کنید:
const Person = { firstName: "Amir", lastName:"Rezvani", printPerson: function () { console.log(this.firstName + " " +this.lastName); } }; Persin.printPerson(); // ===> "Amir Rezvani"
خب مشخصه که برامون Amir Rezvani رو چاپ کرد حالا یه آبجکت دیگه کنارش اضافه میکنیم. اگر من بخوام printPerson رو با مقدار دیگهای فراخوانی کنم، یعنی باید دوباره بنویسمش ؟ بعد برم داخل Object مقدار firstName و lastName رو تغییر بدم ؟
جواب اینه نه از call استفاده میکنیم.
(حواستون باشه فرض کنید کد بالا رو کنار کد پایین نوشتیم جدا نیستن فقط نخواستم دوباره کد رو کپی کنم پایینم بنویسم).
const Person2 = { firstName: "Arash", lastName:"Naraqi", }; Person.printPerson.call( Person2 ); // ===> "Arash Naraqi"
حالا اتفاقی که افتاد اینه
اول در متد printPerson ما نوشته بودیم this.firstName + " " +this.lastName که this به Person رجوع میکرد. ولی وقتی بهش گفتیم که با آبجکت Person2 این تابع رو call کن . یعنی بهش گفتیم که this به Person2 اشاره کنه. در نتیجه this به Person2 رجوع میکنه.
حالا اگر این تابع یه آرگومان داشته باشه چی اونو کجا باید بهش پاس بدیم ؟
جواب اینه که آرگومان اول در call یعنی آبجکتی که this باید بهش ارجاع بده و تمام آرگومانهای باقی مانده رو میتونیم بعد از اون دونه دونه با ویلگول جدا کنیم و بهش پاس بدیم.
یه مثال میزنم:
const Person = { firstName: "Amir", lastName:"Rezvani", printPerson: function (ALERT, NAME) { console.log(ALERT,NAME); console.log(this.firstName + " " +this.lastName); } }; const Person2 = { firstName: "Arash", lastName:"Naraqi", }; Person.printPerson("SUCCESS","Hello"); ///////////// output ///////////// // SUCCESS, Hello // Amir Rezvani ///////////// ///////////// ///////////// Person.printPerson.call( Person2 , "SUCCESS", "Hello"); ///////////// output ///////////// // SUCCESS, Hello // Arash Naraqi ///////////// ///////////// /////////////
متد apply دقیقا مثل call میمونه ولی فقط با یک تفاوت اونم اینه که میتونیم ارگومان هایی که قبلا با ویلگول جدا میکردیم و به تابع پاس میدادیم پشت هم درون یک آرایه بگذاریم و یه دفعه آرایه رو پاس بدیم که راحتتر باشیم.
Person.printPerson.apply( Person2 , ["SUCCESS", "Hello"] ); ///////////// output ///////////// // SUCCESS, Hello // Arash Naraqi ///////////// ///////////// /////////////
داستان call و apply اینه که به محض فراخوانی اجرا میشوند. اگر بخوایم ارجاع this رو برای تابعی مشخص کنیم ولی بعدا در کد اونو فراخوانی کنیم میتونیم از bind استفاده کنیم. همیچنین قابلیتهای جالبی به ما میده که در مثال براتون توضیح میدم.
به نظر خودم به سایت Mozilla حتما سر بزنید تا بهتر و دقیقتر بتونید یاد بگیرید.
ولی درکل متد bind یک function جدید میسازه که وقتی فراخوانی میشه this اون برابر با همان مقداری هست که براش فراهم شده بهعلاوه با توالی مشخصی از آرگومانها که قبل از فراخوانی تابع جدید( این تابع جدیدی که ساخته) ارائه شده .
const module = { x: 42, getX: function () { return this.x; } }; const module2 = { x: 55, } const unboundGetX = module.getX; console.log(unboundGetX()); // ==> undefined const unboundGetX = module.getX.bind(module2); // ==> 55
از اونجایی که module.getX برابر با مقدار خود فانکشن هست درست مثل اینه که ما ()unboundGetX خارج از module فراخوانی کردیم و طبق مقاله قبلی this به window ارجاع پیدا میکنه و در window پروپرتیه x نداریم. اما با استفاده از bind به unboundGetX اعلام میکنیم که this برابر با module2 است.
جای دیگه که این متد بسیار خودشو قدرتمند نشون میده جاهایی مثل مثال زیره :
function LateBloomer() { this.petalCount = 2; this.declare = function() { console.log('I am a beautiful flower with ' + this.petalCount + ' petals!'); }; } LateBloomer.prototype.bloom = function() { setTimeout(this.declare.bind(this), 1000); // * }; const flower = new LateBloomer(); flower.bloom();
همانطور که میدونید setTimeOut متدی از window هست و this همیشه به window برمیگرده. در چنین مواقعی باید مشخصا اعلام کنیم که this چه مقداری باید داشته باشه فقط حواستون باشه که مقدار this خارج از setTimeout مشخص شده و بعد this.declare.bind
به عنوان function درون setTimeout قرار گرفته. این جوری مثل زیر ننوشتیمش:
const x = this.declare.bind(this); // --> (this) is just LateBloomer here in both this setTimeout(x, 1000);
اگر مثل بالا باشد this به window برمیگرده. اگرم دوست داشتین اینجوری تعریفش کنید میتونید از روش cache کردن همونطور که در مقاله قبلی گفتم استفاده کنید. اینجا x در حقیقت بیرون setTimeOut قرار گرفته و مقدار this در هر دو جای استفاده شده در این خط this.declare.bind(this) دقیقا یک مقدار داره. و اونم داره به LateBloomer اشاره میکنه. اما وقتی bind نمیکنیم دقیقا مثل این میمونه که این کارو کرده باشیم.
setTimeout(this.declare, 1000); == setTimeout(function() { console.log('I am a beautiful flower with ' + this.petalCount + ' petals!'); })
و دقیقا در این حالت بدون اشاره مستقیم به this تابع فوق مقدار this پیش فرض در setTimeOut رو قرار میده که همون مقدار window هستش. یه جور دیگه بگم یعنی در this.declare مقدار this اول که به LateBloomer شاره میکنه خرج آوردن متد declare شده و this دوم هم که باز به LateBloomer اشاره میکنه خرج جای گذاری مقداره this با this پیش فرض setTimeout شده.
متد های call و apply به امکان اینو میدن که بطور دستی مقدار this رو مشخص کنیم. فرقشون با هم اینه که در call آرگومان های ارسالی به تابع با ویرگول(،) از هم جدا میشن . ولی در apply کل آرگومانها رو درون آرایه یه جا ارسال میکنیم. و فورا فراخوانی میشن.
متد bind هم مثل اونا کار میکنه ولی مقدار اونو میشه جایی ذخیره کرد و بعدا هر وقت که خواستیم فراخوانی کنیم.