JS closure and how it behaves

I am reading through YDKJS scope and closure and here are something I wanna ask you guys. In the book it stated these:

function foo() {
	var a = 2;

	function bar() {
		console.log( a );
	}

	return bar;
}

var baz = foo();

baz(); // 2 -- Whoa, closure was just observed, man.

The function bar() has lexical scope access to the inner scope of foo(). But then, we take bar(), the function itself, and pass it as a value. In this case, we return the function object itself that bar references.

After we execute foo(), we assign the value it returned (our inner bar() function) to a variable called baz, and then we actually invoke baz(), which of course is invoking our inner function bar(), just by a different identifier reference.

bar() is executed, for sure. But in this case, it’s executed outside of its declared lexical scope.

After foo() executed, normally we would expect that the entirety of the inner scope of foo() would go away, because we know that the Engine employs a Garbage Collector that comes along and frees up memory once it’s no longer in use. Since it would appear that the contents of foo() are no longer in use, it would seem natural that they should be considered gone.

But the “magic” of closures does not let this happen. That inner scope is in fact still “in use”, and thus does not go away. Who’s using it? The function bar() itself.

By virtue of where it was declared, bar() has a lexical scope closure over that inner scope of foo(), which keeps that scope alive for bar() to reference at any later time.

bar() still has a reference to that scope, and that reference is called closure.

So, a few microseconds later, when the variable baz is invoked (invoking the inner function we initially labeled bar), it duly has access to author-time lexical scope, so it can access the variable a just as we’d expect.

The function is being invoked well outside of its author-time lexical scope. Closure lets the function continue to access the lexical scope it was defined in at author-time.

So here’s my understanding:

The text seems to suggest that foo is still alive and cannot be garbage collected yet because it’s still being referenced by bar.

Since it would appear that the contents of foo() are no longer in use, it would seem natural that they should be considered gone.
But the “magic” of closures does not let this happen. That inner scope is in fact still “in use”, and thus does not go away. Who’s using it? The function bar() itself.

Also from Memory management - JavaScript | MDN

An object is considered garbage collectable if there is zero reference pointing at this object.

I’m not entirely sure though. The English around this subject seems pretty cryptic.

1 Like

I think it would be more accurate to say that the inner scope of foo() is not garbage collected (doesn’t die as you put it) because bar() has closure over foo(). Instead that scope chain is preserved and it can be referenced when baz() is called despite foo() already returning.

1 Like

mdn has a good page on closure

a closure is a runtime concept - technically a closure is created every time a function definition is evaluated at runtime - see the spec where it explains the runtime semantics of function evaluation - it shows the final result is a closure - it also gives the exact definition of a closure

https://www.ecma-international.org/ecma-262/8.0/index.html#sec-function-definitions-runtime-semantics-evaluation

the other piece of the puzzle is js dynamic memory management that keeps references valid as long as there is a possibility of use in the program

a closure is created for bar when its definition is evaluated in a foo() call - a reference to the bar closure is returned and assigned to baz - so bar closure remains valid and usable when baz() is called

I think describing this as “magic” and setting up strawmen like “it would seem natural that [the contents of foo()] should be considered gone” only advance confusion and create an unnecessary aura of mystery around closures

1 Like

So for now I just understand it as:

Because of bar is nested in foo, therefore it has a closure over foo, and bar will be saved for later usage, and at the same time create a reference of what it produce.

Also yeah, sometimes the author did explain it in a confusing way.