Чейнинг функций

Method chaining — стиль вызова, где каждый метод возвращает объект (часто this), позволяя вызывать методы цепочкой через точку. Применяется в jQuery, array methods, Promise, ORM-запросах.

Что это / Зачем

  • Цепочка: obj.a.b.c вместо c(b(a(obj)))
  • Кратко выражает последовательность преобразований
  • Главное правило: каждый метод возвращает что-то, на чём можно продолжить цепочку
  • Может быть мутабельным (возвращает this) или иммутабельным (новый объект)

Способы реализации

Через классы

class Calculator {
  constructor { this.value = 0; }
  add(n)      { this.value += n; return this; }
  subtract(n) { this.value -= n; return this; }
  get result { return this.value; }
}

new Calculator
  .add(10)
  .subtract(3)
  .add(5)
  .result; // 12

Через замыкание (без классов)

const calc = (initial = 0) => {
  let value = initial;
  const api = {
    add(n)      { value += n; return api; },
    subtract(n) { value -= n; return api; },
    valueOf   { return value; },
  };
  return api;
};

+calc.add(5).subtract(2); // 3

Через прототип (производительнее)

function Hash() { this.data = {}; }
Hash.prototype.add = function(k, v) {
  this.data[k] = v;
  return this;
};

new Hash.add('a', 1).add('b', 2);

Иммутабельный (новый объект на каждом шаге)

class ImmutableText {
  constructor(s = '') { this.s = s; }
  append(x) { return new ImmutableText(this.s + x); }
  upper   { return new ImmutableText(this.s.toUpperCase()); }
  toString { return this.s; }
}

new ImmutableText('hi').append(' world').upper.toString(); // "HI WORLD"

Мутабельный vs иммутабельный

Мутабельный (return this) Иммутабельный (new object)
Память Один объект Новый на каждом шаге
Скорость Быстрее Медленнее
Ветвление Нельзя — общий стейт Можно — каждая ветка независима
Пример Array.prototype.sort() Promise, Immer

Пример ветвления цепочек

const root = chain.add(1);
const a = root.add(2);  // если иммутабельный — независим
const b = root.add(3);

Ленивое исполнение

class Query {
  constructor { this.ops = ; }
  where(cond) { this.ops.push({ op: 'where', cond }); return this; }
  limit(n)    { this.ops.push({ op: 'limit', n }); return this; }
  execute   { /* применяет ops по порядку */ }
}

new Query.where(x => x > 0).limit(10).execute;
  • Методы цепочки только накапливают операции
  • Реальное исполнение — отдельным шагом (execute, await, then)
  • Применяется в ORM (Knex, Prisma), Promise

Promise — асинхронный чейнинг

fetch('/api')
  .then(r => r.json())
  .then(data => process(data))
  .catch(err => console.error(err));
  • then всегда возвращает новый Promise
  • Ошибка из любого шага падает в ближайший catch
  • Иммутабельная семантика (по сути)

Ключевые правила

  • Метод должен возвращать что-то, поддерживающее следующий метод
  • return this — самый простой способ
  • Array.prototype.sort() ломает чейнинг для иммутабельной работы — он мутирует
  • При чейнинге через прототипы — быстрее, чем через замыкания

Подводные камни

  • Цепочка с побочными эффектами трудна для отладки
  • Мутабельный чейнинг не позволяет ветвление
  • Ошибка в одном звене ломает всё — нужно учитывать в дизайне
  • Иммутабельные цепочки требовательнее к памяти

🎓 Источник: Чеининг функций и объектов

  • 📅 2018-10-29 · YouTube · ID: PfuEfIiLX34
  • Тезисы:
    • 4 реализации чейнинга: функциональный (скобки), объектный (return this), через прототип, через замыкание
    • Прототипы оказываются быстрее Promise в бенчмарках
    • Ленивое исполнение — накопить операции, потом запустить
    • Идея — return this или новый объект с такими же методами
    • try/catch ловит только синхронные ошибки — для async нужен иной механизм
  • Цитата: «Смысл чейнинга — return this. Возвращай объект с методами, и цепочка продолжается.»

См. также