Skip to content

Instantly share code, notes, and snippets.

@YozhEzhi
Last active July 9, 2018 20:48
Show Gist options
  • Select an option

  • Save YozhEzhi/c5170c82195ab499511d5c0f423ef813 to your computer and use it in GitHub Desktop.

Select an option

Save YozhEzhi/c5170c82195ab499511d5c0f423ef813 to your computer and use it in GitHub Desktop.
Функции-обёртки (декораторы) (ES5).

Декоратор – приём программирования, который позволяет взять существующую функцию и изменить/расширить ее поведение.

Декоратор получает функцию и возвращает обертку, которая делает что-то своё «вокруг» вызова основной функции.

function bind(func, context) {
  return function() {
    return func.apply(context, arguments);
  };
}

Вызов bind(func, context) возвращает обёртку, которая ставит this и передаёт основную работу функции func.

Декоратор для замера скорости выполнения функций:

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 + 'мс');

На строке (*) внутри декоратора осуществляется передача вызова: Этот приём называется «форвардинг вызова» (от англ. forwarding): текущий контекст и аргументы через apply передаются в функцию f, так что изнутри f всё выглядит так, как была вызвана она напрямую, а не декоратор.

Декоратор для проверки типа аргументов:

// вспомогательная функция для проверки на число
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

Допустим, мы хотим выводить элементы массива, обёрнутые в хтмл теги.
В данном способе - мы передаём тег в аргумент функции.
var weekdays = ["понедельник", "вторник", "среда", "четверг", "пятница", "суббота", "воскресенье"];

function getDay(num) {
  return weekdays[num];
}

// декоратор
function wrapDecorator(f, startTag, closeTag) {
  return function(num) {
    return startTag + f.apply(this, arguments) + closeTag;
  };
}

// Использование декоратора
var getDayInDiv = wrapDecorator(getDay, "<div>", "</div>");

for (var i = 0; i < 7; i++) {
  console.log(getDayInDiv(i));
}

Результат:

<div>понедельник</div>
<div>вторрник</div>
<div>среда</div>
<div>четверг</div>
<div>пятница</div>
<div>воскресенье</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment