JavaScript пÑедоÑÑавлÑÐµÑ Ð¸ÑклÑÑиÑелÑно гибкие возможноÑÑи по ÑабоÑе Ñ ÑÑнкÑиÑми: они могÑÑ Ð±ÑÑÑ Ð¿ÐµÑÐµÐ´Ð°Ð½Ñ Ð² дÑÑгие ÑÑнкÑии, иÑполÑÐ·Ð¾Ð²Ð°Ð½Ñ ÐºÐ°Ðº обÑекÑÑ, и ÑейÑÐ°Ñ Ð¼Ñ ÑаÑÑмоÑÑим, как пеÑенапÑавлÑÑÑ Ð²ÑÐ·Ð¾Ð²Ñ Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¸Ð¼Ð¸ и как Ð¸Ñ Ð´ÐµÐºÐ¾ÑиÑоваÑÑ.
ÐÑозÑаÑное кеÑиÑование
ÐÑедÑÑавим, ÑÑо Ñ Ð½Ð°Ñ ÐµÑÑÑ ÑÑнкÑÐ¸Ñ slow(x), вÑполнÑÑÑÐ°Ñ ÑеÑÑÑÑоÑмкие вÑÑиÑлениÑ, но возвÑаÑаÑÑÐ°Ñ ÑÑабилÑнÑе ÑезÑлÑÑаÑÑ. ÐÑÑгими Ñловами, Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ и Ñого же x она вÑегда возвÑаÑÐ°ÐµÑ Ð¾Ð´Ð¸Ð½ и ÑÐ¾Ñ Ð¶Ðµ ÑезÑлÑÑаÑ.
ÐÑли ÑÑнкÑÐ¸Ñ Ð²ÑзÑваеÑÑÑ ÑаÑÑо, Ñо, веÑоÑÑно, Ð¼Ñ Ð·Ð°Ñ Ð¾Ñим кеÑиÑоваÑÑ (запоминаÑÑ) возвÑаÑаемÑе ÐµÑ ÑезÑлÑÑаÑÑ, ÑÑÐ¾Ð±Ñ ÑÑкономиÑÑ Ð²ÑÐµÐ¼Ñ Ð½Ð° повÑоÑнÑÑ Ð²ÑÑиÑлениÑÑ .
ÐмеÑÑо Ñого, ÑÑÐ¾Ð±Ñ ÑÑложнÑÑÑ slow(x) дополниÑелÑной ÑÑнкÑионалÑноÑÑÑÑ, Ð¼Ñ Ð·Ð°ÐºÐ»ÑÑим ÐµÑ Ð² ÑÑнкÑиÑ-обÑÑÑÐºÑ â «wrapper» (Ð¾Ñ Ð°Ð½Ð³Ð». «wrap» â обÑÑÑÑваÑÑ), коÑоÑÐ°Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ ÐºÐµÑиÑование. Ðалее Ð¼Ñ Ñвидим, ÑÑо в Ñаком подÑ
оде маÑÑа пÑеимÑÑеÑÑв.
ÐÐ¾Ñ ÐºÐ¾Ð´ Ñ Ð¾Ð±ÑÑÑнениÑми:
function slow(x) {
// здеÑÑ Ð¼Ð¾Ð³ÑÑ Ð±ÑÑÑ ÑеÑÑÑÑоÑмкие вÑÑиÑлениÑ
alert(`Called with ${x}`);
return x;
}
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
if (cache.has(x)) { // еÑли ÐºÐµÑ ÑодеÑÐ¶Ð¸Ñ Ñакой x,
return cache.get(x); // ÑиÑаем из него ÑезÑлÑÑаÑ
}
let result = func(x); // инаÑе, вÑзÑваем ÑÑнкÑиÑ
cache.set(x, result); // и кеÑиÑÑем (запоминаем) ÑезÑлÑÑаÑ
return result;
};
}
slow = cachingDecorator(slow);
alert( slow(1) ); // slow(1) кеÑиÑÑем
alert( "Again: " + slow(1) ); // возвÑаÑаем из кеÑа
alert( slow(2) ); // slow(2) кеÑиÑÑем
alert( "Again: " + slow(2) ); // возвÑаÑаем из кеÑа
Ркоде вÑÑе cachingDecorator â ÑÑо декоÑаÑоÑ, ÑпеÑиалÑÐ½Ð°Ñ ÑÑнкÑиÑ, коÑоÑÐ°Ñ Ð¿ÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð´ÑÑгÑÑ ÑÑнкÑÐ¸Ñ Ð¸ изменÑÐµÑ ÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ.
ÐÐ´ÐµÑ ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вÑзваÑÑ cachingDecorator Ñ Ð»Ñбой ÑÑнкÑией, в ÑезÑлÑÑаÑе Ñего Ð¼Ñ Ð¿Ð¾Ð»ÑÑим кеÑиÑÑÑÑÑÑ Ð¾Ð±ÑÑÑкÑ. ÐÑо здоÑово, Ñ.к. Ñ Ð½Ð°Ñ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¼Ð½Ð¾Ð¶ÐµÑÑво ÑÑнкÑий, иÑполÑзÑÑÑиÑ
ÑакÑÑ ÑÑнкÑионалÑноÑÑÑ, и вÑÑ, ÑÑо нам нÑжно ÑделаÑÑ â ÑÑо пÑимениÑÑ Ðº ним cachingDecorator.
ÐÑделÑÑ ÐºÐµÑиÑÑÑÑий код Ð¾Ñ Ð¾Ñновного кода, Ð¼Ñ Ñакже ÑÐ¾Ñ ÑанÑем ÑиÑÑоÑÑ Ð¸ пÑоÑÑоÑÑ Ð¿Ð¾Ñледнего.
РезÑлÑÑÐ°Ñ Ð²Ñзова cachingDecorator(func) ÑвлÑеÑÑÑ Â«Ð¾Ð±ÑÑÑкой», Ñ.е. function(x) «обоÑаÑиваеÑ» вÑзов func(x) в кеÑиÑÑÑÑÑÑ Ð»Ð¾Ð³Ð¸ÐºÑ:
С ÑоÑки зÑÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑнего кода, обÑÑнÑÑÐ°Ñ ÑÑнкÑÐ¸Ñ slow по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð´ÐµÐ»Ð°ÐµÑ Ñо же Ñамое. ÐбÑÑÑка вÑего лиÑÑ Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÑ Ðº ÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð°ÑÐ¿ÐµÐºÑ ÐºÐµÑиÑованиÑ.
ÐÐ¾Ð´Ð²Ð¾Ð´Ñ Ð¸Ñог, можно вÑделиÑÑ Ð½ÐµÑколÑко пÑеимÑÑеÑÑв иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾ÑделÑной cachingDecorator вмеÑÑо Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð´Ð° Ñамой slow:
- ФÑнкÑиÑ
cachingDecoratorможно иÑполÑзоваÑÑ Ð¿Ð¾Ð²ÑоÑно. ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ пÑимениÑÑ ÐµÑ Ðº дÑÑгой ÑÑнкÑии. - Ðогика кеÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÑвлÑеÑÑÑ Ð¾ÑделÑной, она не ÑвелиÑÐ¸Ð²Ð°ÐµÑ ÑложноÑÑÑ Ñамой
slow(еÑли ÑÐ°ÐºÐ¾Ð²Ð°Ñ Ð±Ñла). - ÐÑи Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ÑÑи Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ обÑединиÑÑ Ð½ÐµÑколÑко декоÑаÑоÑов (ÑеÑÑ Ð¾Ð± ÑÑом пойдÑÑ Ð¿Ð¾Ð·Ð¶Ðµ).
ÐÑименение «func.call» Ð´Ð»Ñ Ð¿ÐµÑедаÑи конÑекÑÑа
УпомÑнÑÑÑй вÑÑе кеÑиÑÑÑÑий декоÑаÑÐ¾Ñ Ð½Ðµ Ð¿Ð¾Ð´Ñ Ð¾Ð´Ð¸Ñ Ð´Ð»Ñ ÑабоÑÑ Ñ Ð¼ÐµÑодами обÑекÑов.
ÐапÑимеÑ, в пÑиведÑнном ниже коде worker.slow() пеÑеÑÑаÑÑ ÑабоÑаÑÑ Ð¿Ð¾Ñле пÑÐ¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´ÐµÐºÐ¾ÑаÑоÑа:
// Ñделаем worker.slow кеÑиÑÑÑÑим
let worker = {
someMethod() {
return 1;
},
slow(x) {
// здеÑÑ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ ÑÑÑаÑно ÑÑжÑÐ»Ð°Ñ Ð·Ð°Ð´Ð°Ñа Ð´Ð»Ñ Ð¿ÑоÑеÑÑоÑа
alert("Called with " + x);
return x * this.someMethod(); // (*)
}
};
// ÑÐ¾Ñ Ð¶Ðµ код, ÑÑо и вÑÑе
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
if (cache.has(x)) {
return cache.get(x);
}
let result = func(x); // (**)
cache.set(x, result);
return result;
};
}
alert( worker.slow(1) ); // оÑигиналÑнÑй меÑод ÑабоÑаеÑ
worker.slow = cachingDecorator(worker.slow); // ÑепеÑÑ Ñделаем его кеÑиÑÑÑÑим
alert( worker.slow(2) ); // Ðй! ÐÑибка: не ÑдаÑÑÑÑ Ð¿ÑоÑиÑаÑÑ ÑвойÑÑво 'someMethod' из 'undefined'
ÐÑибка Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ð² ÑÑÑоке (*). ФÑнкÑÐ¸Ñ Ð¿ÑÑаеÑÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к this.someMethod и завеÑÑаеÑÑÑ Ñ Ð¾Ñибкой. ÐидиÑе поÑемÑ?
ÐÑиÑина в Ñом, ÑÑо в ÑÑÑоке (**) декоÑаÑÐ¾Ñ Ð²ÑзÑÐ²Ð°ÐµÑ Ð¾ÑигиналÑнÑÑ ÑÑнкÑÐ¸Ñ ÐºÐ°Ðº func(x), и она в данном ÑлÑÑае полÑÑÐ°ÐµÑ this = undefined.
ÐÑ Ð±Ñ Ð½Ð°Ð±Ð»Ñдали Ð¿Ð¾Ñ Ð¾Ð¶ÑÑ ÑиÑÑаÑиÑ, еÑли Ð±Ñ Ð¿Ð¾Ð¿ÑÑалиÑÑ Ð·Ð°Ð¿ÑÑÑиÑÑ:
let func = worker.slow;
func(2);
Т.е. декоÑаÑÐ¾Ñ Ð¿ÐµÑедаÑÑ Ð²Ñзов оÑигиналÑÐ½Ð¾Ð¼Ñ Ð¼ÐµÑодÑ, но без конÑекÑÑа. СледоваÑелÑно â оÑибка.
ÐавайÑе ÑÑо иÑпÑавим.
СÑÑеÑÑвÑÐµÑ ÑпеÑиалÑнÑй вÑÑÑоеннÑй меÑод ÑÑнкÑии func.call(context, â¦args), коÑоÑÑй позволÑÐµÑ Ð²ÑзÑваÑÑ ÑÑнкÑиÑ, Ñвно ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°Ñ this.
СинÑакÑиÑ:
func.call(context, arg1, arg2, ...)
Ðн запÑÑÐºÐ°ÐµÑ ÑÑнкÑÐ¸Ñ func, иÑполÑзÑÑ Ð¿ÐµÑвÑй аÑгÑÐ¼ÐµÐ½Ñ ÐºÐ°Ðº ÐµÑ ÐºÐ¾Ð½ÑекÑÑ this, а поÑледÑÑÑие â как ÐµÑ Ð°ÑгÑменÑÑ.
ÐÑоÑе говоÑÑ, ÑÑи два вÑзова делаÑÑ Ð¿Ð¾ÑÑи Ñо же Ñамое:
func(1, 2, 3);
func.call(obj, 1, 2, 3)
Ðни оба вÑзÑваÑÑ func Ñ Ð°ÑгÑменÑами 1, 2 и 3. ÐдинÑÑвенное оÑлиÑие ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо func.call еÑÑ Ð¸ ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ this ÑавнÑм obj.
ÐапÑимеÑ, в пÑиведÑнном ниже коде Ð¼Ñ Ð²ÑзÑваем sayHi в конÑекÑÑе ÑазлиÑнÑÑ
обÑекÑов: sayHi.call(user) запÑÑÐºÐ°ÐµÑ sayHi, пеÑÐµÐ´Ð°Ð²Ð°Ñ this=user, а ÑледÑÑÑÐ°Ñ ÑÑÑока ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ this=admin:
function sayHi() {
alert(this.name);
}
let user = { name: "John" };
let admin = { name: "Admin" };
// иÑполÑзÑем 'call' Ð´Ð»Ñ Ð¿ÐµÑедаÑи ÑазлиÑнÑÑ
обÑекÑов в каÑеÑÑве 'this'
sayHi.call( user ); // John
sayHi.call( admin ); // Admin
ÐдеÑÑ Ð¼Ñ Ð¸ÑполÑзÑем call Ð´Ð»Ñ Ð²Ñзова say Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñм конÑекÑÑом и ÑÑазой:
function say(phrase) {
alert(this.name + ': ' + phrase);
}
let user = { name: "John" };
// 'user' ÑÑановиÑÑÑ 'this', и "Hello" ÑÑановиÑÑÑ Ð¿ÐµÑвÑм аÑгÑменÑом
say.call( user, "Hello" ); // John: Hello
РнаÑем ÑлÑÑае Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ call в обÑÑÑке Ð´Ð»Ñ Ð¿ÐµÑедаÑи конÑекÑÑа в иÑÑ
однÑÑ ÑÑнкÑиÑ:
let worker = {
someMethod() {
return 1;
},
slow(x) {
alert("Called with " + x);
return x * this.someMethod(); // (*)
}
};
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
if (cache.has(x)) {
return cache.get(x);
}
let result = func.call(this, x); // ÑепеÑÑ 'this' пеÑедаÑÑÑÑ Ð¿ÑавилÑно
cache.set(x, result);
return result;
};
}
worker.slow = cachingDecorator(worker.slow); // ÑепеÑÑ Ñделаем ÐµÑ ÐºÐµÑиÑÑÑÑей
alert( worker.slow(2) ); // ÑабоÑаеÑ
alert( worker.slow(2) ); // ÑабоÑаеÑ, не вÑзÑÐ²Ð°Ñ Ð¿ÐµÑвонаÑалÑнÑÑ ÑÑнкÑÐ¸Ñ (кеÑиÑÑеÑÑÑ)
ТепеÑÑ Ð²ÑÑ Ð² поÑÑдке.
ЧÑÐ¾Ð±Ñ Ð²ÑÑ Ð±Ñло понÑÑно, давайÑе поÑмоÑÑим глÑбже, как пеÑедаÑÑÑÑ this:
- ÐоÑле декоÑаÑии
worker.slowÑÑановиÑÑÑ Ð¾Ð±ÑÑÑкойfunction (x) { ... }. - Так ÑÑо пÑи вÑполнении
worker.slow(2)обÑÑÑка полÑÑаеÑ2в каÑеÑÑве аÑгÑменÑа иthis=worker(Ñак как ÑÑо обÑÐµÐºÑ Ð¿ÐµÑед ÑоÑкой). - ÐнÑÑÑи обÑÑÑки, еÑли ÑезÑлÑÑÐ°Ñ ÐµÑÑ Ð½Ðµ кеÑиÑован,
func.call(this, x)пеÑедаÑÑ ÑекÑÑийthis(=worker) и ÑекÑÑий аÑгÑÐ¼ÐµÐ½Ñ (=2) в оÑигиналÑнÑÑ ÑÑнкÑиÑ.
ÐеÑÐµÑ Ð¾Ð´Ð¸Ð¼ к неÑколÑким аÑгÑменÑам Ñ Â«func.apply»
ТепеÑÑ Ð´Ð°Ð²Ð°Ð¹Ñе Ñделаем cachingDecorator еÑÑ Ð±Ð¾Ð»ÐµÐµ ÑнивеÑÑалÑнÑм. Ðо ÑиÑ
Ð¿Ð¾Ñ Ð¾Ð½ ÑабоÑал ÑолÑко Ñ ÑÑнкÑиÑми Ñ Ð¾Ð´Ð½Ð¸Ð¼ аÑгÑменÑом.
Ðак же кеÑиÑоваÑÑ Ð¼ÐµÑод Ñ Ð½ÐµÑколÑкими аÑгÑменÑами worker.slow?
let worker = {
slow(min, max) {
return min + max; // здеÑÑ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ ÑÑжÑÐ»Ð°Ñ Ð·Ð°Ð´Ð°Ñа
}
};
// бÑÐ´ÐµÑ ÐºÐµÑиÑоваÑÑ Ð²ÑÐ·Ð¾Ð²Ñ Ñ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñми аÑгÑменÑами
worker.slow = cachingDecorator(worker.slow);
ÐдеÑÑ Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð´Ð²Ðµ задаÑи Ð´Ð»Ñ ÑеÑениÑ.
Ðо-пеÑвÑÑ
, как иÑполÑзоваÑÑ Ð¾Ð±Ð° аÑгÑменÑа min и max Ð´Ð»Ñ ÐºÐ»ÑÑа в коллекÑии cache? Ранее Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ аÑгÑменÑа x Ð¼Ñ Ð¼Ð¾Ð³Ð»Ð¸ пÑоÑÑо ÑоÑ
ÑаниÑÑ ÑезÑлÑÑÐ°Ñ cache.set(x, result) и вÑзваÑÑ cache.get(x), ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ ÐµÐ³Ð¾ позже. Ðо ÑепеÑÑ Ð½Ð°Ð¼ нÑжно запомниÑÑ ÑезÑлÑÑÐ°Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñии аÑгÑменÑов (min,max). ÐÑÑÑоеннÑй Map пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑолÑко одно знаÑение как клÑÑ.
ÐÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ возможнÑÑ ÑеÑений:
- РеализоваÑÑ Ð½Ð¾Ð²ÑÑ (или иÑполÑзоваÑÑ ÑÑоÑоннÑÑ) ÑÑÑÑкÑÑÑÑ Ð´Ð°Ð½Ð½ÑÑ
Ð´Ð»Ñ ÐºÐ¾Ð»Ð»ÐµÐºÑии, коÑоÑÐ°Ñ Ð±Ð¾Ð»ÐµÐµ ÑнивеÑÑалÑна, Ñем вÑÑÑоеннÑй
Map, и поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ Ð¼Ð½Ð¾Ð¶ÐµÑÑвеннÑе клÑÑи. - ÐÑполÑзоваÑÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ñе коллекÑии:
cache.set(min)бÑдеÑMap, коÑоÑÐ°Ñ Ñ ÑÐ°Ð½Ð¸Ñ Ð¿Ð°ÑÑ(max, result). Тогда полÑÑиÑÑresultÐ¼Ñ Ñможем, вÑзвавcache.get(min).get(max). - СоединиÑÑ Ð´Ð²Ð° знаÑÐµÐ½Ð¸Ñ Ð² одно. РнаÑем конкÑеÑном ÑлÑÑае Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ пÑоÑÑо иÑполÑзоваÑÑ ÑÑÑокÑ
"min,max"как клÑÑ ÐºMap. ÐÐ»Ñ Ð³Ð¸Ð±ÐºÐ¾ÑÑи, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ позволиÑÑ Ð¿ÐµÑедаваÑÑ Ñ ÐµÑиÑÑÑÑÑÑ ÑÑнкÑÐ¸Ñ Ð² декоÑаÑоÑ, коÑоÑÐ°Ñ Ð·Ð½Ð°ÐµÑ, как ÑделаÑÑ Ð¾Ð´Ð½Ð¾ знаÑение из Ð¼Ð½Ð¾Ð³Ð¸Ñ .
ÐÐ»Ñ Ð¼Ð½Ð¾Ð³Ð¸Ñ Ð¿ÑакÑиÑеÑÐºÐ¸Ñ Ð¿Ñименений ÑÑеÑий ваÑÐ¸Ð°Ð½Ñ Ð´Ð¾ÑÑаÑоÑно Ñ Ð¾ÑоÑ, поÑÑÐ¾Ð¼Ñ Ð¼Ñ Ð±Ñдем пÑидеÑживаÑÑÑÑ ÐµÐ³Ð¾.
Также нам понадобиÑÑÑ Ð·Ð°Ð¼ÐµÐ½Ð¸ÑÑ func.call(this, x) на func.call(this, ...arguments), ÑÑÐ¾Ð±Ñ Ð¿ÐµÑедаваÑÑ Ð²Ñе аÑгÑменÑÑ Ð¾Ð±ÑÑнÑÑой ÑÑнкÑии, а не ÑолÑко пеÑвÑй.
ÐÐ¾Ñ Ð±Ð¾Ð»ÐµÐµ моÑнÑй cachingDecorator:
let worker = {
slow(min, max) {
alert(`Called with ${min},${max}`);
return min + max;
}
};
function cachingDecorator(func, hash) {
let cache = new Map();
return function() {
let key = hash(arguments); // (*)
if (cache.has(key)) {
return cache.get(key);
}
let result = func.call(this, ...arguments); // (**)
cache.set(key, result);
return result;
};
}
function hash(args) {
return args[0] + ',' + args[1];
}
worker.slow = cachingDecorator(worker.slow, hash);
alert( worker.slow(3, 5) ); // ÑабоÑаеÑ
alert( "Again " + worker.slow(3, 5) ); // аналогиÑно (из кеÑа)
ТепеÑÑ Ð¾Ð½ ÑабоÑÐ°ÐµÑ Ñ Ð»ÑбÑм колиÑеÑÑвом аÑгÑменÑов.
ÐÑÑÑ Ð´Ð²Ð° изменениÑ:
- Ð ÑÑÑоке
(*)вÑзÑваемhashÐ´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ клÑÑа изarguments. ÐдеÑÑ Ð¼Ñ Ð¸ÑполÑзÑем пÑоÑÑÑÑ ÑÑнкÑÐ¸Ñ Â«Ð¾Ð±ÑединениÑ», коÑоÑÐ°Ñ Ð¿ÑевÑаÑÐ°ÐµÑ Ð°ÑгÑменÑÑ(3, 5)в клÑÑ"3,5". Рболее ÑложнÑÑ ÑлÑÑаÑÑ Ð¼Ð¾Ð³ÑÑ Ð¿Ð¾ÑÑебоваÑÑÑÑ Ð´ÑÑгие ÑÑнкÑии Ñ ÐµÑиÑованиÑ. - ÐаÑем в ÑÑÑоке
(**)иÑполÑзÑемfunc.call(this, ...arguments)Ð´Ð»Ñ Ð¿ÐµÑедаÑи как конÑекÑÑа, Ñак и вÑÐµÑ Ð°ÑгÑменÑов, полÑÑеннÑÑ Ð¾Ð±ÑÑÑкой (незавиÑимо Ð¾Ñ Ð¸Ñ ÐºÐ¾Ð»Ð¸ÑеÑÑва), в иÑÑ Ð¾Ð´Ð½ÑÑ ÑÑнкÑиÑ.
ÐмеÑÑо func.call(this, ...arguments) Ð¼Ñ Ð¼Ð¾Ð³Ð»Ð¸ Ð±Ñ Ð½Ð°Ð¿Ð¸ÑаÑÑ func.apply(this, arguments).
СинÑакÑÐ¸Ñ Ð²ÑÑÑоенного меÑода func.apply:
func.apply(context, args)
Ðн вÑполнÑÐµÑ func, ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°Ñ this=context и пÑÐ¸Ð½Ð¸Ð¼Ð°Ñ Ð² каÑеÑÑве ÑпиÑка аÑгÑменÑов пÑевдомаÑÑив args.
ÐдинÑÑÐ²ÐµÐ½Ð½Ð°Ñ ÑазниÑа в ÑинÑакÑиÑе Ð¼ÐµÐ¶Ð´Ñ call и apply ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо call Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ ÑпиÑок аÑгÑменÑов, в Ñо вÑÐµÐ¼Ñ ÐºÐ°Ðº apply пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð¿ÑевдомаÑÑив.
ÐÑи два вÑзова поÑÑи ÑквиваленÑнÑ:
func.call(context, ...args); // пеÑедаÑÑ Ð¼Ð°ÑÑив как ÑпиÑок Ñ Ð¾Ð¿ÐµÑаÑоÑом ÑаÑÑиÑениÑ
func.apply(context, args); // ÑÐ¾Ñ Ð¶Ðµ ÑÑÑекÑ
ÐÑÑÑ ÑолÑко одна неболÑÑÐ°Ñ ÑазниÑа:
- ÐпеÑаÑÐ¾Ñ ÑаÑÑиÑениÑ
...позволÑÐµÑ Ð¿ÐµÑедаваÑÑ Ð¿ÐµÑебиÑаемÑй обÑекÑargsв виде ÑпиÑка вcall. - Ð
applyпÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑолÑко пÑевдомаÑÑивargs.
Так ÑÑо ÑÑи вÑÐ·Ð¾Ð²Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½ÑÑÑ Ð´ÑÑг дÑÑга. ÐÐ»Ñ Ð¿ÐµÑебиÑаемÑÑ
обÑекÑов ÑÑабоÑÐ°ÐµÑ call, а где Ð¼Ñ Ð¾Ð¶Ð¸Ð´Ð°ÐµÐ¼ пÑевдомаÑÑив â apply.
РеÑли Ñ Ð½Ð°Ñ Ð¾Ð±ÑекÑ, коÑоÑÑй и Ñо, и дÑÑгое, напÑимеÑ, ÑеалÑнÑй маÑÑив, Ñо ÑеÑ
ниÑеÑки Ð¼Ñ Ð¼Ð¾Ð³Ð»Ð¸ Ð±Ñ Ð¸ÑполÑзоваÑÑ Ð»Ñбой меÑод, но apply, веÑоÑÑно, бÑÐ´ÐµÑ Ð±ÑÑÑÑее, поÑÐ¾Ð¼Ñ ÑÑо болÑÑинÑÑво движков JavaScript внÑÑÑенне опÑимизиÑÑÑÑ ÐµÐ³Ð¾ лÑÑÑе.
ÐеÑедаÑа вÑÐµÑ Ð°ÑгÑменÑов вмеÑÑе Ñ ÐºÐ¾Ð½ÑекÑÑом дÑÑгой ÑÑнкÑии назÑваеÑÑÑ Â«Ð¿ÐµÑенапÑавлением вÑзова» (call forwarding).
ÐÑоÑÑейÑий вид Ñакого пеÑенапÑавлениÑ:
let wrapper = function() {
return func.apply(this, arguments);
};
ÐÑи вÑзове wrapper из внеÑнего кода его не оÑлиÑиÑÑ Ð¾Ñ Ð²Ñзова иÑÑ
одной ÑÑнкÑии.
ÐаимÑÑвование меÑода
ТепеÑÑ Ð´Ð°Ð²Ð°Ð¹Ñе Ñделаем еÑÑ Ð¾Ð´Ð½Ð¾ неболÑÑое ÑлÑÑÑение ÑÑнкÑии Ñ ÐµÑиÑованиÑ:
function hash(args) {
return args[0] + ',' + args[1];
}
Ðа даннÑй Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð¾Ð½Ð° ÑабоÑÐ°ÐµÑ ÑолÑко Ð´Ð»Ñ Ð´Ð²ÑÑ
аÑгÑменÑов. ÐÑло Ð±Ñ Ð»ÑÑÑе, еÑли Ð±Ñ Ð¾Ð½Ð° могла ÑклеиÑÑ Ð»Ñбое колиÑеÑÑво args.
ÐÑÑеÑÑвеннÑм ÑеÑением бÑло Ð±Ñ Ð¸ÑполÑзоваÑÑ Ð¼ÐµÑод arr.join:
function hash(args) {
return args.join();
}
â¦Ð ÑожалениÑ, ÑÑо не ÑÑабоÑаеÑ, поÑÐ¾Ð¼Ñ ÑÑо Ð¼Ñ Ð²ÑзÑваем hash(arguments), а обÑÐµÐºÑ arguments ÑвлÑеÑÑÑ Ð¿ÐµÑебиÑаемÑм и пÑевдомаÑÑивом, но не ÑеалÑнÑм маÑÑивом.
Таким обÑазом, вÑзов join Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ поÑеÑÐ¿Ð¸Ñ Ð½ÐµÑдаÑÑ, ÑÑо Ð¼Ñ Ð¸ можем видеÑÑ Ð½Ð¸Ð¶Ðµ:
function hash() {
alert( arguments.join() ); // ÐÑибка: arguments.join не ÑвлÑеÑÑÑ ÑÑнкÑией
}
hash(1, 2);
Тем не менее, еÑÑÑ Ð¿ÑоÑÑой ÑпоÑоб иÑполÑзоваÑÑ Ñоединение маÑÑива:
function hash() {
alert( [].join.call(arguments) ); // 1,2
}
hash(1, 2);
ÐÑÐ¾Ñ ÑÑÑк назÑваеÑÑÑ Ð·Ð°Ð¸Ð¼ÑÑвование меÑода.
ÐÑ Ð±ÐµÑÑм (заимÑÑвÑем) меÑод join из обÑÑного маÑÑива [].join. РиÑполÑзÑем [].join.call, ÑÑÐ¾Ð±Ñ Ð²ÑполниÑÑ ÐµÐ³Ð¾ в конÑекÑÑе arguments.
ÐоÑÐµÐ¼Ñ ÑÑо ÑабоÑаеÑ?
ÐÑо ÑвÑзано Ñ Ñем, ÑÑо внÑÑÑенний алгоÑиÑм вÑÑÑоенного меÑода arr.join(glue) оÑÐµÐ½Ñ Ð¿ÑоÑÑ.
ÐзÑÑо из ÑпеÑиÑикаÑии пÑакÑиÑеÑки «как еÑÑÑ»:
- ÐÑÑкай пеÑвÑм аÑгÑменÑом бÑдеÑ
glueили, в ÑлÑÑае оÑÑÑÑÑÑÐ²Ð¸Ñ Ð°ÑгÑменÑов, им бÑÐ´ÐµÑ Ð·Ð°Ð¿ÑÑаÑ"," - ÐÑÑкай
resultбÑÐ´ÐµÑ Ð¿ÑÑÑой ÑÑÑокой"". - ÐобавиÑÑ
this[0]кresult. - ÐобавиÑÑ
glueиthis[1]. - ÐобавиÑÑ
glueиthis[2]. - â¦Ð²ÑполнÑÑÑ Ð´Ð¾ ÑеÑ
поÑ, пока
this.lengthÑлеменÑов не бÑÐ´ÐµÑ Ñклеено. - ÐеÑнÑÑÑ
result.
Таким обÑазом, ÑеÑ
ниÑеÑки он пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ this и обÑединÑÐµÑ this[0], this[1]⦠и Ñ.д. вмеÑÑе. Ðн намеÑенно напиÑан Ñак, ÑÑо допÑÑÐºÐ°ÐµÑ Ð»Ñбой пÑевдомаÑÑив this (не ÑлÑÑайно, многие меÑÐ¾Ð´Ñ ÑледÑÑÑ ÑÑой пÑакÑике). ÐÐ¾Ñ Ð¿Ð¾ÑÐµÐ¼Ñ Ð¾Ð½ Ñакже ÑабоÑÐ°ÐµÑ Ñ this=arguments.
ÐÑого
ÐекоÑаÑÐ¾Ñ â ÑÑо обÑÑÑка вокÑÑг ÑÑнкÑии, коÑоÑÐ°Ñ Ð¸Ð·Ð¼ÐµÐ½ÑÐµÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ поÑледней. ÐÑÐ½Ð¾Ð²Ð½Ð°Ñ ÑабоÑа по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð²ÑполнÑеÑÑÑ ÑÑнкÑией.
ÐбÑÑно безопаÑно замениÑÑ ÑÑнкÑÐ¸Ñ Ð¸Ð»Ð¸ меÑод декоÑиÑованнÑм, за иÑклÑÑением одной мелоÑи. ÐÑли иÑÑ
Ð¾Ð´Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð¿ÑедоÑÑавлÑÐµÑ ÑвойÑÑва, Ñакие как func.calledCount или Ñипа Ñого, Ñо декоÑиÑÐ¾Ð²Ð°Ð½Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð¸Ñ
не пÑедоÑÑавиÑ. ÐоÑÐ¾Ð¼Ñ ÑÑо ÑÑо обÑÑÑка. Так ÑÑо нÑжно бÑÑÑ Ð¾ÑÑоÑожнÑм в иÑ
иÑполÑзовании. ÐекоÑоÑÑе декоÑаÑоÑÑ Ð¿ÑедоÑÑавлÑÑÑ Ñвои ÑобÑÑвеннÑе ÑвойÑÑва.
ÐекоÑаÑоÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ ÑаÑÑмаÑÑиваÑÑ ÐºÐ°Ðº «дополниÑелÑнÑе возможноÑÑи» или «аÑпекÑÑ», коÑоÑÑе можно добавиÑÑ Ð² ÑÑнкÑиÑ. ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ добавиÑÑ Ð¾Ð´Ð¸Ð½ или неÑколÑко декоÑаÑоÑов. РвÑÑ ÑÑо без Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð´Ð° оÑигиналÑной ÑÑнкÑии!
ÐÐ»Ñ ÑеализаÑии cachingDecorator Ð¼Ñ Ð¸Ð·ÑÑили меÑодÑ:
- func.call(context, arg1, arg2â¦) â вÑзÑваеÑ
funcÑ Ð´Ð°Ð½Ð½Ñм конÑекÑÑом и аÑгÑменÑами. - func.apply(context, args) â вÑзÑваеÑ
func, пеÑедаваÑcontextкакthisи пÑевдомаÑÑивargsкак ÑпиÑок аÑгÑменÑов.
РоÑновном пеÑеадÑеÑаÑÐ¸Ñ Ð²Ñзова вÑполнÑеÑÑÑ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ apply:
let wrapper = function(original, arguments) {
return original.apply(this, arguments);
};
ÐÑ Ñакже ÑаÑÑмоÑÑели пÑÐ¸Ð¼ÐµÑ Ð·Ð°Ð¸Ð¼ÑÑÐ²Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼ÐµÑода, когда Ð¼Ñ Ð²ÑзÑваем меÑод Ñ Ð¾Ð±ÑекÑа в конÑекÑÑе дÑÑгого обÑекÑа. ÐеÑÑма ÑаÑпÑоÑÑÑанено заимÑÑвоваÑÑ Ð¼ÐµÑÐ¾Ð´Ñ Ð¼Ð°ÑÑива и пÑименÑÑÑ Ð¸Ñ
к arguments. РкаÑеÑÑве алÑÑеÑнаÑÐ¸Ð²Ñ Ð¼Ð¾Ð¶Ð½Ð¾ иÑполÑзоваÑÑ Ð¾Ð±ÑÐµÐºÑ Ñ Ð¾ÑÑаÑоÑнÑми паÑамеÑÑами ...args, коÑоÑÑй ÑвлÑеÑÑÑ ÑеалÑнÑм маÑÑивом.
Ðа пÑакÑике декоÑаÑоÑÑ Ð¸ÑполÑзÑÑÑÑÑ Ð´Ð»Ñ ÑамÑÑ ÑазнÑÑ Ð·Ð°Ð´Ð°Ñ. ÐÑовеÑÑÑе, наÑколÑко Ñ Ð¾ÑоÑо Ð²Ñ Ð¸Ñ Ð¾Ñвоили, ÑеÑÐ°Ñ Ð·Ð°Ð´Ð°Ñи ÑÑой главÑ.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)