Suppose we have a JavaScript application with a large number of widgets. Consider the example caps. The initHeader() function collects all the DOM objects necessary for further work inside the header, adds event handlers, generally prepares for its use. Does the following code pattern "good tone" code writing?

 function initHeader(){ // объекты виджета, с которыми будем работать в функциях var var1 = $('#div1'); var var2 = $('#div2'); // ... // вспомогательные функции function function1(){ // ... } function function2(){ // ... } function function3(){ // ... } } 

The peculiarity of this template is that all auxiliary functions are inside the main one.

Why such a structure? According to Robert Martin’s book Clean Code, ideally the function should have no arguments at all, and if the arguments are unavoidable, then there should be one, maximum two arguments. But in our case, inside auxiliary functions, you may need a large number of variables: jQuery objects, some constants, working variables like animation time, some entered data, and so on. If you write all the auxiliary functions outside the main one, then you will have to pass all this in the form of arguments, and you’ll hardly manage to get into two arguments. This structure, which I showed, eliminates the need to pass arguments, while all variables, being declared inside initHeader() , are not available outside of this function.

The second advantage of this approach is that it allows you to collect all DOM objects jQeury() through the jQeury() function, and then access them through variable names (if it is correctly called caching DOM elements, then you can say that this code template creates conditions to ensure caching of DOM elements).

And one more thing: it follows from the function name, but I’ll clarify that the initHeader() function itself is called only once (this can be done in the HTML code immediately after the closing </header> , for example), and internal functions can already be called as many times as you like

Please provide arguments for and / or against such an approach.

  • I'm afraid I misunderstood something (my idea is too obvious), but if you have some data that the computer previously spent on generating, then you need to pass this data in the form of arguments, and not generate each just inside the function just because Robert Martin likes it so much. Well, I do not understand why this is a limitation of arguments in terms of quantity, because you can transfer them within a single object at least a million. - Beast Winterwolf
  • I apologize. This follows from the name of the function, but still I had to say that the initHeader() function itself is executed only once, for example, it can be called after the HTML code of the header, then the function will be executed after the header is displayed in the browser. All auxiliary functions can be called at any time (an example of an auxiliary function is the search button handler in the header). - Bokov Gleb
  • one
    in my opinion here is the right approach. the closures in JS are for this purpose made to contain the set of variables and functions we need without blocking the global scope - Mike
  • The reality is that if you were a Christian, you will be accepted as your Christian. Whether you are a satanist you will be accepted as your satanists. And in fact, everything is right in its own way and to say what you should do this or that also means imposing some kind of religion on you, albeit a different one. I do not understand why we need closures in this form. If for non-pollution of the global area, then for this came up with modules. For the rest, they are needed only by those who write on in a functional style. Although in this implementation these are just ordinary procedures and therefore the style is not functional, but procedural. - user220409
  • one
    By the way, here it is necessary to divide what is said above - about performance or about the quality of the pattern, with reference to, in particular, Javascript ... - 11111000000

4 answers 4

In Javascript, this style is used to limit the scope of functions so nested, in order to provide only the necessary external interfaces (with a similar purpose as private methods in C ++). See for example, pattern module , facade . Thus, nothing bad has been done here, only in the original example the pattern is underdeveloped.

I note that there is no need to add the collected scripts to the header , it is better to do this at the end of the body , so that at the time of loading all the markup elements have already been processed.

And the condition of loading a certain part of the code (module) should be independent of the fact that the script starts, be controlled by a special entity — for example, widget initialization or routing.

As for the recommendation mentioned in the question to use one or less arguments of a function - one should understand that in the context of Javascript this sounds just funny and probably should be understood in some other way. Still, it is multi-paradigm, incl. functional, programming language . Those. Yes, of course, instead of a set of arguments, it is better to use curried functions and their compositions . Further, with the goal voiced in the question, one argument is passed - the object, which, together with the ES6 destructurization sugar and this or that type checking system, provides the most flexible means for transferring groups of entities to / from a function.

  • Thank you for your reply! Interesting. And what do you mean by "widget initialization"? The function that I cited as an example, and initializes the widget (namely, the header, but not important). However, you obviously mean by "widget initialization" something else. - Bokov Gleb
  • strictly speaking, it could be anything - 11111000000
  • timer or document.registerElement or component render - i.e. meaning the entity that initializes your module - 11111000000
  • supplemented the answer about the number of arguments - 11111000000
  • It sounds more amusing that it is BETTER to use currying. - user220409

According to Robert Martin’s book Clean Code, ideally the function should have no arguments at all, and if the arguments are unavoidable, then there should be one, maximum two arguments.

Does he not assume that these should be pure functions?

This structure, which I showed, eliminates the need to pass arguments, while all variables, being declared inside initHeader() , are not available outside of this function.

But this seems to me to be the exact opposite of recommendations about functions.

I believe that in most cases, named functions should receive what they work with through parameters.

  • And if they work with what, about five or ten? This does not mean that the function is large; This may be a narrowly specialized function. - Bokov Gleb
  • @GurebuBokofu, parameters can always be combined into an object, and one parameter passed to the function - Grundy

Nested functions create a closure, and closure really allows you to make “something” inaccessible, but is it necessary to do that at all? Very often, people use in one sentence such words as замыкание-инкапсуляция , инкапсуляция-приватность , приватность-защита каких-то данных от злоумышленников . No, this is wrong! Encapsulation offers to hide work with lower-level api and very often offers to implement this with the help of syntactic sugar access modifiers, which include private . Access modifiers, in turn, are designed to protect themselves from the worst enemy of the code - the developer, but not to hide data that the attacker replaces or steals. It's just fiction, because the attacker does not need to change your code to steal something.

The real purpose of the circuit is to protect the global space from clogging with everything that can clog it.

But you asked a specific question and probably want to hear the answer to it.

Nested functions are the reason for unreadable code. Nested functions are the cause of leaks. Nested functions are created anew each time you call your context. Nested functions are very convenient in small doses.

Perhaps, someone else will add arguments and this will help you to make a choice.

Someday you will sit down to write code as usual and you will be filled with the feeling that today you wrote for the first time in the way you read about it in a smart book. And it is precisely such moments that break the veil of misunderstanding and open up new spaces for reflection and awareness of how to write. In one split second, you understand a very large part of the information that previously seemed to you to be just a collection of unrelated words. But up to this point, try to listen only to common sense, which if it says that here and now it will be convenient to write three arguments, it means you need to write three arguments. Do not turn yourself inside out if so ordered by someone who wrote the book. He studied for forty years and says that at the very beginning the brain would carry it out as if it were understood by everyone. But this is completely normal and excusable.

And personally, I advise you to look in the direction of the classes, they are much more useful than those who write about react and on frp forgetting that they are penetrated through classes.

  • Thank you for your reply! I did not pursue the goal of hiding data from users, since the JavaScript code is open in nature and no important data can be stored there. The goal is, as you said, to protect the global space from clogging. - Gleb

Of course, yes - it should be avoided, divide the functions into several groups, the simplest, performing elementary things separately from the more loaded ones and so on to the most complex ones or use classes, the principle is the same.

  • Try to write more detailed answers. Explain what your statement is based on - Grundy
  • Yes, please explain. Actually, I do what you say. Some very common functions such as getting time in the desired format stand in a separate file. In the example that I gave, we have the main function initHeader() and some auxiliary simple functions, but they relate only to the header. We are now discussing what to do with these functions — declare them inside the main one, or outside it. In the second case, you will have to pass arguments, as well as each time re-conduct the resource-intensive procedure of capturing DOM elements within functions. - Bokov Gleb
  • @Gurebu Bokofu - if these functions are not needed anywhere else, then it is not necessary to take them out. Good practice is not to make unnecessary variables and functions in the global scope, and I also advise you to read about: замыкание, функции изнутри - And
  • @And, I don’t know if you wanted to say that the approach I have given is correct, but everything you said is at the core of this method. - Bokov Gleb
  • @Gurebu Bokofu, first read your question, and then read my comment and I assure you - the questions will disappear by themselves. ;) - And