0

I was looking at some javascript code to create a calculator app. It's straightforward, but what confused me was when there's function that returns a function.

With the code below, I understand each button of the calculator needs an event listener to utilize "click", upon which the function is called depending on what button was clicked. The addValue(i) function adds the button value to the result field when the button is clicked. The function below has a function that returns a function. Without the function call, the result returns "789+456-123/0.=x"

Can some explain why the addValue(i) function needs to return a function for this function to work.

for (var i=0; i<buttons.length; i++) { 
    if(buttons[i]  === "="){
        buttons[i].addEventListener("click", calculate(i));
    } else {
        buttons[i].addEventListener("click", addValue(i));
    }
}

function addValue(i) {
    return function() {
        if(buttons[i].innerHTML === "÷") {
            result.innerHTML += "/";
        } else if (buttons[i] === "x") {
            result.innerHTML += "*";
        } else {
            result.innerHTML += buttons[i].innerHTML;
        }
    }
};
jamesvphan
  • 1,825
  • 6
  • 23
  • 30
  • 4
    Possible duplicate of [How do JavaScript closures work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Marty Jul 29 '16 at 01:44

1 Answers1

1

In this example, I'm assuming that the webpage is a calculator, similar to:

|---------------|
|               |
|---------------|
| 1 | 2 | 3 | * |
|---|---|---|---|
| 4 | 5 | 6 | / |
|---|---|---|---|
| 7 | 8 | 9 | = |
|---------------|

In this case, the button buttons[i] is one of the calculator buttons; | 1 |, | 5 |, etc. The actual number of the button is its index, so buttons[2] would be | 2 |, buttons[7] would be | 7 |, etc. And I'm guessing other values, like buttons[0] and buttons[10], are the operator buttons (| = |, | * |, | / |).

Now, when the user clicks on one of these buttons, the character on the button, which is the button's innerHtml, is added to the calculator's result (by adding it to the result's innerHTML), which is just a display of the operation. So if I clicked | 3 |, then | * |, then | 5 |, the calculator would look like

|---------------|
|           3*5 |
|---------------|
| 1 | 2 | 3 | * |
|---|---|---|---|
| 4 | 5 | 6 | / |
|---|---|---|---|
| 7 | 8 | 9 | = |
|---------------|

Here's where the addEventListener comes into play. When you call element.addEventListener("click", func), whenever element is clicked, func will be called. So to add the button to the result when it's clicked, you could do:

buttons[i].addEventListener("click", addButtonValueToResult);

where addButtonValueToResult is the function to add the button's value to the result.

The problem is, the different buttons have different values, so one function like this won't work for all of them. Therefore, addButtonValueToResult can't be a simple function; each button needs its own function. One such solution is to ditch the for loop and just do something like:

buttons[1].addEventListener("click", add1ToResult);
buttons[2].addEventListener("click", add2ToResult);
buttons[1].addEventListener("click", add3ToResult);
...

and then have functions like:

function add1ToResult() {
    result.innerHTML += "1"
}

But this takes a lot of unneeded work, because you need to add functions for each of the number buttons (| 1 | through | 9 |) as well as the operator buttons (| = |, | / |, and | * |), which just add their innerHtmls to the result. Moreover, the button's index is already assigned to a variable i and the entire button can be referenced with that index with buttons[i]. Couldn't you just have the program make a function automatically, for each button, that when given the button's index i, gets the button (through buttons[i]) and adds that button's value to the result?

Well that's exactly what this program does: addValue(i) doesn't just add the button's inner value itself; it returns another function that, also with a few test cases for special buttons, adds the button's inner value. Looking at this code:

function addValue(i) {
    return function() {
        if(buttons[i].innerHTML === "÷") {
            result.innerHTML += "/";
        } else if (buttons[i] === "x") {
            result.innerHTML += "*";
        } else {
            result.innerHTML += buttons[i].innerHTML;
        }
    }
};

Say you call addValue(3); this will return a function that will add the digit 3 to the result, in result. If you call addValue(9); this will return a function that will add the digit 9 to the result, in result. You can call the function addValue returns and actually add a digit to the result, through (addValue(digit)()). But addEventListener doesn't take an evaluated result; it takes a function, which it will later call when the button is clicked.

Doc
  • 281
  • 3
  • 5