Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: Event Handlers: Where to declare variables - local or closure (vs overhead)?

I find myself writing various functions which contain event handlers. It feels preferable to declare the variables required by the handler functions at the root of the parent function (closure), especially if they are jQuery selections, constants required by more than one handler, or some pre-computations needed which I wouldn't want to repeat each time en event is fired. A simple example:

var touchDrag = function() {

    var x, y, i;
    var $mySelection = $('.selection');

    $('#some-elem').on( 'touchmove', function(e) {

        x = something;
        y = something;
        i++;
        $mySelection.doSomething();

        // more code..
    });
}  

However, I often see handler variables declared inside the handler functions (local). Asked a couple of coders, some debate ensued but not a clear answer.

I understand that it's good practice to keep variable scope as small as possible. However, for frequently-firing events like .scroll() or touchmove, it seems to me there would be a big overhead re-declaring them each time an event is fired (as opposed to allocating each var just once)?

like image 756
pete Avatar asked Mar 20 '23 09:03

pete


2 Answers

In general a variable should be defined in the smallest scope in which it is needed. So, if a variable is only needed during the actual processing of a touchmove event and it does not carry state across from one event to the next, then I would generally declare it in the actual touchmove event handler so its scope is as small as possible and it is garbage collected when not being used.

There are, of course, exceptions that might benefit from declaring it at a higher scope such as:

  1. Precomputation. Rather than compute something everytime you need it, you'd rather compute it once and keep it handy. If these are user triggered events, then performance of a small precalculation is rarely actually relevant vs. user time.

  2. Preserve state from one occurrence of the event to the next. This requires declaring the variable at a higher level so that it can persist from one event to the next.

  3. Shared with other code. If the value in the variable is shared by other handlers in the same context, then obviously you have to declare it at a high enough level that it's available for all who want access to it. A common example of this might be a timer that one event wants to start and another event might want to stop.

Here are some reasons to avoid declaring variables at a higher scope which gives them a longer lifetime:

  1. Memory Leak. You can inadvertently create some memory leaks. If you're caching a DOM element and then elsewhere in your code you or someone else decides to replace that DOM element, now you have a reference to that DOM element in your JS code that will keep it from getting garbage collected.

  2. Stale values. The same scenario above can lead to a wrong value in a cached variable. There is always less risk of a stale value if you fetch or compute as needed rather than save the value over a long period of time.

  3. Code Clarity and Robustness. If you've got a set of variables declared at a higher scope and then a bunch of functions, each of which uses some of those variables, it is not very clear who is using what. If these are actually functions that can call each other, you have all sorts of possibilities for one function to trounce the variables being used by another function. Taken to it's logical extreme, this is exactly why local variables are preferable to global variables. While a one level higher scope isn't as bad as a global variable, it still has some of the same issues.


Also, in response to one of your concerns, declaring and initializing a local variable is not a "big overhead" unless the work to compute the initial value is a time consuming task. But in your example, simply moving var x into the function who uses it is not going to be a noticeable drop in performance when the function starts up and, in fact, it might even improve the performance during the execution of the function because accessing local variables is faster than accessing variables at a higher scope (the local namespace is checked for a variable first before higher scopes are checked).

As for the $mySelection variable you've now added to your question, I would declare it inside the smallest scope where it is needed until/unless you have any data/information that its initialization performance is actually causing you a problem. In general, simple selector search operations are pretty darn fast on modern CPUs.

As with pretty much all performance issues - trying to solve a performance issue prematurely before you have any evidence that it is actually an issue is rarely an effective use of time or a reason to diverge from the best coding practices. When in doubt, keep your code as simple and self-contained as possible.

like image 124
jfriend00 Avatar answered Apr 14 '23 15:04

jfriend00


Despite the fact that reusing as much code as possible is a good thing, in some cases, reusing variables might result in unexpected problems

  • Shared variables can be changed with side-effects. For example, in some large applications where there are a lot of events, using shared variables among them can lead to some unexpected behaviors as events are asynchronous and no one can predict when the events are completed

  • (In my opinion) It is hard to read and debug with shared variables because I don't know if something else is using it (which lead to several problems when refactoring my code)

like image 43
Tan Nguyen Avatar answered Apr 14 '23 16:04

Tan Nguyen