[Comment removed by author]
To my recollection, SICP doesn’t really address the distinction between dynamic / lexical scope. There are the “what would Bob / Eva / So-and-so do?” questions when you are building the (first) evaluator however, and those are proxies for different views on scoping.
One thing to be aware of is that nested functions in JS can incur a penalty cost - they’re basically going to add to allocations/cleanup anytime the scope enclosing them is set up/torn down. Basically - you don’t want to use them in a function that’s going to be called very rapidly or that is incredibly runtime sensitive - in other cases it will basically be negligible.
It’s worth considering pushing such functions up to the most top level scope possible however. Partially for this performance reason but mostly because it will force you to consider ways to make the function more general and useful to other places in the code rather than the scope of a single function.
Cleaner/terser function syntax in newer versions of JS makes this technique especially effective too.
One thing that worries me slightly about nesting helper functions inside of other functions is that it can break up the flow for the reader. Generally when I read a function, I expect to see a list of statements that will be executed, creating code flows. Nesting a helper around these statements always trips me up a bit, because I have to skip over its declaration and find its usages to trace the code execution correctly.
That being said I think it’s fine for small one- or two-liner helpers, especially in languages that have a concise syntax for lambdas. And I definitely agree that polluting the top-level scope with lots of helpers can make your code feel really cluttered and messy. So there’s a balance(as with everything style-related).
It’s also problematic if you want to test the nested function. Most languages don’t provide (sane) ways of accessing them.