This reply is for anyone without any programming background at all. For beginners like myself learning through picking the code apart.
To your answer: for every return to the if-statement conditional, there is a “newly created function execution context” and another “multiply’” function which is placed on top of the call-stack.
I am currently editing this: for everyone > newbies, novices, and like me…trying to get it right!
The secret-sauce to fully understanding recursion, or, for that matter, any JS code, is to understand the “execution function context” which the JS Engine will perform on any, and all functions.
When I read code, I read it like the JS engine. Every time there is a “function invocation” , the engine will open a new execution context for that specific function. It works the functions code in it’s own little isolated box. However, within that “isolated box” if there is another function ( the secret sauce here), the engine will keep the current function on the “call stack” and pop out of the current function and into a brand new execution context. The new working function (and new execution context) gets it’s own memory store for it’s values; but these values can be accessed (depending on context) through the prototypical inheritance ( the chain).
The code:
var array = [1, 2, 3, 4];
//declare and instantiated array
function multiply(arr, n) {
//the function definition, declaration, statement, expression
if (n <= 0) {
// if statement (Boolean conditional)
return arr[0];
// if True, return this statement
} else {
// the else clause gets fired if conditional is False
return multiply(arr, n - 1) * arr[n];
// nested recursive function (closure)
}
// ends the if code block
}
// ends the outer function code block
console.log(multiply(array, 3)); // 24 this line invokes the whole thing!
The function call:
Pretending I am the JS engine: ( third person? )
Since JavaScript is synchronous, “I read code line-by-line” from top-to-bottom in a single thread and to keep it simple. I then read the entire code and put things where they need to be in my memory, and then, as things have it; I get called with a “function call” at the bottom of the code which makes me execute the code. Sometimes, I even get called by other functions, or even by variables that I am assigned to. I am First Class!
First things first.
-
I read and store the [Array] name ’ arr ' with its two ‘parameters’, and allocate memory.
-
Next, I also read the function’s definition body into memory.
-
I continue reading down the code until I come upon: console.log( multiply ( array, 3 ))
An Aside: The JS built-in log() function is invoked (remember > parentheses) and says, " I need to look inside the parentheses to see what I am to invoke/run/execute". It looks inside and reads the ‘function’ name multiply with associated parentheses ( ).
1. The parentheses tell me to ‘execute’ this named-function, ‘multiply’.
2. I find the function’s name in memory, multiply, with its two parameters, arr and n. The function name also references it’s function definition located in memory.
c. I pass in the two arguments from the function call; the ‘array’ , which has been instantiated with an array containing a sequence of integers (primitive number object’s in JS), from 1 to 4 .
d. I also pass in the argument, number ‘3’ into the ‘n’ parameter in the function definition (statement).
arr is [1,2,3,4],
and n is 3.
4. I am now in the function body: everything between the { } curly brackets at:
if ( n <= 0 )
n = 3
if (3 <= 0 )
This evaluates to a falsy and so the engine will run what’s in the “else-clause”.
If it were truthy, the JS engine would then execute the “first statement” in the if-conditional, the return arr[ 0 ]
This array has (4) elements each with a number value. These values are consumed by their ‘index’ values.
console.log( arr [ 0 ] ) // 1
So, using the array: arr = [ 1, 2 ,3 ,4 ]
index 0 = 1
index 1 = 2
index 2 = 3
index 3 = 4
Take Note : Inside the if-conditional is a 'nested' function with the same name as the outer function's name. They have the same name, but when executed they will have their own 'execution context environment' which holds their own returned values upon code completion of that function.
Working with the nested function: Inner function of the if-conditional
return multiply (arr, n - 1) * arr[n];
- So, once again, when this multiply function is invoked, the JS engine creates a new execution context.
Take Note: For every time the multiply function gets called, as it is looped through the if-conditional ( 4 times : because 4 array elements) , the JS engine creates a separate execution context for each function call.
arr is : arr = [1,2,3,4]
// just as a reference
n is 3
First function call of multiply: gets placed on the call-stack:
(arr, 3 - 1) * arr[ 3 ]
// arr is array, n=
3, so 3-1 is '
2' * arr[3] is index 3: value of '
4
n = 2
arr [ 3 ] is value 4, from index 3 of arr.
Second function call of multiply: it gets placed on the call-stack on top of previous multiply:
(arr, 2 - 1) * arr[ 2 ]
// n=
2, so 2-1 is
1 * arr[2] is index 2: value of
3
n = 1
arr [ 2 ] is value 3 , from index 2 of arr.
Third function call of multiply: it gets placed on the call-stack on top of previous multiply:
(arr, 1 - 1) * arr[ 3 ]
// n=
1, so 1-1 is
0 * arr[ 1 ] is index 1: value of '2'.
n = 0
// In if > if ( n <= 0 ) still truthy, so execute the else clause.
arr[ 1] is value 2, from index of arr.
Fourth function call of multiply: it gets placed on the call-stack on top of previous multiply:
(arr , 0 - 1) * arr[ 0 ]
// n=
0, so 0 - 1 is '0' * arr[0] is index 0: value of '1'.
Finally, the if statement evaluates to a truthy condition: n=0, 0 is = 0!
Remember: the JS engine is still in the INNER or NESTED multiply function
This is otherwise known as a “closure” in JS.
return arr[0];
On the call-stack we have:
top of call stack
multiply 4th func: n=0, val=1
multiply 3rd func: n=1, val= 2
multiply 2nd func: n= 2, val= 3
multiply 1st func: n=3, val= 4
bottom of call stack
so the engine returns the ‘value’ of n[0] each successive time the function’s are asked to return their return values; functions are 'pooped from the stack in this order: LIFO (last-in, first-out);
4th ‘multiply’ function is popped off the stack, returns a value of ‘1’.
3rd ‘multiply’ function is popped off the stack, returns a value of ‘2’.
2nd ‘multiply’ function is popped off the stack, returns a value of ‘3’.
1st ‘multiply’ function is popped off the stack, returns a value of ‘4’.
Each time the JS engine recursively is multiplying the returned function-call values together.
4th: val=1, 3rd: val=2, 2nd: val=3, 1st: val=4. 1 x 2 x 3 x 4
Do it recursively multiples 1 x 2 x 3 x 4.
24
The hours it took me grasp this.
I am a self-taught programmer and 63 years old. Yes, old F…T.
I hope this can help others.
I am currently studying Python, TensorFlow, OpenCV, and ML.
Please inform me of any errors right away so that I can correct them.