Closure in a Nutshell
Closures are a Software phenomenon which exist in several languages, in which methods declared inside other methods (nested methods), capture variables declared inside the outer methods. This behavior makes captured variables available even after the outer method's scope has vanished.The following pseudo-code demonstrates the simplest sample:
Main() //* Program starts from here { Closures(); } AgeCalculator() { int myAge = 30; return() => { //* Returns the correct answer although AgeCalculator method Scope should have ordinarily disappear return myAge++; }; } Closures() { Func ageCalculator = AgeCalculator(); //* At this point AgeCalculator scopeid cleared, but the captured values keeps to live Log(ageCalculator()); //* Result: 30 Log(ageCalculator()); //* Result: 31 }JavaScript and C# are two languages that support Closures. In this Post I will explain the different internal implementations of this phenomenon in these two languages.
NOTE: Although tested only in C#, I believe the same behavior is valid for all CLR compliant languages.
C# Closures
Simple C# Closures
I fixed the previous pseudo-code to a Compilable C# representation:Func<int> AgeCalculator() { int myAge = 30; return() => myAge++; } void Closure() { Func<int> ageCalculator = AgeCalculator(); Console.WriteLine(ageCalculator()); Console.WriteLine(ageCalculator()); }Internally, Closures are implemented in C# by encapsulating the captured variables into fields of a new generated (at compile time) class. This class pointer is used instead of the 'simple'
myAge
variable:
NOTE: The following is a generated code, I just beautify it a bit to make it more readable
private Func<int> AgeCalculator() { // * New generated class that encapsulates myAge variable as a field and AgeCalculator as a regular method that just increases myAge variable __Plain__.__DisplayClass1 cDisplayClass1 = new __Plain__.__DisplayClass1(); cDisplayClass1.myAge = 30; return new Func<int>((object) cDisplayClass1, __methodptr(AgeCalculator)); } [CompilerGenerated] private sealed class__DisplayClass1 { public int myAge; // * As a field public __DisplayClass1() { base.ctor(); } public int AgeCalculator() //* Simple method { return this.myAge++; } }We can see that a new
cDispalyClass1
object was created and within it the myAge
variable and a new AgeCalculator
method. This object's pointer continues to live even after privateFunc
AgeCalculator() scope is cleared, just because it is treated like a normal pointer.
Because of this .NET compiler implementation, captured types are evaluated always at runtime and NOT at capture time, because all closures point to the same object and its variable. Even if fields are altered, the change affects all of them:
private void ClosureSample5() { Action[] printerList = newAction[3]; string i = "A"; printerList[0] = () => Console.Write(i); i = "B"; printerList[1] = () => Console.Write(i); i = "C"; printerList[2] = () => Console.Write(i); i = "D"; foreach (Action printer in printerList) printer(); //* Result: DDD instead of ABC }As explained above, the compiler generates a class containing the '
i
' variable and 3 separate 'Closure5
' methods. The class is instantiated and the object is used by all closures:
NOTE: Generated & beautified
private void Closure5() { __Plain__.__DisplayClass3 cDisplayClass3 = new __Plain__.__DisplayClass3(); Action[] actionArray = new Action[3]; cDisplayClass3.i = "A"; actionArray[0] = new Action((object) cDisplayClass3, __methodptr(Closure5_0)); cDisplayClass3.i = "B"; actionArray[1] = new Action((object) cDisplayClass3, __methodptr(Closure5_1)); cDisplayClass3.i = "C"; actionArray[2] = new Action((object) cDisplayClass3, __methodptr(Closure5_2)); cDisplayClass3.i = "D"; foreach (Action action in actionArray) action(); } private sealed class __DisplayClass3 { publicstring i; public__DisplayClass3() { base.\u002Ector(); } public void Closure5__0() { Console.Write(this.i); } public void Closure5__1() { Console.Write(this.i); } public void Closure5__2() { Console.Write(this.i); } }This implementation is self explanatory. Also, in this example a new
cDisplayClass3
object has been auto generated and used like a normal pointer.
In Loops
Another place where Closure can effect your code is nested methods declared inside loops. Let's see an example:private void Closure2() { Action[] actions = newAction[5]; for (int i = 0; i < 5; i++) { actions[i] = () => Console.Write(i); } foreach (Action action in actions) action(); //* Result 55555 }We could have thought that the result would be
01234
, but actually the final result is 55555
because the compiler "hoists" the iterator variable 'i
' outside the 'for' loop inside cDisplayClass1
, like all other closure samples above.By "hoisting" the variable outside of the loop, the
cDisplayClass1
is shared by all lambda expressions. When 'i
' value is changed, it's changed for all of them.If the the compiler would have initialized
cDisplayClass1
inside a 'for' loop, each Action
would have got it's private instance.
NOTE: Generated & beautified
private void Closure2() { Action[] actionArray = newAction[5]; __Plain__.__DisplayClass1 cDisplayClass1 = new__Plain__.__DisplayClass1(); for(cDisplayClass1.i = 0; cDisplayClass1.i < 5; ++cDisplayClass1.i) { actionArray[cDisplayClass1.i] = new Action((object) cDisplayClass1, __methodptr(Closure2__0)); } foreach(Action action in actionArray) action(); }This strange behavior inside loops is well documented throughout the Web, and the standard suggested fix is to declare another variable inside the curly brackets, filling it with the value of the iteration variable '
i
'.
private void Closure2() { Action[] actions = newAction[5]; for (int i = 0; i < 5; i++) { int internalInt = i; actions[internalInt] = () => Console.Write(internalInt); } foreach (Action action in actions) action(); //* Result: 01234 }The reason why this fixes the behavior is that when the compiler encounters this code, it initializes the
cDisplayClass1
object inside the curly brackets. By doing so every Action
gets it's own instance.
NOTE: Generated & beautified
private void Closure2() { Action[] actionArray = newAction[5]; for(int index = 0; index < 5; ++index) { __Plain__.__DisplayClass1 cDisplayClass1 = new__Plain__.__DisplayClass1(); cDisplayClass1.internalInt = index; actionArray[cDisplayClass1.internalInt] = newAction((object) cDisplayClass1, __methodptr(\Closure2__0)); } foreach(Action actioninactionArray) action(); }
JavaScript Closures
Simple JS Closures
The first thing I would like to highlight is that in JS, closures are not just a phenomenon, but actually a strong feature of the language (Just be careful how you use closures in JS or you may come across memory issues).The internal implementation of a closure in JS is totally different then C#. I won't explain JS closure top to bottom in this article, the web is full of documentation about this subject, like Dmitry Soshnikov's great article about ECMAScript Closures.
To put it shortly; any time we access a method in JS, a special context object is created. This object contains the variables to be searched in the scope chain search process.
var x = 10; (function parentFunc () { console.log(x); }()); //* Result: 10The code above will print
10
, because when parentFunc
method is executed, a special context object containing it's parent's variables is created and is pointed by parentFunc
method scope.When the interpreter starts searching for '
x
' value it searches inside parentFunc
scope. If it can not find it, it's parent scope gets evaluated. In this case the parent scope is the global scope (window
in browsers implementation, global
in nodeJS).
Closures in JS are implemented the same way. This is not an interpreter trick, but a language feature. Inner functions actually capture the entire parent scope and add it to the chain:
var x = 10; var funcResult = (function parentFunc () { var x = 20; var y =30; var sonFunc = function () { console.log(x + y); } return sonFunc; })(); funcResult();//* Result 50When
sonFunc
is returned, the parentFunc
scope with x
(20) and y
(30) variables is captured and added to the search chain.When
funcResult
is called, and console.log(x + y)
is encountered, the interpreter searches the variables 'x
' and 'y
' inside sonFunc
. Once it concludes it can't find them, it searches them inside it's parent scope, (as I've explained, parentFunc
was added to the chain).
Note that if we comment-out
var x = 20
code line, the result will be 40. Because the interpreter won't find the 'x
' variable inside parentFunc
, it will search in parentFunc
's parent scope where 'x
' value is 10.
In Loops
The same is true when talking about loops in JS. As in C#, when an iterator variable is used within the inner method, it's value will be the value at runtime and not the value at capture time:var myResultFuncList = function () { var resultFuncList = []; for (var i = 0; i < 5; i++) { resultFuncList[i] = function () { console.log(i); } } return resultFuncList; }; var list = myResultFuncList(); for (func in list) { list[func](); //* Result: 55555 }The result of this method is not
01234
as expected, but 55555
instead. Because all the created functions inside resutFuncList
have added their shared parent context in the search chain, when the 'i
' value changes, it affects the entire function, as all of them just point to the same parent context object in which 'i
' resides
...But Different
There are two big difference in JS, that prevents us using the same trick we used in C# in order to change this behavior:- In JS, the closure implementation is different than in C#, will be explained later.
- In C# every code inside curly brackets gets it's own scope, meaning that variables declared inside curly brackets can be accessed only within the curly brackets.
In JS curly brackets doesn't create a new scope, the variables are shared for the entire function.
So even if we declare a variable inside the 'for' loop JS interpreter will "hoist" the variable to the beginning of the function and it will be treated like any other "function variable". [Adjusted Thanks to Adar's Comment]
var myResultFuncList = function () { //* 'Hoisted' here: var niceTry; (undefined) var resultFuncList = []; for (var i = 0; i < 5; i++) { var niceTry = i; //* Hoisted to the top of the function resultFuncList[i] = (function () { console.log(niceTry); }); } return resultFuncList; } var list = myResultFuncList(); for (func in list) { list[func](); //* Result: 55555 }In JS, the closure implementation is different than in C#. The result remains
55555
even when a new variable is used to hold the 'i
' value.In the example above,
niceTry
variable is treated exactly like the 'i
' variable. Its just another variable captured inside the parent context object.The way to change this behavior in JS, is by creating new scopes for every single returned method:
var myResultFuncList = function () { var resultFuncList = []; for (var i = 0; i < 5; i++) { resultFuncList[i] = (function (iterator) { return function () { console.log(iterator); } })(i); } return resultFuncList; } var list = myResultFuncList(); for (func in list) { list[func](); }Now the result is
01234
. Because every returned function was wrapped by a parent function, these functions become the parents scope in the chain search process, and each of them capture a different value of the iterator variable.
Just remember, if you see nested methods make sure you handle Closures correctly.
Comments
Just one small comment. The reason why local variables are treated differently in JS closures is not because of the closure implementation, but rather because of variable definitoon rules. In JS, all variable definitions are "hoisted" up to the finction level. This means that defining the variable inside the loop has no meaning. It's exactly the same as declaring it at the top of the function.
The only way to create an inner scope is to wrap the relevant code in another function.
---
Adar Wesley
The reason is a combination of the two:
1 - The way closure is implemented, via the scope chain
2 - Like you mentioned, variables "hoisting"
I will adjust the Post and add the second point as well.
Thx
Your closure explanation Helped me to solve issue with creating multiply timers in a loop
Nachum
Thank you for sharing.
Web Designing Training Institute in Chennai | web design training class in chennai | Web Designing and Development Course in Chennai | Web Designer Training Course in Chennai
Digital Marketing Course In Kolkata
Web Design Course In Kolkata