When are nested functions generally good for JavaScript?

I am learning JavaScript through this site . Link to a specific chapter that I am currently reading.

In the book, the author talks about storing the implementation details of a module in the local area. He achieves this by doing:

var dayName = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return function(number) {
    return names[number];
  };
}();

      

I understand how it works, but I don't understand why it does it. Why doesn't he just do ...

function dayName(number) {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return names[number];
}

      

... which looks much cleaner and more readable code to me? Given its purpose to keep names

within the local scale, creating a nested lambda seems like overkill.

Is there any real benefit to using a nested function in this case? Or does he only use it for educational purposes?

Thank!

+3


source to share


5 answers


The toy example you show is not very convincing. The main advantage of writing this way is that the array is created only once, not every time you call the function. This makes it a little faster by keeping the array allocated for the entire session (common time-space communique). In practice, few people program this path.

The technique becomes more useful when you have multiple functions and they work with common data. If one of the functions modifies the variable, this is remembered in the closure and visible to other functions when they are called later.

var dayName = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return {
    getDay: function(number) {
        return names[number];
    },
    setDay: function(number, newName) {
        names[number] = newName;
    }
  };
}();

      



Now you can write:

console.log(dayName.getDay(3)); // prints "Wednesday"
dayObj.setDay(3, "Mercredi");
console.log(dayName.getDay(3)); // prints "Mercredi"

      

You cannot do this with your second form of the function, because it has no memory from one call to the next.

+2


source


One case where you might want to return such a function is when you create several similar event listeners. For example, suppose you have buttons named button1

, button2

and button3

. You can add click listeners like this (using JQuery for brevity):

button1.click(() => console.log(1));
button2.click(() => console.log(5));
button1.click(() => console.log(42));

      

This can be written instead:



function makeLogger(x) {
    return () => console.log(x);
}

button1.click(makeLogger(1));
button2.click(makeLogger(5));
button1.click(makeLogger(42));

      

This makes an even bigger difference if you can't use the arrow function for compatibility reasons (not using Babel).

+1


source


In modern JavaScript, you would put code like this in a module ( CommonJS , AMD, or ES6 ). In this case, something will be good

const names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];

function dayName(number) {
  return names[number];
}

module.exports = {
  dayName: dayName,
};

      

Otherwise, yes, it makes sense to do it your own way. Your way to recreate the array of names every time you execute the function. Its way it only created once, and it doesn't pollute the namespace (no one else but dayName

can't see the array names

.

You really need to learn modules of one type or another.

In response to the question When are nested functions usually appropriate in JavaScript? the general answer is when you want to close.

In the case where you placed an inner function, it's a closure. It closes over a variable names

.

Closures is a fairly simple and generic JavaScript function. They are especially useful for callbacks.

+1


source


One possible benefit of this code is to cache the result of highly computational code that only needs to be executed once (although this is rare), and then use that result for other purposes. Browser related variables are usually cached using this strategy.

0


source


You said:

I understand how it works, but I don't understand why it does it.

If you don't understand why it does it, then you really don't understand how it works.

In this example, you see a closure. And in my opinion this is a better introduction to closure than a lot of introductory examples.

Closing is a bit like a global variable. When using a global variable, the data access function goes outside its own scope. For example:

var x = 2;

function amplify (n) {
    return n * x; // here a function is using x which does not exist
                  // in the function own scope
}

      

Closures are similar except that they are hidden in the global scope. Whenever the inner variable accessor function constructs the outer function, it does so using the same mechanism as the global variables, except that the variable is not visible from the global scope. Now let's see a simple example similar to the one in your post:

var amplify;

function init () {
    var x = 2;
    amplify = function (n) { return n * x };
};
init();

console.log(amplify(5)); // prints 10
console.log(x); // syntax error

      

So, in this case, as in the code you posted, the variable x

behaves like a global variable, but is invisible in the global scope. Just like globals, it is displayed for all functions declared in the scope it is in. And because it is x

used by a function amplify()

, it is not freed or garbage collected when the function returns init()

. And that's all there is to it - this strange contradictory concept of private globals. Nothing more.

In this particular use case, the benefits are minimal. You save memory by not allocating the array every time you call the function, but that's about it. But the closure is much more powerful than that. For example, you can use the same variables with multiple functions, but still hide them from the global scope. For example:

var amplify;
var setFactor;
var getFactor;

function init () {
    var x = 2;

    amplify = function (n) { return n * x };
    setFactor = function (n) { x = n };
    getFactor = function () { return x };
};
init();

amplify(5); // returns 10
getFactor(); // returns 2
setFactor(4);
amplify(5); // returns 20
console.log(x); // still a syntax error

      

As you can see. Closure allows you to capture and exchange data between related functions, but hide that data from all other functions. In a sense, gates are almost functionally equivalent to objects. So my guide on when to use closures is when you need modulation but objects are overflowing. The most common use case for this is state capture for callbacks. But caching (memoization) as shown in the above code example is a good use case.

0


source







All Articles