Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

JavaScript: стоит ли избегать стиля написания кода “функция в функции”?

#javascript #code_style


Допустим, у нас есть JavaScript приложение с большим количеством виджетов. Рассмотрим
на примере шапки. Функция initHeader() собирает все нужные для дальнейшей работы DOM-объекты
внутри шапки, добавляет обработчики событий, вообщем готовит к её использованию. Соответствует
ли приведённый ниже шаблон кода "правилам хорошего тона" написания кода?

function initHeader(){

    // объекты виджета, с которыми будем работать в функциях
    var var1 = $('#div1');
    var var2 = $('#div2');
    // ...


    // вспомогательные функции

    function function1(){
        // ...
    }

    function function2(){
        // ...
    }

    function function3(){
        // ...
    }
}


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

Зачем такая структура? Согласно книге Роберта Мартина "Чистый код", в идеале у функции
не должно быть аргументов вообще, а если аргументы неизбежны, то должен быть один,
максимум два аргумента. Но в нашем случае, внутри вспомогательных функций может быть
нужно большое количество переменных: jQuery-объекты, какие-то константы, рабочие переменные
наподобие времени анимации, какие-то введённые данные и так далее. Если написать все
вспомогательные функции вне основной, то тогда придётся всё это передавать в виде аргументов,
и в два аргумента уложиться едва ли получится. Такая структура, которую я показал,
избавляет нас от необходимости передавать аргументы, при этом все переменные, будучи
объявленными внутри initHeader(), за пределами данной функции недоступны. 

Второй плюс данного подхода в том, что он позволяет единыжды собрать все DOM-объекты
через функцию jQeury(), а потом уже обращаться к ним через имена переменных (если это
корректно называть кэшированием DOM-элементов, то можно сказать, что данный шаблон
кода создаёт условия для обеспечения кэширования DOM-элементов).

И ещё: это следует из названия функции, но всё же уточню, что сама функция initHeader()
вызывается только один раз (это можно сделать в HTML-коде сразу после закрывающего
, например), а внутренние функции уже могут быть вызваны сколько угодно раз.

Приведите, пожалуйста, аргументы за и/или против такого подхода.
    


Ответы

Ответ 1



В Javascript этот стиль используется для ограничения области видимости вложенных таким образом функций, с целью предоставлять только необходимые внешние интерфейсы (с аналогичной целью, как приватные методы в C++). См. например, паттерн модуль, фасад. Таким образом, ничего плохого тут не сделано, только в исходном примере паттерн недоразвит. Замечу, что нет необходимости добавлять собранные скрипты в header, лучше это сделать в конце body, чтобы на момент их загрузки все элементы разметки уже были обработаны. А условие загрузки определённой части кода (модуля) должно быть независимо от факта старта скрипта, управляться специальной сущностью - например инициализацией виджета или роутингом. Что касается упомянутой в вопросе рекомендации использовать один или менее аргументов у функции - надо понимать, что в контексте Javascript это просто забавно звучит и, вероятно, должно пониматься как-то иначе. Всё-таки, это мультипарадигменный, в т.ч. функциональный, язык программирования. Т.е. да, конечно, вместо множества аргументов лучше использовать каррированные функции и их композиции. Далее, с озвученной в вопросе целью, передаётся один аргумент - объект, что, вместе с сахаром ES6-деструктуризации и той или иной системой проверки типов, даёт самые гибкие средства для передачи групп сущностей в/из функции.

Ответ 2



Согласно книге Роберта Мартина "Чистый код", в идеале у функции не должно быть аргументов вообще, а если аргументы неизбежны, то должен быть один, максимум два аргумента. Разве он при этом не предполагает, что это должны быть чистые функции? Такая структура, которую я показал, избавляет нас от необходимости передавать аргументы, при этом все переменные, будучи объявленными внутри initHeader(), за пределами данной функции недоступны. А вот это мне кажется вообще прямо противоположным рекомендации про функции. Я считаю, что в большинстве случаев именованные функции должны получать то, с чем работают, через параметры.

Ответ 3



Вложенные функции создают замыкание, а замыкание действительно позволяет сделать "что-то" недоступным, но нужно ли так вообще делать? Очень часто люди употребляют в одном предложении такие слова как замыкание-инкапсуляция, инкапсуляция-приватность, приватность-защита каких-то данных от злоумышленников. Нет, это неправильно! Инкапсуляция предлагает скрывать работу с более низкоуровневым api и очень часто предлагает это реализовывать при помощи синтаксического сахара модификаторов доступа, к каковым относится и private. Модификаторы доступа в свою очередь созданы чтобы защититься от самого страшного врага кода - разработчика, но не для сокрытия данных, которые подменит или украдет злоумышленник. Это просто выдумки, ведь злоумышленником не нужно изменять Ваш код чтобы что-то украсть. Настоящая цель замыкания, это защита глобального пространства от засорения всем чем только его можно засорить. Но ведь Вы задали конкретный вопрос и наверняка хотите услышать на него ответ. Вложенные функции, это причина нечитаемости кода. Вложенные функции, это причина утечек. Вложенные функции при каждом вызове своего контекста создаются заново. Вложенные функции при этом бывают очень удобными в малых дозах. Возможно, кто-то ещё добавит аргументов и это Вам поможет определится с выбором. Когда-нибудь Вы как обычно сядете писать код и Вас озарит чувство, что сегодня Вы впервые написали так, как когда-то читали об этом в умной книжке. И именно такие моменты срывают пелену непонимания и открывают новые просторы для размышлений и осознания того, как нужно писать. За одну долю секунды Вы понимаете очень большую часть информации, которая до этого Вам казалась просто набором несвязанных слов. Но до этого момента старайтесь слушать только здравый смысл, который если говорит что здесь и сейчас будет удобно написать три аргумента, то значит нужно писать три аргумента. Не выворачивайте себя наизнанку если так велит какой-то человек написавший книгу. Он сорок лет учился и говорит о том что в самом начале вынесло бы мозг и ему так, будто бы это понятно всем. Но это вполне нормально и простительно. И лично я советую посмотреть в сторону классов, они намного полезнее чем о них рассказывают те, кто пишет на react и на frp забывая что они насквозь пронизаны классами.

Ответ 4



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

Комментариев нет:

Отправить комментарий