(здесь разработчик V8.) Вы правы, локальные переменные в одной функции, которые закрывает другая функция, не могут быть легко сохранены в стеке. В частности, в V8 они хранятся в так называемом объекте «Контекст» в куче, о котором говорит @JonasWilms. Чтобы проиллюстрировать это на примере:
function outer() {
let a = 1; // Will be on the stack
let b = 2; // Will be in the context on the heap
return function inner() {
return b;
}
}
Вы также абсолютно правы, что вам не о чем беспокоиться :-) В частности, потому что "стек" и "куча" действительно являются внутренними деталями реализации; именно так получилось, что это наиболее распространенный способ достижения движками (для многих языков) требуемого поведения с достойной производительностью.
Говоря более концептуально, мы говорим об объемах и временах жизни переменных здесь: в примере a
выходит из области видимости (= становится недоступным), когда outer
завершает работу, тогда как b
- это по-прежнему достижимы через inner
, поэтому он длится до тех пор, пока inner
не может быть вызван снова. То, как двигатель выполняет это, является внутренней деталью и может измениться в любое время. Например, механизм может решить упростить свою реализацию, всегда помещая все переменные в кучу, избегая необходимости заранее анализировать, какие переменные будут закрыты, а какие нет. Все, что имеет значение, это наблюдаемое поведение, а не то, как оно достигается под капотом.