Constructor Binding و مرور جامع Method Invocation

مقدمه

در سه درس قبل، سه قانون از چهار قانون اصلی تعیین this را بررسی کردیم: Default Binding، Implicit Binding و Explicit Binding. در این درس، به سراغ چهارمین و آخرین قانون سنتی می‌رویم: Constructor Binding، که وقتی تابعی با کلیدواژه‌ی new فراخوانی شود فعال می‌شود. سپس یک مرور جامع از تمام قوانین Binding، همراه با توضیح اولویت آن‌ها نسبت به یکدیگر، ارائه می‌دهیم.

Constructor Function چیست؟

یک تابع معمولی، وقتی با کلیدواژه‌ی new فراخوانی شود، نقش یک Constructor Function (تابع سازنده) را پیدا می‌کند؛ یعنی تابعی که هدف آن، ساختن یک شیء جدید است. طبق قرارداد رایج (نه یک قانون اجباری زبان)، نام چنین توابعی معمولاً با حرف بزرگ شروع می‌شود:

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

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

وقتی new User("علی") فراخوانی می‌شود، جاوااسکریپت پیش از اجرای بدنه‌ی تابع، چهار مرحله را به‌طور خودکار انجام می‌دهد:

  1. یک شیء کاملاً جدید و خالی ساخته می‌شود.
  2. مقدار this داخل تابع، برابر همین شیء تازه‌ساخته‌شده قرار داده می‌شود.
  3. بدنه‌ی تابع اجرا می‌شود؛ در طول این اجرا، هر Property ای که روی this تنظیم شود (مثل this.name = name)، روی همان شیء جدید قرار می‌گیرد.
  4. در پایان، اگر تابع خودش صراحتاً یک Object دیگر را return نکرده باشد، همان شیء تازه‌ساخته‌شده به‌طور خودکار بازگردانده می‌شود.

به همین دلیل، this داخل User به شیء تازه‌ساخته‌شده اشاره دارد، نه به شیء Global (که در Default Binding می‌دیدیم) و نه به هیچ شیء از پیش موجودی (که در Implicit Binding می‌دیدیم). این یک قانون کاملاً مستقل و متفاوت از سه قانون قبلی است.

اهمیت به‌کارگیری new

اگر همین تابع را بدون new فراخوانی کنیم، رفتار کاملاً متفاوتی رخ می‌دهد؛ چون دیگر Constructor Binding اعمال نمی‌شود و به‌جای آن، Default Binding حاکم خواهد بود:

function User(name) {
  this.name = name;
}

let result = User("علی"); // بدون new
console.log(result); // undefined — چون هیچ return ای وجود نداشت
console.log(typeof this); // در Global Scope، معمولاً به شیء Global اضافه شده (در غیر-Strict Mode)

این یکی از دلایلی است که کلاس‌ها (Class)، که در فصل Object Model بررسی خواهیم کرد، به‌طور پیش‌فرض اجازه نمی‌دهند بدون new فراخوانی شوند و در چنین حالتی، بلافاصله خطا صادر می‌کنند؛ رفتاری که ایمن‌تر از Constructor Function سنتی است.

Method Invocation — یک نگاه دقیق‌تر به Implicit Binding

اصطلاح Method Invocation (فراخوانی به شکل متد) دقیقاً به همان چیزی اشاره دارد که در درس Implicit Binding با آن آشنا شدیم: زمانی که تابعی از طریق یک شیء و با Dot Notation یا Bracket Notation فراخوانی می‌شود. ارزش دارد این نکته را با یک مثال دیگر مرور کنیم تا تفاوت آن با Function Invocation (فراخوانی مستقل تابع) کاملاً روشن شود:

مقایسه‌ی Method Invocation و Function Invocation
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

دو فراخوانی اول، از نوع Method Invocation هستند؛ چون increment مستقیماً از طریق counter.increment() صدا زده می‌شود و this برابر counter است؛ به همین دلیل count به‌درستی افزایش پیدا می‌کند (1، سپس 2). اما فراخوانی سوم، از نوع Function Invocation است؛ چون incrementAlone بدون هیچ شیئی صدا زده می‌شود. در این حالت، طبق چیزی که در درس Default Binding دیدیم، this به شیء Global اشاره می‌کند (که count ندارد)؛ به همین دلیل، this.count++ به‌جای افزایش دادن counter.count، یک Property جدید و نامرتبط روی شیء Global می‌سازد (در غیر-Strict Mode) یا خطا می‌دهد (در Strict Mode).

خلاصه‌ی چهار قانون Binding در یک نگاه

اکنون که هر چهار قانون را دیدیم، بیایید آن‌ها را در یک جدول واحد کنار هم قرار دهیم:

قانوننحوه‌ی فراخوانیمقدار this
Default Bindingfn()شیء Global (یا undefined در Strict Mode)
Implicit Bindingobj.fn()obj (شیء قبل از نقطه)
Explicit Bindingfn.call(obj) / fn.apply(obj) / fn.bind(obj)()obj (به‌صراحت مشخص‌شده)
Constructor Bindingnew Fn()شیء تازه‌ساخته‌شده

اولویت این قوانین نسبت به یکدیگر

اگر شرایط چند قانون هم‌زمان برقرار باشد (که به‌ندرت پیش می‌آید، اما دانستنش مفید است)، ترتیب اولویت این چهار قانون از قوی‌ترین به ضعیف‌ترین به این شکل است:

Constructor Binding (new)
    بالاتر از
Explicit Binding (call / apply / bind)
    بالاتر از
Implicit Binding (obj.fn())
    بالاتر از
Default Binding (فراخوانی مستقل)

به بیان دیگر، Constructor Binding قوی‌ترین قانون است و همیشه بر سایر قوانین اولویت دارد؛ و Default Binding ضعیف‌ترین قانون است که فقط زمانی اعمال می‌شود که هیچ‌کدام از سه قانون دیگر برقرار نباشند.

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

Constructor Binding پیش‌زمینه‌ی مستقیم فصل Object Model است؛ جایی که Constructor Function را در کنار Prototype و Class با جزئیات کامل بررسی خواهیم کرد. در آخرین درس همین فصل، با Arrow Function و رفتار کاملاً متفاوت آن با this آشنا می‌شویم؛ رفتاری که در واقع از تمام این چهار قانون سنتی، مستقل و متفاوت است.

جمع‌بندی

Constructor Binding زمانی اعمال می‌شود که تابعی با کلیدواژه‌ی new فراخوانی شود؛ در این حالت، یک شیء جدید ساخته می‌شود، this به آن شیء اشاره می‌کند، و در پایان همان شیء بازگردانده می‌شود. Method Invocation اصطلاح دیگری برای همان Implicit Binding است که در آن، تابع از طریق یک شیء فراخوانی می‌شود؛ در مقابل آن، Function Invocation یعنی فراخوانی مستقل تابع که Default Binding را فعال می‌کند. از میان چهار قانون Binding، Constructor Binding بالاترین اولویت را دارد و Default Binding پایین‌ترین اولویت را.