Default Binding و Implicit Binding: دو قانون پایه‌ی تعیین this

مقدمه

در درس قبل دیدیم که مقدار this بر اساس نحوه‌ی فراخوانی تابع تعیین می‌شود، نه محل تعریف آن. در این درس، دو قانون اول و ساده‌تر از چهار قانون Binding را با جزئیات کامل بررسی می‌کنیم: Default Binding و Implicit Binding.

Default Binding — فراخوانی مستقل تابع

وقتی تابعی به‌تنهایی و بدون اینکه به هیچ شیئی متصل باشد فراخوانی شود، قانون Default Binding اعمال می‌شود:

Default Binding: فراخوانی مستقل تابع
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این فراخوانی، تابع showThis بدون هیچ پیشوندی (مثل obj.showThis()) صدا زده شده است؛ به همین دلیل قانون Default Binding اعمال می‌شود و this به شیء Global (در مرورگر: window) اشاره می‌کند. این همان رفتاری بود که در درس قبل هم مشاهده کردیم.

Default Binding در Strict Mode

رفتار Default Binding در حالت Strict Mode متفاوت است. Strict Mode یک حالت خاص اجرای جاوااسکریپت است که با نوشتن "use strict" در ابتدای فایل یا تابع فعال می‌شود و قوانین سخت‌گیرانه‌تری بر کد اعمال می‌کند:

Default Binding در Strict Mode
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل رفتار جاوااسکریپت

در Strict Mode، وقتی تابع بدون هیچ صاحبی فراخوانی شود، جاوااسکریپت مقدار this را به‌جای شیء Global، برابر undefined قرار می‌دهد. این تفاوت به‌عمد در نسخه‌ی ES5 طراحی شد تا از خطاهای ناخواسته‌ی ناشی از تغییر تصادفی Property های شیء Global جلوگیری شود؛ در حالت غیر-Strict، اگر برنامه‌نویس به‌اشتباه فرض کند this برابر undefined است و سعی کند Property ای روی آن تنظیم کند، این کار بی‌سروصدا روی شیء Global اعمال می‌شود که می‌تواند منشأ باگ‌های پنهان باشد؛ اما در Strict Mode، چنین اشتباهی بلافاصله با خطا مواجه می‌شود، چون تلاش برای خواندن Property از undefined Error می‌دهد.

نکته‌ی مهم: کدهایی که به‌صورت Module (که در فصل Modules بررسی خواهیم کرد) نوشته می‌شوند، به‌طور خودکار در Strict Mode اجرا می‌شوند، حتی بدون نوشتن صریح "use strict".

Implicit Binding — فراخوانی از طریق یک شیء

وقتی تابعی به‌عنوان متد یک شیء، یعنی از طریق Dot Notation یا Bracket Notation، فراخوانی شود، قانون Implicit Binding اعمال می‌شود:

Implicit Binding: فراخوانی به‌عنوان متد شیء
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این فراخوانی، this برابر همان شیئی می‌شود که مستقیماً قبل از نقطه در لحظه‌ی فراخوانی قرار دارد؛ یعنی user. قانون کلی Implicit Binding این است: this برابر شیئی است که «بلافاصله قبل از نقطه» در فراخوانی قرار گرفته، صرف‌نظر از این‌که تابع اصلاً کجا تعریف شده باشد.

Implicit Binding در شیء‌های تودرتو

وقتی یک شیء تودرتو باشد، this همیشه به نزدیک‌ترین شیء (یعنی همان شیئی که مستقیماً متد را صدا زده) اشاره می‌کند، نه شیء‌های بیرونی‌تر:

Implicit Binding در شیء تودرتو
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل رفتار جاوااسکریپت

خروجی این کد "مدیر: رضا" است، نه "مدیر: شرکت الف". دلیل این است که مستقیماً قبل از نقطه‌ی فراخوانی نهایی، manager قرار دارد، نه company؛ به همین دلیل this داخل introduce برابر company.manager است، نه company. جاوااسکریپت هیچ توجهی به این‌که این شیء خودش داخل شیء دیگری قرار دارد، نمی‌کند؛ فقط آخرین شیء قبل از نقطه اهمیت دارد.

مشکل رایج: از دست رفتن Implicit Binding

یکی از رایج‌ترین اشتباهات مرتبط با this، این است که وقتی یک متد از شیء اصلی خودش «جدا» شود (مثلاً در یک متغیر مستقل ذخیره شود یا به‌عنوان Callback پاس داده شود)، Implicit Binding آن از دست می‌رود و به Default Binding برمی‌گردد:

از دست رفتن this هنگام جدا شدن متد از شیء
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

با اینکه extractedGreet دقیقاً همان تابعی است که user.greet به آن اشاره داشت، وقتی extractedGreet() به‌تنهایی و بدون هیچ شیئی فراخوانی می‌شود، دیگر هیچ Implicit Binding ای وجود ندارد؛ چون هیچ شیئی قبل از نقطه در این فراخوانی حضور ندارد. به همین دلیل، این فراخوانی به قانون Default Binding برمی‌گردد و this.name یا undefined (در Strict Mode با خطا) یا خالی (در غیر-Strict، چون شیء Global معمولاً name ندارد) خواهد بود.

این دقیقاً همان مشکلی است که هنگام پاس دادن یک متد به‌عنوان Callback (مثلاً به یک Event Listener) پیش می‌آید و یکی از رایج‌ترین منابع سردرگمی برنامه‌نویسان تازه‌کار با this است. راه‌حل‌های این مشکل -از جمله استفاده از bind- در درس‌های بعدی همین فصل بررسی خواهد شد.

جدول جمع‌بندی

قانونشرط فعال شدنمقدار this
Default Bindingفراخوانی مستقل تابع، بدون هیچ شیئیشیء Global (یا undefined در Strict Mode)
Implicit Bindingفراخوانی به‌عنوان متد یک شیءهمان شیئی که بلافاصله قبل از نقطه قرار دارد

ارتباط با سایر مفاهیم

Default Binding مستقیماً به مفهوم Strict Mode مرتبط است که در فصل‌های بعدی (به‌ویژه Modules) دوباره با آن مواجه خواهیم شد. مشکل «از دست رفتن Implicit Binding» که در این درس دیدیم، پیش‌زمینه‌ی ضروری برای درک اهمیت Explicit Binding (با call، apply و bind) است که در درس بعدی بررسی می‌کنیم.

جمع‌بندی

Default Binding زمانی اعمال می‌شود که تابعی بدون هیچ شیء صاحبی فراخوانی شود؛ در این حالت this برابر شیء Global (یا undefined در Strict Mode) است. Implicit Binding زمانی اعمال می‌شود که تابع به‌عنوان متد یک شیء فراخوانی شود؛ در این حالت this برابر همان شیئی است که بلافاصله قبل از نقطه در فراخوانی قرار دارد. اگر یک متد از شیء اصلی خودش جدا شود و مستقلاً فراخوانی شود، Implicit Binding از دست می‌رود و رفتار آن به Default Binding برمی‌گردد.