Хороший плохой манкипатчинг

в 16:16, , рубрики: javascript, Блог компании HTML Academy, манкипатчинг

Нам часто задают вопросы по технологиям в письмах и сообщениях — мы с удовольствием отвечаем. Но в таком случае ответ получает один человек, а он мог бы пригодиться многим.

Поэтому мы решили еженедельно разбирать по одному вопросу, буквально за пять минут.

Хороший плохой манкипатчинг - 1

Сегодня вопрос про разработку от Сергея, отвечает наш преподаватель Игорь Алексеенко:

Манкипатчинг — почему это так плохо или не так уж и плохо?

Ответить на этот вопрос, не начав холивар, сложно. Манкипатчинг работает, поэтому нельзя сказать, что он плох, но важно помнить, что с ним очень легко выстрелить себе в ногу.

Манкипатчинг — это когда мы изменяем код в рантайме, то есть по ходу выполнения программы. Например, добавляем несуществующие методы в массив или переопределяем какую-нибудь глобальную функцию.

Array.prototype.shuffle = function() {
  const i = this.length;
  while (i--) {
    fisherYatesShuffle(this, i);
  }
};

[1, 2, 3].shuffle();

Манкипатчинг идеально подходит для одноразовых задач, когда нужен реверс-инжиниринг. Например, если нужно отладить решение проблемы или разово собрать какую-нибудь аналитику, это подойдёт, но это заведомо одноразовые подходы.

Если говорить о промышленном коде, то неосторожно обращаясь с манки-патчами, можно сломать работу встроенных объектов, которые будут использоваться во всей программе. Представьте, что вы зачем-то, изменили поведение функции setTimeout. Внешне, она работает так же, но дополнительно эта функция решает какие-то другие задачи, например, сохраняет контекст.

const initNewTimeout = function() {
  const initialTimeout = window.setTimeout;
  window.setTimeout = function(fn, timeout, ctx) {
    initialTimeout(function() {
      fn.call(ctx);
    }, timeout);
  }
};

initNewTimeout();

setTimeout(tickInContext, 100, this);

Конечно, в вашем коде setTimeout будет работать так, как вам нужно, но вы не можете гарантировать, что в других местах, где он используется всё будет работать так же гладко. Есть и более экстремальные примеры, например, раньше в JavaScript можно было переопределить даже undefined и window.

Получается, что с манкипатчингом нужно быть очень осторожным и придерживаться определённых правил:

  • Добавлять новое лучше, чем изменять старое.

    Изменять поведение уже встроенных в язык методов плохо, потому что они могут использоваться где-то ещё кроме вашего кода. А вот, добавить что-то новое, чем кто-то другой вряд ли додумается воспользоваться — относительно безопасно. Например, добавить в массивы метод их перемешивания. Правда, через какое-то время, в массиве может появиться такой метод — стандарты развиваются, а браузеры быстро их подхватывают.

  • Нужно тщательно тестировать патчи.

    Это общий совет для разработчиков, но когда речь идёт о манкипатчах, нужно быть вдвойне осторожным, потому что в одном месте они могут помочь, а в другом только навредить. При написании манкипатчей нужно тестировать не только на сам патч, но и хорошо бы проверить, не сломалось ли чего-нибудь на уровне всего приложения.

  • Лучше использовать патчи как временные решения.

    Поскольку манкипатчи могут навредить, от них нужно отказываться, как только это становится возможным.

По этим правилам создаются полифилы — патчи, которые добавляют поддержку новых фич в старые браузеры. Согласитесь, как сильно меняет название отношение к подходу. Были обезьяньи исправления, а стал универсальный наполнитель. Были непонятные изменения встроенных объектов, а стало исправление проблем с поддержкой новых стандартов.

Кажется, что полифилы — это хорошая идея: подключаешь полифил и вне зависимости от того, какой у пользователя браузер, в коде можно использовать новые штуки, например, Fetch для HTTP-запросов, Promise или Object.entries для итерирования по объектам.

<script src="./promise-polyfill.js"></script>
<script src="./whatwg-fetch.js"></script>
<script>
  fetch('/api/request')
    .then(resp => resp.json())
    .then(json => render);
</script>

Но и в этом подходе есть проблемы:

  • Надо что-то делать с теми браузерами, которые уже поддерживают то, что вы патчите.

    Идеально бы отключать патчи для тех браузеров, в которых фича уже поддерживается. Обычно, разработчики полифилов берут это на себя, но этот момент нужно проверять.

  • Соответствие стандартам.

    Если речь идёт об уже принятом стандарте, который работает как минимум в одном браузере и его поддержка в остальных — это просто вопрос времени, то проблемы нет. Но если говорить о ещё обсуждающемся стандарте, лучше не создавать полифила, потому что программисты могут начать использовать его и привыкнут к нему, а разработчики стандартов не смогут внести в стандарт какие-то улучшения, потому что стандарт будет закреплён де-факто.

В таких ситуациях лучше использовать функции-обёртки, которые иногда называются понифилами. В принципе, они позволяют делать то же самое что и полифилы, но явно. А ещё у них нет проблем со стандартами: например, популярная библиотека Lodash выросла как раз из обёртки над неработающими итераторами в массивах. Ребята добавили в библиотеку forEach, map и прочие some, every, но не привязывались к стандарту и поэтому у них были изначально развязаны руки. Через какое-то время они добавили другие удобные методы для массивов и коллекций, такие как shuffle, unique, flatten и другие. Несмотря на то что итераторы в массивах поддерживаются уже давно, Lodash успешно развивается.

// полифил
[1, 2, 3].map(it => it / 2);

// понифил
arrayMap([1, 2, 3], it => it / 2);

Ещё, функции-понифилы проще тестировать. Очень сложно до конца протестировать полифил. Если подключить его там же где, выполняются тесты, ваш патч может изменить код самого фреймворка, что может повлиять на тестирование. А если тестировать логику полифилов отдельно, то непонятно будет, правильно ли они подключаются.

Ещё в разной среде ваши тесты будут работать по-разному, потому что в одних средах патч сработает, в других — нет. У понифилов таких проблем нет: вы всегда знаете что тестируете, они подключаются и работают явно.

Подытожим

Плох ли манкипатчинг? Судя по тому, что он работает, он не так плох, но при его использовании нужно помнить, что с ним проще сломать чей-то чужой код, его сложнее тестировать и можно вообще забыть, что он используется.

Материалы по теме

Видеоверсия

Вопросы можно задавать здесь.

Автор: htmlacademy

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js