03.ii Bound Function

A bound function is a function bind with an object using fn.bind(obj) method. As a result of bind call we receive new function with a this bound to obj forever. .bind() makes a permanent context link and will always keep it.

The original and bound functions share the same code and scope, but different contexts on execution. this is the first argument of .bind() when invoking a bound function.

fun.bind(thisArg, [arg1, arg2, ...])

  • thisArg value to be passed as the this parameter to the target function when the bound function is called. The value is ignored if the bound function is constructed using the new operator.

  • [arg1, arg2, ...] - arguments provided to the bound function when invoking the target function.

In general, .bind function is equals to following code:

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

Методы call/apply вызывают функцию с заданным контекстом и аргументами. А bind не вызывает функцию. Он только возвращает «обёртку», которую мы можем вызвать позже, и которая передаст вызов в исходную функцию, с привязанным контекстом.

Use bind to carry function:

Карринг (currying) или каррирование – термин функционального программирования, который означает создание новой функции путём фиксирования аргументов существующей.

При помощи bind удобно выполнять каррирование функции:

function mul(a, b) {
  return a * b;
}
// double умножает только на два
var double = mul.bind(null, 2); // контекст фиксируем null, он не используется

double(3); // = mul(2, 3) = 6
double(4); // = mul(2, 4) = 8
double(5); // = mul(2, 5) = 10

Говорят, что double является частичной функцией (partial function) от mul.

Decorate functions with bind

Через функцию bind и привязку контекста очень удобно делать функции-декораторы поверх других функций. Создадим декоратор, замеряющий время выполнения функции. Он будет называться timingDecorator и получать функцию вместе с «названием таймера», а возвращать — функцию-обёртку, которая измеряет время и прибавляет его в специальный объект timer по свойству-названию:

const timers = {};

// прибавит время выполнения f к таймеру timers[timer]
function timingDecorator(f, timer) {
  return function() {
    const start = performance.now();

    const result = f.apply(this, arguments); // (*)

    if (!timers[timer]) timers[timer] = 0;
    timers[timer] += performance.now() - start;

    return result;
  };
}

// функция может быть произвольной, например такой:
function fibonacci(n) {
  return n > 2 ? fibonacci(n - 1) + fibonacci(n - 2) : 1;
}

// использование: завернём fibonacci в декоратор
fibonacci = timingDecorator(fibonacci, "fibo");

// неоднократные вызовы...
fibonacci(10); // 55
fibonacci(20); // 6765
// ...

// в любой момент можно получить общее количество времени на вызовы
alert(timers.fibo + "мс");

Декораторы можно не только повторно использовать, но и комбинировать!

Это кардинально повышает их выразительную силу. Декораторы можно рассматривать как своего рода возможности, которые можно «нацепить» на любую функцию. Можно один, а можно несколько.

Last updated