> console.log(numberString + " " + label);
>}
>// вывестиИнвентаризациюФермы
>function printFarmInventory(cows, chickens, pigs) {
> printZeroPaddedWithLabel(cows, "Коров");
> printZeroPaddedWithLabel(chickens, "Куриц");
> printZeroPaddedWithLabel(pigs, "Свиней");
>}
>printFarmInventory(7, 11, 3);
Работает! Но название >printZeroPaddedWithLabel
немного странное. Оно объединяет три вещи – вывод, добавление нулей и метку – в одну функцию. Вместо того, чтобы вставлять в функцию весь повторяющийся фрагмент, давайте выделим одну концепцию:
>// добавитьНулей
>function zeroPad(number, width) {
> var string = String(number);
> while (string.length < width)
> string = "0" + string;
> return string;
>}
>// вывестиИнвентаризациюФермы
>function printFarmInventory(cows, chickens, pigs) {
> console.log(zeroPad(cows, 3) + " Коров");
> console.log(zeroPad(chickens, 3) + " Куриц");
> console.log(zeroPad(pigs, 3) + " Свиней");
>}
>printFarmInventory(7, 16, 3);
Функция с хорошим, понятным именем >zeroPad
облегчает понимание кода. И её можно использовать во многих ситуациях, не только в нашем случае. К примеру, для вывода отформатированных таблиц с числами.
Насколько умными и универсальными должны быть функции? Мы можем написать как простейшую функцию, которая дополняет число нулями до трёх позиций, так и навороченную функцию общего назначения для форматирования номеров, поддерживающую дроби, отрицательные числа, выравнивание по точкам, дополнение разными символами, и т. п.
Хорошее правило – добавляйте только ту функциональность, которая вам точно пригодится. Иногда появляется искушение создавать фреймворки общего назначения для каждой небольшой потребности. Сопротивляйтесь ему. Вы никогда не закончите работу, а просто напишете кучу кода, который никто не будет использовать.
Функции и побочные эффекты
Функции можно грубо разделить на те, что вызываются из-за своих побочных эффектов, и те, что вызываются для получения некоторого значения. Конечно, возможно и объединение этих свойств в одной функции.
Первая вспомогательная функция в примере с фермой, >printZeroPaddedWithLabel
, вызывается из-за побочного эффекта: она выводит строку. Вторая, >zeroPad
, из-за возвращаемого значения. И это не совпадение, что вторая функция пригождается чаще первой. Функции, возвращающие значения, легче комбинировать друг с другом, чем функции, создающие побочные эффекты.
Чистая функция – особый вид функции, возвращающей значения, которая не только не имеет побочных эффектов, но и не зависит от побочных эффектов остального кода – к примеру, не работает с глобальными переменными, которые могут быть случайно изменены где-то ещё. Чистая функция, будучи вызванной с одними и теми же аргументами, возвращает один и тот же результат (и больше ничего не делает) – что довольно приятно. С ней просто работать. Вызов такой функции можно мысленно заменять результатом её работы, без изменения смысла кода. Когда вы хотите проверить такую функцию, вы можете просто вызвать её, и быть уверенным, что если она работает в данном контексте, она будет работать в любом. Не такие чистые функции могут возвращать разные результаты в зависимости от многих факторов, и иметь побочные эффекты, которые сложно проверять и учитывать.