Right, this is the magic of recursive functions. So, it is going through that array and dropping elements that do not meet the test function. If it encounters an element that meets the test function, then it returns the remaining array.
In the example you have, none of the elements meet the test function so they all get eliminated. What would you expect to happen if you passed it
[1, 2, 3, 4, 127]? What would you expect if you sent
[1, 2, 3, 4, 127, 1, 2]? Test it out.
Why? It’s hard to explain without understanding how recursion works on a deeper level. I would recommend to check out some videos to understand how recursion and the call stack work. But basically … the first call gets made and it encounters:
return i < arr.length && !func(arr[i])
? dropElements(arr.slice(i + 1), func, i)
If the condition is met (which it is for the 4 elements, it wants to return but can’t until it evaluates the function call. So, this function call gets interrupted and a new one gets started. And then a new one… In the end, you have 4 incomplete function calls on the call stack. Finally we reach a case where the array is empty (all the elements have been dropped) or we encounter an element that passes the test. At that point, we return arr. Now that that returns, the previous function call can finish and it returns, allowing the previous call before that to finish and return to the previous call … That is what happens, all those function calls on the call stack unravel until you get to the first function call and then that just returns back to where we originally called the function.
Yes, it’s weird. It takes a while to wrap your head around. It takes time get it. And really, most coders don’t need recursion that much - in 2.5 years as a developer, I’ve only used it once. (But, there may be some jobs that deal with a lot of data structures that need recursion.) It’s still good to know - it’s good for your brain and it can come up on interviews - but also don’t freak out about it. Just work at it and it will gradually make sense.