Как работает декоратор?

function slow(x) {
  alert(`Called with ${x}`);
  return x;
}

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;
  };
}

slow = cachingDecorator(slow); // *

alert( slow(1) ); // slow(1)
alert( "Again: " + slow(1) );

Как это работает? Мы сохраняем cachingDecorator(slow) в переменную slow, которая у нас является функцией. Т. е. вставили функцию#1 в другую функцию#2 и тем самым присвоили все в функцию#1. Если есть источники, где более ясно все объясняется , пожалуйста, скиньте.


Ответы (1 шт):

Автор решения: Leonid

cachingDecorator() - создает замыкание, то есть сочетания функции, переданной в нее, и области видимости, привязанной к этой функции.

В данном случае, метод cachingDecorator() (чтобы не путать) возвращает анонимную функцию, которая имеет доступ к следующим данным:

  • переданная в cachingDecorator() функция (slow())
  • созданный для анонимной функции словарь (cache = Map()).

В словарь записываются данные вызовов функции slow. В качестве ключа записывается параметр, с которым она вызвана. А в качестве значения - возвращенные данные.

Таким образом, анонимная функция "запоминает" свои вызовы. При повторном вызове с этим же параметром она просто возвращает данные из "кэша", не вызывая повторно функцию slow().

Выражение cachingDecorator(slow) создает своего рода кэширующую оболочку над функцией slow(). Внутри этой оболочки будет вызвана функция slow() только в том случае, если с таким же параметром она не вызывалась.

Выражение slow = cachingDecorator(slow) переопределяет переменную slow - назначая ей новую функцию-оболочку. Для того, чтобы проще понять это преобразование, можно объявление функции slow() переписать следующим образом:

var slow = function(x){return x}; // alert я убрал

Это почти эквивалентное выражение для function slow(x){return x}. В результате объявляем переменную slow и присваиваем ей объект-функцию. Эту функцию можно вызывать с помощью круглых скобок, передавая внутри необходимые параметры. Все так же.

Поэтому в выражении slow = cachingDecorator(slow) мы в эту же переменную кладем другую функцию, но которая хранит "знание" прежней функции, а также имеет доступ к словарю ее вызовов.

// slow() - простая функция, возвращающая переданный в нее аргумент как есть.

function slow(x) {
  return x;
}

// cachingDecorator() - функция, принимающая в качестве параметра другую функцию как объект
// создает замыкание для переданной функции (сохраняемую индивидуальную область видимости)
// возвращает анонимную функцию, имеющую доступ к данным вмещающей ее функции

function cachingDecorator(func) { 
  let cache = new Map(); // "Словарь" для хранения данных замыкания
    
    // анонимная (безымянная функция), принимающая один аргумент - x
  return function(x) {
    if (cache.has(x)) {     // Если в словаре есть ключ == x
      return cache.get(x);  // Возвращаем значение под этим ключом
    }
    // Иначе вызываем, переданную в cachingDecorator, функцию.
    // Возваращенный результат присваиваем переменной result
    let result = func(x);

    cache.set(x, result); // Записываем данные в cache под ключом x
    return result;        // Возвращаем результат
  };
}

// Переопределяем переменную slow
// Теперь она содержит результат вызова cachingDecorator(slow)
// т.е. она хранит функцию, имеющую доступ к пустому словарю и объекту-функции slow()
slow = cachingDecorator(slow);

// Первый вызов новой функции с параметром 1
// В словаре ключа 1 нет -> вызываем функцию (== slow(1))
// Возвращаемое значение (1) записываем в словарь под ключом 1
console.log( slow(1));

// Повторный вызов функции с параметром 1
// В словаре содержится ключ 1
// Возвращаем значение из словаря под ключом 1 - 1
console.log( "Again: " + slow(1) );

→ Ссылка