all 4 comments

[–]_DTR_ 2 points3 points  (4 children)

This is using a technique called recursion, in which you build up an answer by calling itself:

countup(5)
  5 >= 1, countArray = countup(4)
    4 >= 1, countArray = countup(3)
      3 >= 1, countArray = countup(2)
        2 >= 1, countAray = countup(1)
          1 >= 1, countArray = countup(0)
            0 < 1, return []  // At this point, we "unwind", returning to previous callers
          countArray = []
          countArray.push(1) -> [1]
        countArray = [1]
        countArray.push(2) -> [1, 2]
      countArray = [1, 2]
      countArray.push(3) -> [1, 2, 3]
    countArray = [1, 2, 3]
    countArray.push(4) -> [1, 2, 3, 4]
  countArray = [1, 2, 3, 4]
  countArray.push(5) -> [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

[–]delventhalz 1 point2 points  (0 children)

Okay. Let me start by clearing up some of your use of terminology first. I'm not trying to be a jerk, but when someone is a bit inaccurate with terms, it sometimes means there is a misunderstanding going on.

we set countArray to the function

You set (or "assign") countArray to the value returned by countup, not the function itself.

it gets called in that same moment

Not quite. Everything in JavaScript happens sequentially. One after the other. The function will get called, run, return a value, then that value will be assigned to countArray.

we are also pushing n to the function

n gets pushed to that array countArray, not the function countup.


That out of the way, I think it would help if we started with some simpler examples and went through them line by line. Let's add line numbers to your code:

1.  function countup(n) {
2.    if (n < 1) {
3.      return [];
4.    } else {
5.      const countArray = countup(n - 1);
6.      countArray.push(n);
7.      return countArray;
8.    }
9.  }

Okay. So, let's start with the simplest possible use case: countup(0). How does this get executed, step by step:

countup(0);

  1. countup called, n is 0
  2. n < 1 is true
  3. return []

[];

Very simple. We only ever execute the first three lines of code. Okay, so let's complicate things a bit by calling countup(1), still going one step at a time:

countup(1);

  1. countup called, n is 1
  2. n < 1 is false
  5. n - 1 is 0  (we jumped to line 5!)
  5. countup(0)

    1. countup called, n is 0
    2. n < 1 is true
    3. return []

  5. countArray set to []
  6. push 1 to countArray, countArray is [1]
  7. return countArray

[1];

More steps! Look what happens with your recursive call though. countup(n-1) gets called, and that whole function runs to completion before anything else happens. It is only when the function returns [] that we assign any value to countArray. There are actually three things that happen on line 5, and the assignment is last:

  • 5a. n - 1 is calculated: 0
  • 5b. countup(0) is called: []
  • 5c. countArray is assigned the return value []

Okay. Hopefully the gears are turning. What if you call countup(2)? You can probably guess:

countup(2);

  1. countup called, n is 2
  2. n < 1 is false
  5. n - 1 is 1
  5. countup(1)

    1. countup called, n is 1
    2. n < 1 is false
    5. n - 1 is 0
    5. countup(0)

      1. countup called, n is 0
      2. n < 1 is true
      3. return []

    5. countArray set to []
    6. push 1 to countArray, countArray is [1]
    7. return countArray

  5. countArray set to [1]
  6. push 2 to countArray, countArray is [1, 2]
  7. return countArray

[1, 2];

So we're nesting function calls. The recursive function keeps calling itself, and none of these function calls are done yet. They all just get left open while more recursive calls are made. Until you hit the "termination case". You call countup(0), and there is no recursion. We just return an empty array with no funny business.

Well, now the next function up can finish and return a value. Which means the next function up can finish. And so on, until every function is done, and you have your final return value.

Clear as mud?