Proxy и мемоизация функции


Есть такая платформенная штука — Proxy. Это когда берёшь обычный объект в JS, создаёшь для него прокси и получаешь возможность перехватывать и переопределять основные операции, которые с объектом можно совершить: get, set, delete… Внутри перехватчиков можно, например, логировать, валидировать или дополнять значения. Например:

const target = {
message1: "hello",
message2: "everyone",
};
const handler = {
get(target, prop, receiver) {
console.log(`Свойство ${prop} считалось`);
return target[prop];
},
set(target, prop, value) {
console.log(`Свойству ${prop} задано значение ${value}`);
target[prop] = value;
return true;
},
};
const proxiedObj = new Proxy(target, handler);
proxiedObj.message1;
// log: Свойство message1 считалось
proxiedObj.message2 = "nobody";
// log: Свойству message2 задано значение nobody

Так вот, проксировать можно не только объекты, но и функции. В случае функций через прокси можно перехватывать момент вызова функции (перехватчик apply) и, например, манипулировать аргументами до вызова.

const a = () => {
// функция
};
const b = new Proxy(a, {
apply: (target, thisArg, argumentsList) => {
// перехватчик вызова
},
});

До вызова функции мы получаем референс на саму функцию, контекст и массив агрументов и можем с ними делать что угодно перед тем, как непосредственно вызвать (или даже не вызвать) функцию. К примеру, можно организовать кэш, чтобы по переданному набору аргументов запомнить вычисленное значение и при повторном вызове брать его из кэша.

function memoize(target) {
const cache = new Map();
return new Proxy(target, {
apply: (target, thisArg, argumentsList) => {
const key = JSON.stringify(argumentsList);
if (cache.has(key)) {
console.log(`Результат для ${argumentsList} взят из кэша`);
return cache.get(key);
} else {
const result = target.apply(thisArg, argumentsList);
cache.set(key, result);
return result;
}
},
});
const add = (a, b) => a + b;
const memoizedAdd = memoize(add);
memoizedAdd(3, 7); // Вычисляет и записывает в кэш
memoizedAdd(3, 7); // Берёт результат из кэша
}

Аргументами функции, то есть и ключами в кэше могут быть не только примитивы, но и объекты, за счёт того, что ключ формируется с помощью JSON.stringify.

Таким образом, с помощью Proxy можно на коленке собрать «мидлвари» для объектов, массивов и функций, в общем-то, с любыми целями.