جاوا اسکریپت به عنوان یکی از بهترین زبانهای برنامه نویسی، حاوی توابع و متدهای بسیار زیادی است که میتوان از آنها برای انجام کارهای خاص و کاربردی استفاده کرد. یکی از این متدهای پرکاربرد، متد «bind» است. متد bind در جاوا اسکریپت برای ایجاد تابعی جدید با «زمینهای» (Context) خاص استفاده میشود که معمولاً به آن تابع «binding» میگویند. این تابع به کاربر اجازه میدهد تا مقدار This در جاوا اسکریپت را به صراحت در تابع ایجاد شده جدید تنظیم کند. در این مطلب از «مجله تم آف» میخواهیم در رابطه با متد bind در جاوا اسکریپت و رسالت آن به همراه ارائه چندین مثال کاربردی توضیحاتی را ارائه کنیم. کاربران با مطالعه این مطلب درک مطلوبی از bind در زبان برنامه نویسی جاوا اسکریپت پیدا خواهند کرد.
bind در جاوا اسکریپت چیست؟
متد bind در جاوا اسکریپت تابع جدیدی را برمیگرداند که در زمان فراخوانی، مقدار This روی مقدار ارائه شده خاصی تنظیم خواهد شد. این امر به کاربر امکان میدهد تا نسخهای تغییر یافته از تابع را با زمینهای ثابت برای This ایجاد کند.
«سینتکس» (Syntax) متد bind در Javascript به صورت زیر است:
fn.bind(thisArg[, arg1[, arg2[, ...]]])
در سینتکس بالا، متد bind()
تابعی جدید ایجاد میکند که نوعی کپی از تابع اصلی fn
محسوب میشود. تابع جدید دارای مقدار This
خاص ( thisArg
) است و همچنین میتواند آرگومانهای اضافی ( arg1
، arg2
و غیره) را بپذیرد. توجه به این نکته مهم است که بر خلاف متدهای call()
و application()
، متد bind در جاوا اسکریپت بلافاصله تابع را اجرا نمیکند. در عوض، نسخه جدیدی از تابع را با مقدار This
بر روی آرگومان ارائه شده thisArg
بازمیگرداند. این به کاربر امکان میدهد تا تابع محدود شده را بعداً ذخیره و استفاده کند یا آن را به عنوان نوعی تماس ارسال کند، بدون اینکه زمینه This
مورد نظر را از دست بدهد.
مثالی برای درک bind در جاوا اسکریپت
فرض میشود که ۲ شخص به نامهای alice
و bob
هر کدام سبد میوه دارند که در داخل سبد alice
پرتقال و در داخل سبد bob
انگور وجود دارد.
همچنین سبد میوه bob
در داخل سبد میوه alice
مانند شکل زیر قرار گرفته است.
در جاوا اسکریپت، This
به شیئی اشاره میکند که تابع فعلی در حال فراخوانی آن است. بنابراین، وقتی alice
تابعی را برای دریافت میوه از سبد خود فراخوانی میکند، به This
به عنوان سبد خودش اشاره خواهد کرد که حاوی پرتقال است. قطعه کد زیر برای بیان این مفهوم آورده شده است.
var alice = {
fruit: "orange",
getFruit: function() {
return this.fruit;
}
};
alice.getFruit(); // "orange"
حال، اگر bob
بخواهد تابع getFruit
را فراخوانی کند، کار نخواهد کرد، زیرا تابع نام برده نوعی ویژگی سبد او نیست. در عوض، bob
خطایی دریافت میکند، زیرا تابع getFruit
در سبد او وجود ندارد.
var bob = {
fruit: "grape",
basket: {
getFruit: function() {
return this.fruit;
}
}
};
bob.basket.getFruit(); // Error: Cannot read property 'fruit' of undefined
برای حل این مشکل میتوان از تابع bind
استفاده کرد. bind
به کاربر اجازه میدهد که مقدار This
را به صراحت تعیین کند. در این حالت، bob
میخواهد تابع را به سبد خود متصل کند تا به سبدش اشاره کند، نه شی سراسری که برای درک بهتر، کدهای زیر ارائه شدهاند.
var bob = {
fruit: "grape",
basket: {
getFruit: function() {
return this.fruit;
}
}
};
var getBobFruit = bob.basket.getFruit.bind(bob);
getBobFruit(); // "grape"
با استفاده از bind در جاوا اسکریپت، باب اکنون میتواند تابع getFruit
را فراخوانی و نتیجه صحیح را دریافت کند، زیرا این تابع اکنون به سبد او اشاره دارد.
متد bind در جاوا اسکریپت برای اتصال تابع
در جاوا اسکریپت، زمانی که متد شیئی را به عنوان نوعی فراخوانی به تابع دیگری ارسال میکند، زمینه This
امکان دارد از بین برود یا تغییر کند.
مثال زیر برای درک این مفهوم آورده شده است:
let person = {
name: 'John Doe',
getName: function() {
console.log(this.name);
}
};
setTimeout(person.getName, 1000);
خروجی کد بالا به جای John Doe
، «تعریف نشده» ( undefined
) خواهد بود. این اتفاق به این دلیل رخ خواهد داد که setTimeout()
تابع، person.getName
را به طور جداگانه از شی person
دریافت میکند. برای رفع این مشکل، چند راهحل وجود دارد. روش اول این است که تماس با person.getName
را در «تابعی ناشناس» (Anonymous Function) به صورت زیر قرار دهیم:
setTimeout(function () {
person.getName();
}, 1000);
کد بالا بدون هیچ مشکل خاصی کار میکند، زیرا تابع ناشناس، شی person
را از محدوده بیرونی میگیرد و سپس متد getName()
را روی آن فراخوانی میکند. راهحل دیگر این مسئله، استفاده از متد bind در جاوا اسکریپت به صورت زیر است:
let f = person.getName.bind(person);
setTimeout(f, 1000);
در کد فوق از متد bind()
برای اتصال متد person.getName
به شی person
استفاده شده است. رویکرد بالا تابع جدید f
را ایجاد میکند که در آن مقدار This
به طور صریح برای شی person
تنظیم شده است. سپس f
به عنوان پاسخ تماس به setTimeout()
فرستاده خواهد شد و اطمینان حاصل میشود که متد getName()
با زمینه صحیح فراخوانی شده است. هر دوی این رویکردها به کاربر این امکان را میدهند که هنگام استفاده از متد شی به عنوان فراخوانی در جاوا اسکریپت، زمینه This
مورد نظر را حفظ کند.
استفاده از bind برای قرض گرفتن متدها از شیئی متفاوت
در جاوا اسکریپت، میتوان از متد bind()
برای قرض گرفتن متدی از شی و تنظیم زمینه This
بر روی شیئی دیگر استفاده کرد.
برای نشان دادن این موضوع، فرض میکنیم شی runner
با متد run()
به صورت زیر در دسترس است.
let runner = {
name: 'Runner',
run: function(speed) {
console.log(this.name + ' runs at ' + speed + ' mph.');
}
};
همچنین شی flyer
با متد fly()
به صورت زیر موجود است:
let flyer = {
name: 'Flyer',
fly: function(speed) {
console.log(this.name + ' flies at ' + speed + ' mph.');
}
};
برای اینکه شی flyer
بتواند با استفاده از متد run()
اجرا شود، میتوان از متد bind در جاوا اسکریپت برای ایجاد تابعی جدید با مقدار This
برای شی flyer
به صورت زیر استفاده کرد:
let run = runner.run.bind(flyer, 20);
run();
در کد فوق، متد bind()
در متد runner.run()
فراخوانی شده و شی flyer
به عنوان آرگومان اول و مقدار 20
به عنوان آرگومان دوم ارسال شده است. این کار تابعی جدید را اجرا میکند که به شی flyer
متصل بوده و آرگومان 20
نیز از پیش تنظیم شده است. در نهایت، تابع run()
فراخوانی خواهد شد که خروجی را ثبت میکند و این خروجی به صورت زیر است:
Flyer runs at 20 mph
با استفاده از bind()
، میتوان متد run()
را از شی runner
قرض گرفت و زمینه This
را بدون ایجاد نوعی کپی جداگانه از متد، روی شی flyer
تنظیم کرد. این به شی flyer
اجازه میدهد تا از متد run()
به گونهای استفاده کند که انگار مال خودش است. این توانایی برای قرض گرفتن متدها بین اشیا با استفاده از bind()
نوعی ویژگی قدرتمند در جاوا اسکریپت است، زیرا استفاده مجدد از کدها را ترویج کرده و از تکرار پیادهسازی متد جلوگیری میکند.
انواع رویکرد bind در جاوا اسکریپت
تابع bind در Javascript امکان سفارشیسازی رفتار و استفاده از توابع جاوا اسکریپت را با ایجاد نوعی زمینه خاص فراهم میکند.
در کل ۲ نوع رویکرد برای استفاده از bind در زبان برنامه نویسی جاوا اسکریپت وجود دارد که این ۲ نوع رویکرد به صوت زیر هستند.
- اتصال اشیا در جاوا اسکریپت با متد bind
- اتصال پارامتر با متد bind
در ادامه هر ۲ رویکرد نام برده شده مورد بررسی قرار گرفتهاند.
اتصال اشیا در جاوا اسکریپت با متد bind
زمانی که کاربر بخواهد تابع برای نمونهای خاص از کلاس کار خاصی را انجام دهد و بدون استفاده از آن نمونه قابل فراخوانی باشد، میتواند از متد bind در جاوا اسکریپت برای ایجاد نوعی کپی تغییر یافته از آن تابع استفاده کند. متد bind()
به کاربر امکان میدهد تابع را به شیئی خاص متصل کنیم که در آن کلمه کلیدی This
در تابع کپی شده، به شی محدود شده اشاره میکند.
مثال زیر برای درک این مفهوم بسیار مهم است.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Person.prototype.printName = function() {
console.log(this.name + ' ' + this.age);
}
var katy = new Person("Katy", 25);
var printKaty = Person.prototype.printName.bind(katy);
printKaty();
تصویر زیر نحوه انجام عملیات کد فوق را نشان میدهد.
در مثال فوق، کلاس Person
با متد printName()
موجود هستند. برای چاپ اطلاعات خاص، برای مثال Katy
، بدون نوشتن تابعی جدید، میتوان از متد bind()
استفاده کرد. با ارسال شی Katy
(که نمونهای از کلاس Person
است) به عنوان اولین آرگومان برای bind()
، نوعی کپی تغییر یافته از متد printName()
ایجاد میشود که به شی Katy
محدود شده است. کلمه کلیدی this
در تابع printKaty
اکنون به شی Katy
اشاره دارد.
با فراخوانی printKaty
، کپی اصلاح شده متد printName()
اجرا میشود و خروجی Katy 25
را در کنسول ثبت میکند. این کار به کاربر اجازه میدهد تا نوعی تابع ( printKaty
) ایجاد کند که به طور خاص به نوعی شی ( Katy
) متصل است و میتواند مستقل از آن شی فراخوانی شود، در حالی که همچنان به ویژگیها و متدهای آن دسترسی وجود دارد.
اتصال پارامتر با متد bind در جاوا اسکریپت
هنگامی که کاربر نیاز به تعیین مقادیر ثابت برای برخی از آرگومانهای تابع دارد، میتواند با ارسال آن مقادیر به عنوان آرگومان به متد bind()
بعد از اولین آرگومان به این امر دست یابد. این کار تابعی جدید ایجاد میکند که در آن آرگومانهای مشخص شده محدود شدهاند و زمانی که تابع جدید با آرگومانهای محدود نشده باقی مانده فراخوانی میشود، آنها به آرگومانهای محدود اضافه میشوند و تابع اصلی فراخوانی خواهد شد.
مثال زیر برای بیان این موضوع آورده شده است.
function multiply(a, b){
return a * b;
}
var double = multiply.bind(this, 2);
console.log(double(3));
تصویر زیر نحوه انجام عملیات مثال بالا را نشان میدهد:
در مثال فوق، تابع multiply()
موجود است که ۲ عدد را در یکدیگر ضرب خواهد کرد. برای ایجاد تابع جدید double(x)
که همیشه x
را در 2
ضرب میکند، بدون اینکه صریحاً اعلان شود، میتوان از متد bind در جاوا اسکریپت استفاده کرد.
با فراخوانی multiply.bind(this, 2)
، مقدار 2
به اولین آرگومان تابع multiply()
متصل میشود. این نوعی تابع جدید به نام double
ایجاد میکند که در آن آرگومان اول به صورت مقدار 2
ثابت میشود. همچنین هنگامی که double(3)
فراخوانی شود، آرگومان باقیمانده 3
به تابع double
ارسال خواهد شد. در داخل، 2
(آگومان محدود شده) در 3
ضرب میشود و نتیجه 6
در کنسول چاپ خواهد شد.
با اتصال نوعی پارامتر با استفاده از bind()
، تابعی جدید ایجاد میشود که دارای آرگومان خاص از پیش تنظیم شده است که به کاربر امکان میدهد از تابع اصلی با مقداری ثابت برای پارامتری خاص استفاده مجدد کند.
توابع جزئی چه هستند؟
«توابع جزئی» (Partial Functions) در جاوا اسکریپت به کاربر این امکان را میدهند تا نه تنها زمینه This، بلکه برخی از آرگومانهای تابع را نیز متصل کند. این کاربرد میتواند در برخی از سناریوها مفید باشد.
تابع bind در جاوا اسکریپت سینتکس کاملی را برای ایجاد تابع جزئی به صورت زیر فراهم میکند.
let bound = func.bind(context, [arg1], [arg2], ...);
با استفاده از bind()
، میتوان زمینه را به صورت This
پیوند داد و همچنین آرگومانهای شروع را برای تابع ارائه کرد. برای این مفهوم، مثالی از ضرب ۲ عدد به صورت زیر آورده شده است:
function mul(a, b) {
return a * b;
}
میتوان تابعی جدید به نام double
را بر اساس mul
با استفاده از bind()
به صورت زیر استفاده کرد.
function mul(a, b) {
return a * b;
}
let double = mul.bind(null, 2);
alert( double(3) ); // = mul(2, 3) = 6
alert( double(4) ); // = mul(2, 4) = 8
alert( double(5) ); // = mul(2, 5) = 10
در مثال فوق، mul.bind(null, 2)
تابع دوگانهای ایجاد خواهد کرد که mul
را فراخوانی میکند، null
به عنوان زمینه و مقدار ۲ به عنوان اولین آرگومان ارسال میشوند. هر آرگومان دیگری که به double
ارسال شود، مستقیماً به mul
ارسال خواهد شد. به این تکنیک، کاربرد تابع جزئی میگویند که در آن کاربر تابعی جدید را با ثابت کردن برخی از پارامترهای موجود ایجاد میکند.
توجه به این نکته، مهم است که در مثالهای بالا، در واقع از This
استفاده نشده است، ولی با این حال از آنجایی که bind()
به این آرگومان نیاز دارد، میتوان از چیزی مانند null
استفاده کرد.
کاربرد تابع جزئی در جاوا اسکریپت چیست؟
کاربرد تابع جزئی به ویژه زمانی مفید است که کاربر نوعی تابع عمومی دارد و برای ایجاد توابع تخصصی در شی یا ماژول به آن تکیه خواهد کرد. به طور کلی، توابع جزئی با اجازه دادن به کاربر برای ایجاد توابع جدید با آرگومانهای از پیش تعیین شده و در عین حال دست نخورده نگهداشتن تابع اصلی، انعطاف و راحتی را ارائه میدهند.
استفاده از توابع جزئی بدون زمینه
اگر کاربری بخواهد آرگومانهای خاصی را از تابع بدون اتصال به this context
یا زمینه This
تصحیح کند، متد bind در جاوا اسکریپت به او این امکان را نخواهد داد که مستقیماً این کار را انجام دهد. با این حال، به راحتی میتوان تابع جزئی سفارشی را پیاده سازی کرد که فقط آرگومانها را متصل میکند. در پایین مثالی از اجرای تابع جزئی با چنین رویکردی آورده شده است:
function partial(func, ...argsBound) {
return function(...args) { // (*)
return func.call(this, ...argsBound, ...args);
}
}
در کد بالا تابع جزئی نوعی پارامتر func
را میگیرد که نشان دهنده تابعی است که باید اعمال شود. argsBound
هم آرگومانهایی را نشان میدهد که باید محدود شوند. تابع func
، تابع wrapper (*)
را بازمیگرداند که هنگام فراخوانی، تابع را با موارد زیر فراخوانی میکند.
- مقداری که دریافت میکند (به عنوان مثال، user
در مورد user.sayNow
)
- آرگومانهای argsBound
از فراخوانی جزئی، مانند 10:00
.
- آرگومانهای args
ارائه شده به wrapper
، مانند Hello
.
این به کاربر اجازه میدهد تا آرگومانهای خاصی از تابع ثابت شوند و این در حالی است که زمینه This
دست نخورده باقی میماند. به عنوان مثال، شی user
را با متد say
به صورت زیر در نظر میگیریم:
let user = {
firstName: "John",
say(time, phrase) {
alert(`[${time}] ${this.firstName}: ${phrase}!`);
}
};
میتوان با استفاده از تابع جزئی نوعی متد جزئی مانند sayNow
را با زمان ثابت به کدها به صورت زیر اضافه کرد:
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
user.sayNow("Hello");
// Output: [10:00] John: Hello!
در مثال فوق، user.sayNow
تابعی است که با استفاده از partial(user.say، new Date().getHours() + ‘:’ + new Date().getMinutes())
ایجاد شده است که متد say
را به user
متصل میکند. در کدهای فوق همچنین بخش Date
بر اساس زمان فعلی هنگامی که user.sayNow
فراخوانی میشود پیام Hello
را با زمان ثابت و عبارت ارائه شده نمایش میدهد. با استفاده از تابع جزئی، میتوان به راحتی توابعی را با آرگومانهای از پیش تنظیم شده ایجاد کرد که به انعطافپذیری و قابلیت استفاده مجدد بیشتر میانجامد.
نکته: باید به این نکته توجه داشت که اجرای جزئی تابع نشان داده شده در مثال فوق ساده شده است و همه موارد استفاده ممکن را پوشش نمیدهد. همچنین کتابخانههایی مانند «lodash» یا «Ramda» وجود دارند که اجرای جامع تری از کاربرد تابع جزئی و ترکیب تابع را ارائه میدهند که بحث در مورد آنها خارج از محدوده این نوشته است.
سخن پایانی
متد bind در جاوا اسکریپت به کاربر اجازه میدهد تا تابعی جدید را با مقدار This
خاص و آرگومانهای اولیه به صورت اختیاری ایجاد کند. این متد برای قرض گرفتن تابع مفید است، جایی که شیئی میتواند متدی را از شی دیگر بدون ایجاد نوعی کپی قرض بگیرد.
علاوه بر این، bind()
کار با توابع جزئی را امکانپذیر میکند و به کاربر اجازه میدهد توابع تخصصی را با برخی از آرگومانهای از پیش تنظیم شده ایجاد کند. این کار به بهبود قابلیت استفاده مجدد و خوانایی کدها با اجتناب از آرگومانهای تکراری کمک میکند. به طور کلی، bind()
انعطافپذیری را در مدیریت آرگومانها و توابع فراهم میکند و تطبیقپذیری توابع جاوا اسکریپت را افزایش میدهد. در مطلب فوق از «مجله تم آف» متد bind در جاوا اسکریپت به همراه کاربردهای آن به صورت عملی آموزش داده شد و در رویکردهای مختلف استفاده از این متد مورد بررسی قرار گرفت.