JavaScript пÑедоÑÑавлÑÐµÑ ÑдивиÑелÑно гибкие возможноÑÑи по ÑабоÑе Ñ ÑÑнкÑиÑми: Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ пеÑедаваÑÑ, в Ð½Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ запиÑÑваÑÑ Ð´Ð°Ð½Ð½Ñе как в обÑекÑÑ, Ñ Ð½Ð¸Ñ ÐµÑÑÑ Ñвои вÑÑÑоеннÑе меÑодÑâ¦
ÐонеÑно, ÑÑим нÑжно ÑмеÑÑ Ð¿Ð¾Ð»ÑзоваÑÑÑÑ. Ð ÑÑой главе, ÑÑÐ¾Ð±Ñ Ð±Ð¾Ð»ÐµÐµ глÑбоко понимаÑÑ ÑабоÑÑ Ñ ÑÑнкÑиÑми, Ð¼Ñ ÑаÑÑмоÑÑим Ñоздание ÑÑнкÑий-обÑÑÑок или, инаÑе говоÑÑ, «декоÑаÑоÑов».
ÐекоÑаÑÐ¾Ñ â пÑиÑм пÑогÑаммиÑованиÑ, коÑоÑÑй позволÑÐµÑ Ð²Ð·ÑÑÑ ÑÑÑеÑÑвÑÑÑÑÑ ÑÑнкÑÐ¸Ñ Ð¸ измениÑÑ/ÑаÑÑиÑиÑÑ ÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ.
ÐекоÑаÑÐ¾Ñ Ð¿Ð¾Ð»ÑÑÐ°ÐµÑ ÑÑнкÑÐ¸Ñ Ð¸ возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÑÑкÑ, коÑоÑÐ°Ñ Ð´ÐµÐ»Ð°ÐµÑ ÑÑо-Ñо ÑÐ²Ð¾Ñ Â«Ð²Ð¾ÐºÑÑг» вÑзова оÑновной ÑÑнкÑии.
bind â пÑивÑзка конÑекÑÑа
Ðдин пÑоÑÑой декоÑаÑÐ¾Ñ Ð²Ñ Ñже видели Ñанее â ÑÑо ÑÑнкÑÐ¸Ñ bind:
function bind(func, context) {
return function() {
return func.apply(context, arguments);
};
}
ÐÑзов bind(func, context) возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÑÑкÑ, коÑоÑÐ°Ñ ÑÑÐ°Ð²Ð¸Ñ this и пеÑедаÑÑ Ð¾ÑновнÑÑ ÑабоÑÑ ÑÑнкÑии func.
ÐекоÑаÑоÑ-ÑаймеÑ
Создадим более ÑложнÑй декоÑаÑоÑ, замеÑÑÑÑий вÑÐµÐ¼Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑÑнкÑии.
Ðн бÑÐ´ÐµÑ Ð½Ð°Ð·ÑваÑÑÑÑ timingDecorator и полÑÑаÑÑ ÑÑнкÑÐ¸Ñ Ð²Ð¼ÐµÑÑе Ñ Â«Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼ ÑаймеÑа», а возвÑаÑаÑÑ â ÑÑнкÑиÑ-обÑÑÑкÑ, коÑоÑÐ°Ñ Ð¸Ð·Ð¼ÐµÑÑÐµÑ Ð²ÑÐµÐ¼Ñ Ð¸ пÑибавлÑÐµÑ ÐµÐ³Ð¾ в ÑпеÑиалÑнÑй обÑÐµÐºÑ timer по ÑвойÑÑвÑ-названиÑ.
ÐÑполÑзование:
function f(x) {} // лÑÐ±Ð°Ñ ÑÑнкÑиÑ
var timers = {}; // обÑÐµÐºÑ Ð´Ð»Ñ ÑаймеÑов
// оÑдекоÑиÑовали
f = timingDecorator(f, "myFunc");
// запÑÑкаем
f(1);
f(2);
f(3); // ÑÑнкÑÐ¸Ñ ÑабоÑÐ°ÐµÑ ÐºÐ°Ðº ÑанÑÑе, но вÑÐµÐ¼Ñ Ð¿Ð¾Ð´ÑÑиÑÑваеÑÑÑ
alert( timers.myFunc ); // обÑее вÑÐµÐ¼Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²ÑеÑ
вÑзовов f
ÐÑи помоÑи декоÑаÑоÑа timingDecorator Ð¼Ñ Ñможем взÑÑÑ Ð¿ÑоизволÑнÑÑ ÑÑнкÑÐ¸Ñ Ð¸ одним движением ÑÑки пÑикÑÑÑиÑÑ Ðº ней измеÑиÑÐµÐ»Ñ Ð²Ñемени.
Ðго ÑеализаÑиÑ:
var timers = {};
// пÑÐ¸Ð±Ð°Ð²Ð¸Ñ Ð²ÑÐµÐ¼Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ f к ÑаймеÑÑ timers[timer]
function timingDecorator(f, timer) {
return function() {
var start = performance.now();
var result = f.apply(this, arguments); // (*)
if (!timers[timer]) timers[timer] = 0;
timers[timer] += performance.now() - start;
return result;
}
}
// ÑÑнкÑÐ¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÑоизволÑной, напÑÐ¸Ð¼ÐµÑ Ñакой:
var fibonacci = function f(n) {
return (n > 2) ? f(n - 1) + f(n - 2) : 1;
}
// иÑполÑзование: завеÑнÑм fibonacci в декоÑаÑоÑ
fibonacci = timingDecorator(fibonacci, "fibo");
// неоднокÑаÑнÑе вÑзовÑ...
alert( fibonacci(10) ); // 55
alert( fibonacci(20) ); // 6765
// ...
// в лÑбой Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð¼Ð¾Ð¶Ð½Ð¾ полÑÑиÑÑ Ð¾Ð±Ñее колиÑеÑÑво вÑемени на вÑзовÑ
alert( timers.fibo + 'мÑ' );
ÐбÑаÑим внимание на ÑÑÑÐ¾ÐºÑ (*) внÑÑÑи декоÑаÑоÑа, коÑоÑÐ°Ñ Ð¸ оÑÑÑеÑÑвлÑÐµÑ Ð¿ÐµÑедаÑÑ Ð²Ñзова:
var result = f.apply(this, arguments); // (*)
ÐÑÐ¾Ñ Ð¿ÑиÑм назÑваеÑÑÑ Â«ÑоÑваÑдинг вÑзова» (Ð¾Ñ Ð°Ð½Ð³Ð». forwarding): ÑекÑÑий конÑекÑÑ Ð¸ аÑгÑменÑÑ ÑеÑез apply пеÑедаÑÑÑÑ Ð² ÑÑнкÑÐ¸Ñ f, Ñак ÑÑо изнÑÑÑи f вÑÑ Ð²ÑглÑÐ´Ð¸Ñ Ñак, как бÑла вÑзвана она напÑÑмÑÑ, а не декоÑаÑоÑ.
ÐекоÑаÑÐ¾Ñ Ð´Ð»Ñ Ð¿ÑовеÑки Ñипа
Ð JavaScript, как пÑавило, пÑенебÑегаÑÑ Ð¿ÑовеÑками Ñипа. Ð ÑÑнкÑиÑ, коÑоÑÐ°Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° полÑÑаÑÑ ÑиÑло, Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÐµÑедана ÑÑÑока, бÑлево знаÑение или даже обÑекÑ.
ÐапÑимеÑ:
function sum(a, b) {
return a + b;
}
// пеÑедадим в ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ ÑÐ»Ð¾Ð¶ÐµÐ½Ð¸Ñ ÑиÑел неÑиÑловÑе знаÑениÑ
alert( sum(true, { name: "ÐаÑÑ", age: 35 }) ); // true[Object object]
ФÑнкÑÐ¸Ñ Â«ÐºÐ°Ðº-Ñо» оÑÑабоÑала, но в ÑеалÑной жизни пеÑедаÑа в sum подобнÑÑ
знаÑений, ÑкоÑее вÑего, бÑÐ´ÐµÑ ÑледÑÑвием пÑогÑаммной оÑибки. ÐÑÑ-Ñаки sum пÑедназнаÑена Ð´Ð»Ñ ÑÑммиÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÑиÑел, а не обÑекÑов.
Ðногие ÑзÑки пÑогÑаммиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÑÑ Ð¿ÑÑмо в обÑÑвлении ÑÑнкÑии ÑказаÑÑ, какие ÑÐ¸Ð¿Ñ Ð´Ð°Ð½Ð½ÑÑ Ð¸Ð¼ÐµÑÑ Ð¿Ð°ÑамеÑÑÑ. Ð ÑÑо Ñдобно, поÑколÑÐºÑ Ð¿Ð¾Ð²ÑÑÐ°ÐµÑ Ð½Ð°Ð´ÑжноÑÑÑ ÐºÐ¾Ð´Ð°.
Ð JavaScript же пÑовеÑÐºÑ Ñипов пÑÐ¸Ñ Ð¾Ð´Ð¸ÑÑÑ Ð´ÐµÐ»Ð°ÑÑ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸ÑелÑнÑм кодом в наÑале ÑÑнкÑии, коÑоÑÑй во-пеÑвÑÑ Ð¾Ð±ÑÑно Ð»ÐµÐ½Ñ Ð¿Ð¸ÑаÑÑ, а во-вÑоÑÑÑ Ð¾Ð½ ÑвелиÑÐ¸Ð²Ð°ÐµÑ Ð¾Ð±Ñий обÑем ÑекÑÑа, Ñем ÑамÑм ÑÑ ÑдÑÐ°Ñ ÑиÑаемоÑÑÑ.
ÐекоÑаÑоÑÑ ÑпоÑÐ¾Ð±Ð½Ñ ÑпÑоÑÑиÑÑ ÑÑÑиннÑе, повÑоÑÑÑÑиеÑÑ Ð·Ð°Ð´Ð°Ñи, вÑнеÑÑи Ð¸Ñ Ð¸Ð· кода ÑÑнкÑии.
ÐапÑимеÑ, Ñоздадим декоÑаÑоÑ, коÑоÑÑй пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑÑнкÑÐ¸Ñ Ð¸ маÑÑив, коÑоÑÑй опиÑÑÐ²Ð°ÐµÑ Ð´Ð»Ñ ÐºÐ°ÐºÐ¾Ð³Ð¾ аÑгÑменÑа какÑÑ Ð¿ÑовеÑÐºÑ Ñипа пÑименÑÑÑ:
// вÑпомогаÑелÑÐ½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ð¿ÑовеÑки на ÑиÑло
function checkNumber(value) {
return typeof value == 'number';
}
// декоÑаÑоÑ, пÑовеÑÑÑÑий ÑÐ¸Ð¿Ñ Ð´Ð»Ñ f
// вÑоÑой аÑгÑÐ¼ÐµÐ½Ñ checks - маÑÑив Ñ ÑÑнкÑиÑми Ð´Ð»Ñ Ð¿ÑовеÑки
function typeCheck(f, checks) {
return function() {
for (var i = 0; i < arguments.length; i++) {
if (!checks[i](arguments[i])) {
alert( "ÐекоÑÑекÑнÑй Ñип аÑгÑменÑа Ð½Ð¾Ð¼ÐµÑ " + i );
return;
}
}
return f.apply(this, arguments);
}
}
function sum(a, b) {
return a + b;
}
// обеÑнÑм декоÑаÑÐ¾Ñ Ð´Ð»Ñ Ð¿ÑовеÑки
sum = typeCheck(sum, [checkNumber, checkNumber]); // оба аÑгÑменÑа - ÑиÑла
// полÑзÑемÑÑ ÑÑнкÑией как обÑÑно
alert( sum(1, 2) ); // 3, вÑе Ñ
оÑоÑо
// а Ð²Ð¾Ñ Ñак - бÑÐ´ÐµÑ Ð¾Ñибка
sum(true, null); // некоÑÑекÑнÑй аÑгÑÐ¼ÐµÐ½Ñ Ð½Ð¾Ð¼ÐµÑ 0
sum(1, ["array", "in", "sum?!?"]); // некоÑÑекÑнÑй аÑгÑÐ¼ÐµÐ½Ñ Ð½Ð¾Ð¼ÐµÑ 1
ÐонеÑно, ÑÑÐ¾Ñ Ð´ÐµÐºÐ¾ÑаÑÐ¾Ñ Ð¼Ð¾Ð¶Ð½Ð¾ еÑÑ ÑаÑÑиÑÑÑÑ, ÑлÑÑÑаÑÑ, допиÑÑваÑÑ Ð¿ÑовеÑки, но⦠ÐÑ Ñже понÑли пÑинÑип, не пÑавда ли?
Ðдин Ñаз пиÑем декоÑаÑÐ¾Ñ Ð¸ далÑÑе пÑоÑÑо пÑименÑем ÑÑÑ ÑÑнкÑионалÑноÑÑÑ Ð²ÐµÐ·Ð´Ðµ, где нÑжно.
ÐекоÑаÑÐ¾Ñ Ð¿ÑовеÑки доÑÑÑпа
Ð Ð½Ð°ÐºÐ¾Ð½ÐµÑ Ð¿Ð¾ÑмоÑÑим еÑÑ Ð¾Ð´Ð¸Ð½, поÑледний пÑимеÑ.
ÐÑедположим, Ñ Ð½Ð°Ñ ÐµÑÑÑ ÑÑнкÑÐ¸Ñ isAdmin(), коÑоÑÐ°Ñ Ð²Ð¾Ð·Ð²ÑаÑÐ°ÐµÑ true, еÑли Ñ Ð¿Ð¾ÑеÑиÑÐµÐ»Ñ ÐµÑÑÑ Ð¿Ñава админиÑÑÑаÑоÑа.
Ðожно ÑоздаÑÑ Ð´ÐµÐºÐ¾ÑаÑÐ¾Ñ checkPermissionDecorator, коÑоÑÑй добавлÑÐµÑ Ð² лÑбÑÑ ÑÑнкÑÐ¸Ñ Ð¿ÑовеÑÐºÑ Ð¿Ñав:
ÐапÑимеÑ, Ñоздадим декоÑаÑÐ¾Ñ checkPermissionDecorator(f). Ðн бÑÐ´ÐµÑ Ð²Ð¾Ð·Ð²ÑаÑаÑÑ Ð¾Ð±ÑÑÑкÑ, коÑоÑÐ°Ñ Ð¿ÐµÑедаÑÑ Ð²Ñзов f в Ñом ÑлÑÑае, еÑли Ñ Ð¿Ð¾ÑеÑиÑÐµÐ»Ñ Ð´Ð¾ÑÑаÑоÑно пÑав:
function checkPermissionDecorator(f) {
return function() {
if (isAdmin()) {
return f.apply(this, arguments);
}
alert( 'ÐедоÑÑаÑоÑно пÑав' );
}
}
ÐÑполÑзование декоÑаÑоÑа:
function save() { ... }
save = checkPermissionDecorator(save);
// ТепеÑÑ Ð²Ñзов ÑÑнкÑии save() пÑовеÑÑÐµÑ Ð¿Ñава
ÐÑого
ÐекоÑаÑÐ¾Ñ â ÑÑо обÑÑÑка над ÑÑнкÑией, коÑоÑÐ°Ñ Ð¼Ð¾Ð´Ð¸ÑиÑиÑÑÐµÑ ÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ. ÐÑи ÑÑом оÑновнÑÑ ÑабоÑÑ Ð¿Ð¾-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð²ÑполнÑÐµÑ ÑÑнкÑиÑ.
ÐекоÑаÑоÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ не ÑолÑко повÑоÑно иÑполÑзоваÑÑ, но и комбиниÑоваÑÑ!
ÐÑо каÑдиналÑно повÑÑÐ°ÐµÑ Ð¸Ñ Ð²ÑÑазиÑелÑнÑÑ ÑилÑ. ÐекоÑаÑоÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ ÑаÑÑмаÑÑиваÑÑ ÐºÐ°Ðº Ñвоего Ñода «ÑиÑи» или возможноÑÑи, коÑоÑÑе можно «наÑепиÑÑ» на лÑбÑÑ ÑÑнкÑиÑ. Ðожно один, а можно неÑколÑко.
Скажем, иÑполÑзÑÑ Ð´ÐµÐºÐ¾ÑаÑоÑÑ, опиÑаннÑе вÑÑе, можно добавиÑÑ Ðº ÑÑнкÑии возможноÑÑи по пÑовеÑке Ñипов даннÑÑ , замеÑÑ Ð²Ñемени и пÑовеÑке доÑÑÑпа бÑквалÑно одной ÑÑÑокой, не Ð·Ð°Ð»ÐµÐ·Ð°Ñ Ð¿Ñи ÑÑом в ÐµÑ ÐºÐ¾Ð´, Ñо еÑÑÑ (!) не ÑвелиÑÐ¸Ð²Ð°Ñ ÐµÐ³Ð¾ ÑложноÑÑÑ.
ÐÑÐµÐ´Ð»Ð°Ð³Ð°Ñ Ð²Ð°ÑÐµÐ¼Ñ Ð²Ð½Ð¸Ð¼Ð°Ð½Ð¸Ñ Ð·Ð°Ð´Ð°Ñи, коÑоÑÑе помогÑÑ Ð²ÑÑÑниÑÑ, наÑколÑко Ð²Ñ ÑазобÑалиÑÑ Ð² декоÑаÑоÑÐ°Ñ . Ðалее в ÑÑебнике Ð¼Ñ ÐµÑÑ Ð²ÑÑÑеÑимÑÑ Ñ Ð½Ð¸Ð¼Ð¸.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)