Должен ли я поместить функции, которые используются только в одной другой функции, в эту функцию?


31

Конкретно я пишу на JavaScript.

Допустим, моя основная функция - это функция A. Если функция A делает несколько вызовов функции B, но функция B больше нигде не используется, то я должен просто поместить функцию B в функцию A?

Это хорошая практика? Или я все еще должен поместить функцию B в ту же область, что и функция A?

Ответы:


32

Я вообще за вложенные функции, особенно в JavaScript.

  • В JavaScript единственный способ ограничить видимость функции - это поместить ее в другую функцию.
  • Вспомогательная функция является частной реализацией. Если поместить его в один и тот же контекст, все равно, что сделать частные функции класса публичными.
  • Если он оказывается более общего использования, его легко покинуть, потому что вы можете быть уверены, что помощник в настоящее время используется только одной этой функцией. Другими словами, это делает ваш код более сплоченным.
  • Вы можете часто исключать параметры, что делает подпись и реализацию функции менее подробными. Это не то же самое, что сделать переменные глобальными. Это больше похоже на использование члена класса в приватном методе.
  • Вы можете дать вспомогательным функциям лучшие, более простые имена, не беспокоясь о конфликтах.
  • Вспомогательную функцию легче найти. Да, есть инструменты, которые могут вам помочь. Еще проще просто немного поднять глаза на экране.
  • Код, естественно, образует своего рода дерево абстракции: несколько общих функций в корне, разветвляющихся на несколько деталей реализации. Если вы используете редактор с функцией свертывания / свертывания, вложение создает иерархию тесно связанных функций на том же уровне абстракции. Это позволяет очень легко изучить код на нужном вам уровне и скрыть детали.

Я думаю, что многие возражают против того факта, что большинство программистов были воспитаны на традициях C / C ++ / Java или учились кем-то еще. Вложенные функции не выглядят естественными, потому что мы не были подвержены им много, когда мы учились программировать. Это не значит, что они бесполезны.


14

Вы должны поместить это в глобальный охват, по нескольким причинам.

  • Вложение вспомогательной функции в вызывающую сторону увеличивает длину вызывающей стороны. Длина функции почти всегда является отрицательным показателем; короткие функции легче понять, запомнить, отладить и поддерживать.

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

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

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

редактировать

Это оказалось более спорным вопросом, чем я думал. Чтобы уточнить:

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

И дело о более легких предполагает повторного использования , что если подпрограмма действительно становится все более широко используются, вы готовы , чтобы переместить его из места в целом и поместить его в нужном месте, например , в строке служебной библиотеке или в ваш глобальный реестр конфигурации. Если вы не хотите упорядочивать свой код таким образом, вы можете также вложить подпрограмму, как если бы вы делали это с обычным объектом метода на более «блочном» языке.


Спасибо, все очень хорошие моменты. С другой стороны, как мне следует упорядочить свои функции? Например, на данный момент с небольшими программами я просто упорядочиваю их по алфавиту. Но следует ли мне сгруппировать вспомогательные и вызывающие функции? Думаю, мне следует хотя бы разбить мои функции, например, на какие части приложения они относятся?
Гэри

Я давно не беспокоился об определениях порядка заказа. Если вы программируете с какой-либо степенью профессионализма, у вас должна быть среда, которая в любом случае позволяет вам перейти к любому определению с помощью нескольких нажатий клавиш. Поэтому короткие методы полезны, но короткие или даже упорядоченные файлы классов не приносят никакой пользы. (Конечно, JavaScript раньше требовал, чтобы все определения были помещены выше их использования, иначе результатом было бы технически неопределенное поведение - не могу вспомнить, так ли это до сих пор.)
Килиан Фот,

2
Это отличный ответ. Проблема нарушенной инкапсуляции - это то, что многие не учитывают при использовании внутренних функций.
сбиченко

2
Глобалы - это кодовый запах. Они вызывают ненужное связывание, которое предотвращает рефакторинг, они вызывают конфликты имен, раскрывают детали реализации и т. Д. Это особенно плохо в Javascript, поскольку все переменные являются изменяемыми, код, как правило, загружается напрямую от сторонних разработчиков, но не (пока) широко доступная модульная система или даже широко доступная реализация «let». В такой враждебной среде единственной защитной стратегией, которую мы имеем, является инкапсуляция внутри одноразовых функций.
Warbo

1
«Выполняя роль классов» пытается написать JS, как если бы это было что-то еще. Что такое "роль классов"? Тип проверки? Модульность? Постановочный сборник? Наследование? Вопрос включает в себя определение объема, поэтому я предполагаю, что вы имеете в виду грубые правила определения объема занятий. Это просто бремя: как следует ограничивать классы ? PHP делает их глобальными, что не обеспечивает инкапсуляцию. Python ограничивает классы лексически, но если вы находитесь в блоке с лексической областью, зачем заключать все в другой блок с областью действия? JS не имеет такой избыточности: просто используйте столько лексической области, сколько вам нужно.
Варбо

8

Я собираюсь предложить третий путь, чтобы поместить обе функции в замыкание. Это будет выглядеть так:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

Мы создаем замыкание, оборачивая объявление обеих функций в IIFE . Возвращаемым значением IIFE является открытая функция, которая хранится в переменной имени функции. Общественная функция может вызываться точно так же, как если бы она была объявлена как глобальная функция, то есть functionA(). Обратите внимание, что возвращаемое значение является функцией , а не вызовом функции, поэтому в конце не должно быть скобок.

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


0

Определение функции B внутри функции A дает функции B доступ к внутренним переменным A.

Таким образом, функция B, определенная внутри функции A, создает впечатление (или, по крайней мере, возможность), что B использует или изменяет локальные переменные A. Хотя определение B за пределами A дает понять, что B нет.

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


-1

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


-3

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


1
это, кажется, не добавляет ничего существенного к пунктам, изложенным и объясненным в предыдущих 3 ответах
комнат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.