ÐеÑедаÑÑи меÑоди обâÑкÑÑ Ð² ÑкоÑÑÑ ÐºÐ¾Ð»Ð±ÐµÐºÑв, напÑиклад в setTimeout, ÑÑнÑÑ Ð²Ñдома пÑоблема: âвÑÑаÑа thisâ.
Ð ÑÑÐ¾Ð¼Ñ ÑоздÑÐ»Ñ Ð¼Ð¸ ÑозглÑнемо ÑпоÑоби Ñк Ñе можливо випÑавиÑи.
ÐÑÑаÑа âthisâ
Ðи вже ÑозглÑдали пÑиклади вÑÑаÑи this. ЯкÑо меÑод пеÑедаÑи окÑемо вÑд обâÑкÑа â this вÑÑаÑаÑÑÑÑÑ.
РпÑÐ¸ÐºÐ»Ð°Ð´Ñ Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ð¾ Ñк Ñе вÑдбÑваÑÑÑÑÑ Ð· setTimeout:
let user = {
firstName: "Ðван",
sayHi() {
alert(`ÐÑивÑÑ, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // ÐÑивÑÑ, undefined!
Як ми баÑимо, в модалÑÐ½Ð¾Ð¼Ñ Ð²ÑÐºÐ½Ñ Ð±ÑаÑзеÑа вÑдобÑажаÑÑÑÑÑ Ð½Ðµ "Ðван" Ñк this.firstName, а undefined!
Це ÑÐ¾Ð¼Ñ Ñо setTimeout оÑÑимав ÑÑнкÑÑÑ user.sayHi окÑемо вÑд обâÑкÑа. ÐÑÑаннÑй ÑÑдок може бÑÑи пеÑепиÑаний наÑÑÑпним Ñином:
let f = user.sayHi;
setTimeout(f, 1000); // вÑÑаÑа конÑекÑÑÑ Ð¾Ð±âÑкÑа user
ÐеÑод setTimeout в бÑаÑзеÑÑ ÑÑоÑ
и оÑобливий: вÑн вÑÑановлÑÑ this=window пÑд ÑÐ°Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑ ÑÑнкÑÑÑ (в Node.js this ÑÑÐ°Ñ Ð¾Ð±âÑÐºÑ ÑаймеÑÑ, але Ñе вÑе одно не Ñе, Ñо нам поÑÑÑбно). Таким Ñином Ð´Ð»Ñ this.firstName меÑод намагаÑÑÑÑÑ Ð¾ÑÑимаÑи window.firstName, Ñкого не ÑÑнÑÑ. Ð ÑнÑиÑ
ÑÑ
ожиÑ
випадкаÑ
, зазвиÑай this пÑоÑÑо ÑÑÐ°Ñ undefined.
ÐадаÑа доÑиÑÑ Ñипова â ми Ñ Ð¾Ñемо пеÑедаÑи меÑод обâÑкÑÑ Ð´ÐµÑнде (в ÑÑÐ¾Ð¼Ñ Ð²Ð¸Ð¿Ð°Ð´ÐºÑ â в планÑвалÑник) де вÑн бÑде викликаний. Як бÑÑи впевненими в ÑомÑ, Ñо Ñей меÑод обâÑкÑа бÑде викликаний з пÑавилÑним конÑекÑÑом?
Ð ÑÑÐµÐ½Ð½Ñ 1: обгоÑÑка
ÐайпÑоÑÑÑÑе ÑÑÑÐµÐ½Ð½Ñ â викоÑиÑÑаÑи ÑÑнкÑÑÑ Ð¾Ð±Ð³Ð¾ÑÑкÑ:
let user = {
firstName: "Ðван",
sayHi() {
alert(`ÐÑивÑÑ, ${this.firstName}!`);
}
};
setTimeout(function() {
user.sayHi(); // ÐÑивÑÑ, Ðван!
}, 1000);
Ð¢ÐµÐ¿ÐµÑ Ñе пÑаÑÑÑ, бо ми оÑÑимали user з зовнÑÑнÑого лекÑиÑного оÑоÑеннÑ, Ñ Ð¿Ð¾ÑÑм викликали меÑод.
ÐналогÑÑний запиÑ, ÑÑлÑки коÑоÑÑий:
setTimeout(() => user.sayHi(), 1000); // ÐÑивÑÑ, Ðван!
ÐиглÑÐ´Ð°Ñ ÑÑдово, але зâÑвлÑÑÑÑÑÑ Ð»ÐµÐ³ÐºÐ° вÑазливÑÑÑÑ Ð² ÑÑÑÑкÑÑÑÑ Ð½Ð°Ñого кодÑ.
Що ÑкÑо пеÑед ÑпÑаÑÑваннÑм setTimeout (з однÑÑÑ ÑекÑÐ½Ð´Ð¾Ñ Ð·Ð°ÑÑимки!) змÑнна user змÑниÑÑ ÑÐ²Ð¾Ñ Ð·Ð½Ð°ÑеннÑ? ТодÑ, неоÑÑкÑвано, наÑа обгоÑÑка виклиÑе непÑавилÑний обâÑкÑ!
let user = {
firstName: "Ðван",
sayHi() {
alert(`ÐÑивÑÑ, ${this.firstName}!`);
}
};
setTimeout(() => user.sayHi(), 1000);
// ...знаÑÐµÐ½Ð½Ñ user змÑнÑÑÑÑÑÑ Ð²Ð¿Ñодовж 1 ÑекÑнди
user = {
sayHi() { alert("ÐнÑий user в setTimeout!"); }
};
// ÐнÑий user в setTimeout!
ÐаÑÑÑпне ÑÑÑÐµÐ½Ð½Ñ Ð³Ð°ÑанÑÑÑ, Ñо Ñака ÑиÑÑаÑÑÑ Ð½Ðµ ÑÑапиÑÑÑÑ.
Ð ÑÑÐµÐ½Ð½Ñ 2: пÑивâÑзка (bind)
ФÑнкÑÑÑ Ð½Ð°Ð´Ð°ÑÑÑ Ð½Ð°Ð¼ вбÑдований меÑод bind, Ñо дозволÑÑ Ð·Ð±ÐµÑегÑи пÑавилÑний this.
ÐÑновний ÑинÑакÑиÑ:
// бÑлÑÑ Ñкладний ÑинÑакÑÐ¸Ñ Ð±Ñде ÑÑоÑ
и пÑзнÑÑе
let boundFunc = func.bind(context);
РезÑлÑÑаÑом func.bind(context) бÑде певний âекзоÑиÑний обâÑкÑâ, Ñкий може бÑÑи викликаний Ñк ÑÑнкÑÑÑ Ñа пеÑÐµÐ´Ð°Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑ func вÑÑановлений конÑекÑÑ this=context.
ÐнÑими Ñловами, виклик boundFunc Ñе Ñк виклик func з пÑивâÑзаним this.
ÐапÑиклад, ÑÑÑ funcUser пеÑÐµÐ´Ð°Ñ Ð²Ð¸ÐºÐ»Ð¸Ðº func з this=user:
let user = {
firstName: "Ðван"
};
function func() {
alert(this.firstName);
}
let funcUser = func.bind(user);
funcUser(); // Ðван
ТÑÑ func.bind(user) Ñк âпÑивâÑзаний ваÑÑанÑâ ÑÑнкÑÑÑ func, з пÑивâÑзаним this=user.
ÐÑÑ Ð°ÑгÑменÑи пеÑедаÑÑÑÑÑ Ð¿Ð¾ÑаÑковÑй ÑÑнкÑÑÑ func âÑк Ñâ, напÑиклад:
let user = {
firstName: "Ðван"
};
function func(phrase) {
alert(phrase + ', ' + this.firstName);
}
// пÑивâÑзка до user
let funcUser = func.bind(user);
funcUser("ÐÑивÑÑ"); // ÐÑивÑÑ, Ðван (пеÑеданий аÑгÑÐ¼ÐµÐ½Ñ "ÐÑивÑÑ" Ñа this=user)
Ð¢ÐµÐ¿ÐµÑ ÑпÑобÑÑмо з меÑодом обâÑкÑÑ:
let user = {
firstName: "Ðван",
sayHi() {
alert(`ÐÑивÑÑ, ${this.firstName}!`);
}
};
let sayHi = user.sayHi.bind(user); // (*)
// можемо викликаÑи без обâÑкÑÑ
sayHi(); // ÐÑивÑÑ, Ðван!
setTimeout(sayHi, 1000); // ÐÑивÑÑ, Ðван!
// навÑÑÑ ÑкÑо знаÑÐµÐ½Ð½Ñ user змÑниÑÑÑÑ Ð²Ð¿Ñодовж 1 ÑекÑнди
// sayHi викоÑиÑÑовÑÑ Ð¿ÑивâÑзане знаÑеннÑ, Ñке поÑилаÑÑÑÑÑ Ð½Ð° ÑÑаÑий обâÑÐºÑ user
user = {
sayHi() { alert("ÐнÑий user в setTimeout!"); }
};
Ð ÑÑÐ´ÐºÑ (*) ми взÑли меÑод user.sayHi Ñа пÑивâÑзали його до user. sayHi Ñ âпÑивâÑзаноÑâ ÑÑнкÑÑÑÑ, Ñо може бÑÑи викликана окÑемо або пеÑедана до setTimeout. Ðе важливо де вона бÑде викликана, конÑекÑÑ Ð±Ñде заданий нами.
Ð ÑÑÐ¾Ð¼Ñ Ð¿ÑÐ¸ÐºÐ»Ð°Ð´Ñ Ð¼Ð¸ баÑимо, Ñо аÑгÑменÑи пеÑÐµÐ´Ð°Ð½Ñ âÑк Ñâ, ÑÑлÑки this змÑнено за Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ bind:
let user = {
firstName: "Ðван",
say(phrase) {
alert(`${phrase}, ${this.firstName}!`);
}
};
let say = user.say.bind(user);
say("ÐÑивÑÑ"); // ÐÑивÑÑ, Ðван! (ÐÑгÑÐ¼ÐµÐ½Ñ "ÐÑивÑÑ" пеÑеданий ÑÑнкÑÑÑ say)
say("ÐÑвай"); // ÐÑвай, Ðван! ("ÐÑвай" пеÑедане ÑÑнкÑÑÑ say)
bindAllЯкÑо обâÑÐºÑ Ð¼ÑÑÑиÑÑ Ð±Ð°Ð³Ð°Ñо меÑодÑв Ñа ми планÑÑмо акÑивно ÑÑ Ð¿ÐµÑедаваÑи, ÑÐ¾Ð´Ñ Ð¼Ð¸ можемо пÑивâÑзаÑи ÑÑ Ð²ÑÑÑ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ ÑиклÑ:
for (let key in user) {
if (typeof user[key] == 'function') {
user[key] = user[key].bind(user);
}
}
JavaScript бÑблÑоÑеки Ñакож пÑопонÑÑÑÑ ÑÑнкÑÑÑ Ð´Ð»Ñ Ð·ÑÑÑÐ½Ð¾Ñ Ð¼Ð°ÑÐ¾Ð²Ð¾Ñ Ð¿ÑивâÑзки, напÑиклад _.bindAll(object, methodNames) в бÑблÑоÑеÑÑ lodash.
ЧаÑÑкове заÑÑоÑÑÐ²Ð°Ð½Ð½Ñ (partial function)
Ðо ÑÑого моменÑÑ Ð¼Ð¸ говоÑили ÑÑлÑки пÑо пÑивâÑÐ·ÐºÑ this. ÐÑобимо кÑок далÑ.
Ðи можемо пÑивâÑзаÑи не ÑÑлÑки this, а Ñакож аÑгÑменÑи. Це ÑобиÑÑÑÑ ÑÑдко, пÑоÑе може бÑÑи Ñнколи дÑже зÑÑÑним.
Ðовний ÑинÑакÑÐ¸Ñ bind:
let bound = func.bind(context, [arg1], [arg2], ...);
Це дозволÑÑ Ð¿ÑивâÑзаÑи context Ñк this Ñа поÑаÑÐºÐ¾Ð²Ñ Ð°ÑгÑменÑи ÑÑнкÑÑÑ.
ÐапÑиклад, ми маÑмо ÑÑнкÑÑÑ Ð¼Ð½Ð¾Ð¶ÐµÐ½Ð½Ñ mul(a, b):
function mul(a, b) {
return a * b;
}
ÐикоÑиÑÑаÑмо bind Ñоб ÑÑвоÑиÑи ÑÑнкÑÑÑ double на ÑÑ Ð¾ÑновÑ:
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) ÑÑвоÑÑÑ Ð½Ð¾Ð²Ñ ÑÑнкÑÑÑ double Ñо пеÑÐµÐ´Ð°Ñ Ð²Ð¸ÐºÐ»Ð¸Ðº mul, вÑÑановлÑÑи null Ñк конÑекÑÑ Ñа 2 Ñк пеÑÑий аÑгÑменÑ. ÐодалÑÑÑ Ð°ÑгÑменÑи пеÑедаÑÑÑÑÑ âÑк Ñâ.
Це називаÑÑÑÑÑ ÑаÑÑкове заÑÑоÑÑÐ²Ð°Ð½Ð½Ñ â ми ÑÑвоÑÑÑмо Ð½Ð¾Ð²Ñ ÑÑнкÑÑÑ Ð¿ÑивâÑзавÑи деÑÐºÑ Ð¿Ð°ÑамеÑÑи ÑÑнÑÑÑоÑ.
ÐвеÑнÑÑÑ ÑвагÑ, Ñо ми не викоÑиÑÑовÑвали this в ÑÑÐ¾Ð¼Ñ Ð¿ÑикладÑ. ÐÑоÑе bind Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ Ð¿ÐµÑÑим аÑгÑменÑом ÑоÑÑ, Ñо бÑде пÑивâÑзане Ñк this, ÑÐ¾Ð¼Ñ Ð¼Ð¸ мÑÑимо пеÑедаÑи ÑоÑÑ Ñк заглÑÑÐºÑ â null.
ФÑнкÑÑÑ triple в ÐºÐ¾Ð´Ñ Ð½Ð¸Ð¶Ñе поÑÑоÑÑ Ð·Ð½Ð°ÑеннÑ:
function mul(a, b) {
return a * b;
}
let triple = mul.bind(null, 3);
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
Ð§Ð¾Ð¼Ñ Ð¼Ð¸ викоÑиÑÑовÑÑмо ÑаÑÑкове заÑÑоÑÑваннÑ?
ÐеÑÐµÐ²Ð°Ð³Ð¾Ñ ÑÑого Ñ Ñе, Ñо ми можемо ÑÑвоÑиÑи Ð½ÐµÐ·Ð°Ð»ÐµÐ¶Ð½Ñ ÑÑнкÑÑÑ Ð· ÑиÑабелÑним ÑмâÑм (double, triple). Ðи можемо викоÑиÑÑовÑваÑи ÑÑ Ñа не пеÑедаваÑи пеÑÑий аÑгÑÐ¼ÐµÐ½Ñ ÐºÐ¾Ð¶ÐµÐ½ Ñаз, оÑкÑлÑки Ñе замÑÑÑÑ Ð½Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½ÑÑ bind.
Ð ÑнÑÐ¸Ñ Ð²Ð¸Ð¿Ð°Ð´ÐºÐ°Ñ , ÑаÑÑкове заÑÑоÑÑÐ²Ð°Ð½Ð½Ñ Ñ ÐºÐ¾ÑиÑним, коли ми маÑмо дÑже загалÑÐ½Ñ ÑÑнкÑÑÑ Ñа Ñ Ð¾Ñемо Ð¼ÐµÐ½Ñ ÑнÑвеÑÑалÑний ÑÑ Ð²Ð°ÑÑÐ°Ð½Ñ Ð´Ð»Ñ Ð·ÑÑÑноÑÑÑ.
ÐапÑиклад, Ñ Ð½Ð°Ñ Ñ ÑÑнкÑÑÑ send(from, to, text). ТодÑ, в ÑеÑÐµÐ´Ð¸Ð½Ñ Ð¾Ð±âÑкÑÑ user ми можемо заÑ
оÑÑÑи викоÑиÑÑаÑи ÑÑ ÑаÑÑковий ваÑÑанÑ: sendTo(to, text), Ñка вÑдпÑавлÑÑ ÑекÑÑ Ð²Ñд поÑоÑного коÑиÑÑÑваÑа.
ÐикоÑиÑÑÐ°Ð½Ð½Ñ ÑаÑÑкового заÑÑоÑÑÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ· конÑекÑÑÑ
Що ÑкÑо ми Ñ
оÑемо пÑивâÑзаÑи деÑÐºÑ Ð°ÑгÑменÑи, але не конÑекÑÑ this? ÐапÑиклад, Ð´Ð»Ñ Ð¼ÐµÑÐ¾Ð´Ñ Ð¾Ð±âÑкÑа.
ÐбÑдований меÑод bind не дозволÑÑ ÑÑого. Ðи можемо пÑоÑÑо опÑÑÑиÑи конÑекÑÑ Ñа пеÑейÑи до аÑгÑменÑÑв.
Ðа ÑаÑÑÑ, ÑÑнкÑÑÑ partial Ð´Ð»Ñ Ð¿ÑивâÑзÑÐ²Ð°Ð½Ð½Ñ ÑÑлÑки аÑгÑменÑÑв може бÑÑи ÑеалÑзована дÑже пÑоÑÑо.
Як ÑÑÑ:
function partial(func, ...argsBound) {
return function(...args) { // (*)
return func.call(this, ...argsBound, ...args);
}
}
// ÐикоÑиÑÑаннÑ:
let user = {
firstName: "Ðван",
say(time, phrase) {
alert(`[${time}] ${this.firstName}: ${phrase}!`);
}
};
// Ñа меÑод partial з закÑÑпленим ÑаÑом
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
user.sayNow("ÐÑивÑÑ");
// Something like:
// [10:00] Ðван: ÐÑивÑÑ!
РезÑлÑÑаÑом Ð²Ð¸ÐºÐ»Ð¸ÐºÑ partial(func[, arg1, arg2...]) Ñ Ð¾Ð±Ð³Ð¾ÑÑка (*) Ñо Ð²Ð¸ÐºÐ»Ð¸ÐºÐ°Ñ func з:
- Тим же
thisÑке вона оÑÑимÑÑ (Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑuser.sayNowÑеuser) - ÐеÑÐµÐ´Ð°Ñ Ð¹Ð¾Ð¼Ñ
...argsBoundâ аÑгÑменÑи з викликÑpartial("10:00") - ÐоÑÑм пеÑÐµÐ´Ð°Ñ Ð¹Ð¾Ð¼Ñ
...argsâ аÑгÑменÑи оÑÑÐ¸Ð¼Ð°Ð½Ñ Ð· обгоÑÑки ("Hello")
Як баÑиÑе, Ñе легко ÑобиÑÑÑÑ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ Ð¾Ð¿ÐµÑаÑоÑÑ ÑозÑиÑеннÑ, Ñи не Ñак?
Також ÑÑнÑÑ Ð³Ð¾Ñова ÑеалÑзаÑÑÑ _.partial в бÑблÑоÑеÑÑ lodash.
ÐÑдÑÑмки
ÐеÑод func.bind(context, ...args) повеÑÑÐ°Ñ âпÑивâÑзаний ваÑÑанÑâ ÑÑнкÑÑÑ func Ñкий пÑивâÑзÑÑ ÐºÐ¾Ð½ÑекÑÑ this Ñа аÑгÑменÑи, ÑкÑо вони пеÑеданÑ.
ÐазвиÑай ми заÑÑоÑовÑÑмо bind, Ñоб пÑивâÑзаÑи this Ð´Ð»Ñ Ð¼ÐµÑÐ¾Ð´Ñ Ð¾Ð±âÑкÑа Ñа пеÑедаÑи його деÑнде. ÐапÑиклад, в setTimeout.
Ðоли ми пÑивâÑзÑÑмо деÑÐºÑ Ð°ÑгÑменÑи ÑÑнÑÑÑÐ¾Ñ ÑÑнкÑÑÑ, в ÑезÑлÑÑаÑÑ Ð¼Ð¸ оÑÑимÑÑмо Ð½Ð¾Ð²Ñ (Ð¼ÐµÐ½Ñ ÑнÑвеÑÑалÑнÑ) ÑÑнкÑÑÑ, Ñка називаÑÑÑÑÑ ÑаÑÑково заÑÑоÑÐ¾Ð²Ð°Ð½Ð¾Ñ Ð°Ð±Ð¾ ÑаÑÑÐºÐ¾Ð²Ð¾Ñ (partial).
ЧаÑÑÐºÐ¾Ð²Ñ ÑÑнкÑÑÑ Ð·ÑÑÑнÑ, коли ми не Ñ
оÑемо повÑоÑно пеÑедаваÑи Ð¾Ð´Ð½Ñ Ð¹ ÑÑ Ð¶ ÑÐ°Ð¼Ñ Ð°ÑгÑменÑи Ð·Ð½Ð¾Ð²Ñ Ñ Ð·Ð½Ð¾Ð²Ñ. ÐапÑиклад, ÑкÑо ми маÑмо ÑÑнкÑÑÑ send(from, to) Ñа аÑгÑÐ¼ÐµÐ½Ñ from поÑÑÑйно бÑде однаковим Ð´Ð»Ñ Ð½Ð°ÑÐ¾Ñ Ð¿Ð¾ÑоÑÐ½Ð¾Ñ Ð·Ð°Ð´Ð°ÑÑ, ми можемо зÑобиÑи ÑаÑÑково заÑÑоÑÐ¾Ð²Ð°Ð½Ñ ÑÑнкÑÑÑ Ñа викоÑиÑÑовÑваÑи ÑÑ.
ÐоменÑаÑÑ
<code>, Ð´Ð»Ñ ÐºÑлÑÐºÐ¾Ñ ÑÑдкÑв â обгоÑнÑÑÑ ÑÑ Ñегом<pre>, Ð´Ð»Ñ Ð¿Ð¾Ð½Ð°Ð´ 10 ÑÑдкÑв â викоÑиÑÑовÑйÑе пÑÑоÑниÑÑ (plnkr, jsbin, codepenâ¦)