Нам часто задают вопросы по технологиям в письмах и сообщениях — мы с удовольствием отвечаем. Но в таком случае ответ получает один человек, а он мог бы пригодиться многим.
Поэтому мы решили еженедельно разбирать по одному вопросу, буквально за пять минут.
Сегодня вопрос про разработку от Сергея, отвечает наш преподаватель Игорь Алексеенко:
Манкипатчинг — почему это так плохо или не так уж и плохо?
Ответить на этот вопрос, не начав холивар, сложно. Манкипатчинг работает, поэтому нельзя сказать, что он плох, но важно помнить, что с ним очень легко выстрелить себе в ногу.
Манкипатчинг — это когда мы изменяем код в рантайме, то есть по ходу выполнения программы. Например, добавляем несуществующие методы в массив или переопределяем какую-нибудь глобальную функцию.
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