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