نحوه دسترسی به this صحیح در داخل یک کال بک

نحوه دسترسی به “this” صحیح در داخل یک کال بک

سوال:

من یک تابع سازنده دارم که کنترل کننده یک رویداد را ثبت می کند:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

با این حال، من قادر به دسترسی به data ویژگی شی ایجاد شده در داخل callback نیستم. به نظر می رسد “this” به شیئی که ایجاد شده اشاره نمی کند، بلکه به شی دیگری اشاره دارد.

من همچنین سعی کردم به جای یک تابع ناشناس از یک متد شی استفاده کنم:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

اما همان مشکلات را نشان می دهد.

چگونه می توانم به شی درست دسترسی داشته باشم؟

پاسخ:

آنچه باید در مورد “this” بدانید

“this” (یا همان “context”) یک کلمه کلیدی ویژه در داخل هر تابع است و مقدار آن فقط به نحوه فراخوانی تابع بستگی دارد ، نه اینکه چگونه / چه زمانی / کجا تعریف شده است. مانند سایر متغیر ها، تحت تأثیر دامنه های واژگانی قرار نمی گیرد (به جز توابع پیکان، به زیر مراجعه کنید). در اینجا چند نمونه آورده شده است:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

برای کسب اطلاعات بیشتر در مورد “this”, نگاهی به مستندات MDN بیندازید.

نحوه رجوع به “this” صحیح

از توابع جهت دار استفاده کنید.

ECMAScript 6 توابع پیکانی را معرفی کرد که می توان آن ها را به عنوان توابع لامبدا در نظر گرفت. آن ها “this” الزام آور خود را ندارند. درعوض، “this” درست مانند یک متغیر معمولی، از نظر دامنه بررسی می ‌شود. یعنی لازم نیست از “.bind” استفاده کنید. این تنها رفتار خاص آن ها نیست، لطفاً برای اطلاعات بیشتر به مستندات MDN مراجعه کنید.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

از “this” استفاده نکنید

شما در واقع نمی خواهید به طور خاص به “this” دسترسی داشته باشید، بلکه به شیئی که به آن اشاره می کند. به همین دلیل یک راه حل آسان ایجاد یک متغیر جدید است که به آن شی نیز اشاره دارد. متغیر می تواند هر نامی داشته باشد، اما رایج ترین آنها “self” و “that” هستند.

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

از آنجایی که “self” یک متغیر معمولی است، از قوانین دامنه واژگانی تبعیت می کند و در داخل callback قابل دسترسی است. همچنین این مزیت را دارد که می توانید به “that” مقدار خود callback را بدهید.

تنظیم صریح “this” برای callback – قسمت 1

ممکن است به نظر برسد که شما کنترلی روی مقدار “this” ندارید، زیرا مقدار آن به طور خودکار تنظیم می شود، اما در واقع اینطور نیست.

هر تابع دارای متد .bind [docs]  است که یک تابع جدید را با “this”محدود به یک مقدار بر می ‌گرداند. عملکرد آن دقیقاً همان رفتاری را دارد که شما از .bind استفاده کردید، فقط با این تفاوت که “this” توسط شما تنظیم شده است. مهم نیست که چگونه یا چه زمانی آن تابع فراخوانی شود، “this” همیشه به مقدار ارسال شده اشاره خواهد کرد.

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

در این مورد، ما “this” برای callback را به مقدار “this” برای MyConstructor متصل میکنیم.

توجه: هنگامی که یک زمینه الزام آور برای  jQueryپیوند می زنید، به جای آن از jQuery.proxy [docs]  استفاده کنید. دلیل انجام این کار آن است که شما نیازی به ذخیره ارجاع به تابع در هنگام لغو پیوند یک رویداد بازگشتی ندارید. jQuery آن را به صورت داخلی مدیریت می کند.

تعیین “this” برای callback – قسمت 2

برخی از توابع/روش هایی که callback ها را می پذیرند، مقداری را نیز می پذیرند که بازخوانی “this” باید به آن ارجاع دهد. این اساساً مانند اتصال آن توسط خودتان است، اما تابع/روش این کار را برای شما انجام می دهد.  Array#map [docs] چنین روشی می باشد. امضای آن این است:

array.map(callback[, thisArg])

بحث اول callback و بحث دوم مقدار this است که باید به آن ارجاع شود. در اینجا یک مثال ساختگی آورده شده است:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

توجه: اینکه آیا می ‌توانید مقداری را برای “this” ارسال کنید یا نه، معمولاً در مستندات آن تابع/روش ذکر می ‌شود. به عنوان مثال، متد jQuery [docs]$.ajax  گزینه ای به نام context تعریف می کند:

این شی به زمینه تمام تماس های مربوط به Ajax تبدیل می شود.

مشکل رایج: استفاده از روش ‌های شی به ‌عنوان کنترل ‌کننده‌ های رویداد/callback

یکی دیگر از تظاهرات رایج این مشکل زمانی است که از یک روش شی به عنوان کنترل کننده رویداد/callback استفاده می شود. توابع در جاوا اسکریپت شهروندان درجه یک هستند و اصطلاح “روش” فقط یک اصطلاح محاوره ای برای تابعی است که مقدار یک ویژگی شی می باشد. اما آن تابع پیوند خاصی به شیء “شامل” خود ندارد.

به مثال زیر توجه کنید:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

تابع this.method به‌ عنوان کنترل‌ کننده رویداد کلیک اختصاص داده می ‌شود، اما اگر روی document.body کلیک شود، مقدار ثبت ‌شده به شکل undefined در می آید، زیرا در کنترل ‌کننده رویداد، “this” به document.body اشاره دارد، و نه به Foo. همانطور که در ابتدا ذکر شد، آن چه به “this” اشاره دارد به نحوه فراخوانی تابع بستگی دارد، نه نحوه تعریف آن. اگر کد مانند زیر بود، ممکن است واضح تر باشد که تابع ارجاع ضمنی به شی ندارد:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

راه حل همان است که در بالا ذکر شد: در صورت وجود، از bind. برای اتصال صریح “this”به یک مقدار خاص استفاده کنید.

document.body.onclick = this.method.bind(this);

یا به صراحت تابع را به عنوان “روش” شی، با استفاده از یک تابع ناشناس به عنوان کنترل کننده رویداد/ callback فراخوانی کنید و شی “this” را به متغیر دیگری اختصاص دهید:

var self = this;
document.body.onclick = function() {
    self.method();
};

یا از تابع فلش استفاده کنید:

document.body.onclick = () => this.method();

شما می توانید برای یادگیری جاوا اسکریپت از مسیر کارآموزی فرانت اند استفاده کنید.

پاسخ‌ها

آدرس ایمیل شما منتشر نخواهد شد.

پل ورود به بازار تکنولوژی

مشاوره رایگان انتخاب مسیر

با کمک مشاورهای رستاوا آکادمی مسیر کارآموزی مناسب برای خودت رو برای ورود به بازار کار تکنولوژی انتخاب کن

توسعه فردی برای حرفه‌ای شدن

منتورهای رستاوا و دوره‌های ما شما رو برای کارآموزی و در نهایت جذب و استخدام آماده میکنن

مدرک بین المللی و استانداردهای جهانی

یادگیری با استاندار های بین المللی و دریافت مدرک از Credx Academy کانادا

اگر در مسیرهای کارآموزی ما پذیرش بگیری موقعیت‌های کارآموزی و استخدام در پروژه‌ها و شرکت های بین المللی از طریق مجموعه رستاوا به روت باز می شه.

۲ هفته رایگان

همین حالا با منتورها

ارتباط آنی بگیر!