در این مقاله قصد داریم تا با تایپ Function جاوااسکریپت که تایپ همه توابع در این زبان است، آشنا شویم.

مقدمه‌ای بر تایپ Function جاوااسکریپت

در جاوااسکریپت، همه توابع یک آبجکت محسوب می‌شوند که نمونه‌هایی از تایپ Function هستند. از آنجایی که توابع، آبجکت به‌شمار می‌آیند مانند سایر آبجکت‌ها دارای ویژگی‌ها و روش‌هایی نیز می‌باشند.

ویژگی‌های توابع

هر تابع دو ویژگی مهم دارد: lengthو prototype.

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

function add(x, y) {
    return x + y;
}

console.log(add.length); // 2
console.log(add.prototype); // Object{}

تابع add()دو آرگومان x و y را دریافت می‌کند. بنابراین، ویژگی lengthمقدار ۲ را برمی‌گرداند.

new.target

به طور معمول، ما یک تابع را به صورت عادی و به شکل زیر فراخوانی می‌کنیم:

let result = add(10,20);
console.log(result); // 30

همچنین می‌توانیم یک تابع را با کلمه کلیدی newبه عنوان تابع سازنده فراخوانی کنیم:

let obj = new add(10,20);

ES6 شبه خاصیت new.target را معرفی کرده و به ما این امکان را می‌دهد تا بتوانیم تشخیص دهیم که آیا یک تابع معمولی یا تابع سازنده با استفاده از عملگر new فراخوانی شده است یا خیر.

اگر تابعی به طور معمولی فراخوانی شود، new.target در این صورت undefinedاست. با این حال، اگر تابع با استفاده از کلمه کلیدی new به عنوان سازنده فراخوانی شود، new.target رفرنسی را به تابع سازنده برمی‌گرداند. به عنوان مثال:

function add(x, y) {
  console.log(new.target);
  return x + y;
}

let result = add(10, 20);
let obj = new add(10, 20);

خروجی کد به صورت زیر است:

undefined
[Function: add]

با استفاده از new.target می‌توانیم نحوه فراخوانی یک تابع را نیز کنترل کنیم.

به عنوان مثال، برای جلوگیری از فراخوانی تابع add()به عنوان سازنده با استفاده از کلمه کلیدی new، می‌توانیم با بررسی new.target به شکل زیر خطا ایجاد کنیم:

function add(x, y) {
  if (new.target) {
    throw 'The add function cannot be called as a constructor';
  }
  return x + y;
}

let obj = new add(10, 20);
console.log(obj);

متدهای فانکشن: apply، call و bind

یک آبجکت تابع سه متد مهم دارد:  apply(), call() و bind().در ادامه هر کدام از این متدها را بررسی خواهیم کرد.

متدهای ()apply و ()call

متدهای apply()و call()یک تابع با مقدار thisو آرگومان‌های داده شده را فراخوانی می‌کنند.

تفاوت بین این دو متد در این است که ما باید آرگومان‌ها را به عنوان یک آبجکت آرایه مانند به apply()منتقل کنیم، در حالی که در تابع call()آرگومان‌ها را به صورت جداگانه ارسال می‌کنیم. مثلا:

let cat = { type: 'Cat', sound: 'Meow' };
let dog = { type: 'Dog', sound: 'Woof' };

const say = function (message) {
  console.log(message);
  console.log(this.type + ' says ' + this.sound);
};

say.apply(cat, ['What does a cat say?']);
say.apply(dog, ['What does a dog say?']);

خروجی به شکل زیر است:

What does a cat sound?
Cat says Meow
What does a dog sound?
Dog says Woof

در این مثال، ابتدا دو آبجکت catو dogرا با دو ویژگی تعریف می‌کنیم:

let cat = { type: 'Cat', sound: 'Meow' };
let dog = { type: 'Dog', sound: 'Woof' };

در مرحله دوم تابع say()را تعریف می‌کنیم که یک آرگومان می‌گیرد:

const say = function (message) {
  console.log(message);
  console.log(this.type + ' says ' + this.sound);
};

سپس در مرحله سوم تابع say()را توسط متد apply()فراخوانی می‌کنیم:

say.apply(cat, ['What does a cat say?']);

در این مثال اولین آرگومان متد apply()آبجکت catاست. بنابراین thisدر تابع say() به آبجکت cat اشاره می‌کند.

در مرحله چهارم تابع say()را فراخوانی می‌کنیم و آبجکت dogرا به آن ارسال می‌کنیم:

say.apply(dog, ['What does a dog say?']);

اما اینجا this در تابع say() به آبجکت dog اشاره می‌کند.

متد call()مانند متد apply()است اما فقط روشی که آرگومان‌ها را به تابع ارسال می‌کنیم متفاوت است:

say.call(cat, 'What does a cat say?');
say.call(dog, 'What does a dog say?');

متد ()bind

bind()یک نمونه تابع جدید ایجاد می‌کند که این مقدار به آبجکتی که ما ارائه می‌کنیم محدود می‌شود. مثلا:

ابتدا یک آبجکت به نام carتعریف می‌کنیم:

let car = {
    speed: 5,
    start: function() {
        console.log('Start with ' + this.speed + ' km/h');
    }
};

سپس آبجکت دیگری به نام aircraftرا تعریف می‌کنیم:

let aircraft = {
    speed: 10,
    fly: function() {
        console.log('Flying');
    }
};

aircraftمتد start()ندارد. برای این که بتوانیم aircraft را راه‌اندازی کنیم، می‌توانیم به کمک bind()از متد start()آبجکت carاستفاده کنیم:

let taxiing = car.start.bind(aircraft);

در این عبارت، مقدار thisرا در متد start()آبجکت carبه آبجکت aircraftتغییر می‌دهیم. متد bind()تابع جدیدی را برمی‌گرداند که به متغیر taxiingاختصاص داده شده است.

اکنون می‌توانیم متد start()را از طریق متغیر taxiingفراخوانی کنیم:

taxiing();

در این صورت پیغام زیر را خواهیم دید:

Start with 10 km/h

همانطور که می‌بینید، متد bind()یک تابع جدید ایجاد می‌کند که می‌توانیم بعداً آن را اجرا کنیم در حالی که متد call()بلافاصله تابع را اجرا می‌کند. همین موضوع تفاوت اصلی بین متدهای bind()و call()است.

از نظر فنی آبجکت aircraftمتد start()آبجکت carرا از طریق متد apply(), call() و bind() قرض می‌گیرد. به همین دلیل است که متدهای apply(), call() و bind()نیز به عنوان توابع borrowing شناخته می‌شوند.

جمع‌بندی