در این مقاله قصد داریم تا با تایپ Function جاوااسکریپت که تایپ همه توابع در این زبان است، آشنا شویم.
در جاوااسکریپت، همه توابع یک آبجکت محسوب میشوند که نمونههایی از تایپ Function هستند. از آنجایی که توابع، آبجکت بهشمار میآیند مانند سایر آبجکتها دارای ویژگیها و روشهایی نیز میباشند.
هر تابع دو ویژگی مهم دارد: lengthو prototype.
length تعداد آرگومانهای نامگذاری شده که در تعریف تابع مشخص شدهاند را تعیین میکند.prototype به آبجکت تابع واقعی اشاره میکند.به مثال زیر توجه کنید:
function add(x, y) {
return x + y;
}
console.log(add.length); // 2
console.log(add.prototype); // Object{}
تابع add()دو آرگومان x و y را دریافت میکند. بنابراین، ویژگی lengthمقدار 2 را برمیگرداند.
به طور معمول، ما یک تابع را به صورت عادی و به شکل زیر فراخوانی میکنیم:
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()یک تابع با مقدار 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()یک نمونه تابع جدید ایجاد میکند که این مقدار به آبجکتی که ما ارائه میکنیم محدود میشود. مثلا:
ابتدا یک آبجکت به نام 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 شناخته میشوند.
lengthو prototypeapply(), call() و bind()