2

When creating a closure (in Javascript or C#), are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?

Example C# code:

private void Main() {
    var referenced = 1;
    var notReferenced = 2;  // Will this be enclosed?
    new int[1].Select(x => referenced);
}

Example Javascript code:

    var referenced = 1;
    var notReferenced = 2;  // Will this be enclosed?
    var func = function () {
        alert(referenced);
    }

(Question came to me when reading about memory leaks in IE by creating circular references with Javascript closures. http://jibbering.com/faq/notes/closures/#clMem)
Note: with the word "enclosed", I mean what MSDN would call "captured". (http://msdn.microsoft.com/en-us/library/bb397687.aspx)

Gabe
  • 84,912
  • 12
  • 139
  • 238
Protector one
  • 6,926
  • 5
  • 62
  • 86
  • You'd have to define what you mean by "enclosed" in the context of C#, where it's not a term I'm aware of. – Jon Skeet Jul 21 '11 at 09:33
  • At least for JavaScript, this can very well depend on the implementation. – Felix Kling Jul 21 '11 at 09:34
  • 1
    What do you mean? The compiler only actually generates "closure" code to reference the variables that need to be accessed. But you can consider that an optimization. Conceptually, you might consider everything that is visible is in the function's closure, whether or not it is referenced. – jalf Jul 21 '11 at 09:35
  • 1
    For more up-to-date answers for JavaScript see http://stackoverflow.com/questions/5368048/how-are-closures-and-scopes-represented-at-run-time-in-javascript – Theodore Norvell Nov 06 '14 at 12:34

5 Answers5

18

You have two questions here. In the future you might consider posting two separate questions when you have two questions.

When creating a closure in Javascript, are all variables in the scope at the time of the closure's creation "enclosed" in it?

You do not state which of many versions of "JavaScript" you are talking about. I'm going to assume you are talking about a correct implementation of the ECMAScript 3 language. If you are talking about some other version of "JavaScript", say which one you're talking about.

ECMAScript 5 has updated rules on how lexical environments and "eval" work. I have not been a member of Technical Committee 39 since 2001, and I have not been keeping up-to-date with recent changes to the ECMAScript 5 spec; if you want an answer in the context of recent ECMAScript 5 rules, find an expert on that specification; I'm not it.

The answer to your question in the context of ECMAScript 3 is yes. The ECMAScript 3 specification is very clear on this point, but you don't need to look at the spec to know that this must be the case:

function f()
{
 var referenced = 1;
 var notReferenced = 2;
 var func = function () 
 {
   alert(referenced);
   alert(eval("notReferenced"));
 }
 return func;
}
f()();

How can "eval" work correctly if "notReferenced" is not captured?

This is all explained in the ECMAScript specification, but I can briefly sum it up here.

Every variable is associated with a "variable object", which is the object that has properties whose names are the names of the variables. The variable object for the function f is identical to the activation object of f -- that is, the object that is magically created every time f is invoked. The variable object has three properties: "referenced", "notReferenced" and "func".

There is a variable object called the "global object" which represents the code outside of any function. It has a property "f".

Every execution context has a scope chain, which is the list of objects whose properties are searched when trying to evaluate an identifier.

Every function object has a scope chain associated with it, which is a copy of the scope chain that was in effect when the function was created.

The scope chain associated with "f" is the global object.

When execution enters "f", the current scope chain of the execution context has the activation object of "f" pushed onto it.

When "f" creates the function object assigned to "func", its associated scope chain is a copy of the current scope chain of the execution context -- that is, the scope chain that contains the activation of "f", and the global object.

OK, so now all the objects are set up correctly. f returns func, which is then invoked. That creates an activation object for that function. The scope chain of the execution context is fetched from the function object -- remember, it is the activation object of "f" plus the global object -- and onto that copy of the scope chain we push the current activation object. Since the anonymous function we are now executing has neither arguments nor locals, this is essentially a no-op.

We then attempt to evaluate "alert"; we look up the scope chain. The current activation and the "f" activation do not have anything called "alert", so we ask the global object, and it says yes, alert is a function. We then evaluate "referenced". It's not on the current activation object, but it is on f's activation object, so we fetch its value and pass it to alert.

Same thing on the next line. The global object tells us that there are methods "alert" and "eval". But of course "eval" is special. Eval gets a copy of the current scope chain as well as the string argument. Eval parses the string as a program and then executes the program using the current scope chain. That program looks up "notReferenced" and finds it because it is on the current scope chain.

If you have more questions about this area then I encourage you to read chapters 10 and 13 of the ECMAScript 3 specification until you thoroughly understand them.

Let's take a deeper look at your question:

When creating a closure in Javascript, are all variables in the scope at the time of the closure's creation "enclosed" in it?

To definitively answer that question you need to tell us precisely what you mean by "the closure" -- the ECMAScript 3 specification nowhere defines that term. By "the closure" do you mean the function object or the scope chain captured by the function object?

Remember, all these objects are mutable -- the scope chain itself is not mutable, but every object in the chain is mutable! Whether or not there was a variable "at the time" of the creation of either the scope chain or the function object is actually a bit irrelevant; variables come, variables go.

For example, variables that have not yet been created at the time of the function object's creation can be captured!

function f()
{
 var func = function () 
 {
   alert(newlyCreated);
 }
 eval("var newlyCreated = 123;");
 return func;
}
f()(); 

Clearly "newlyCreated" is not a variable at the time the function object is created because it is created after the function object. But when the function object is invoked, there it is, on f's activation/variable object.

Similarly, a variable that exists at the time a nested function object is created can be deleted by the time the function object is executed.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I thought eval gets a newly created frame when it evaluates and evaluating `"var newlyCreated = 123;"` would define it in that frame (rather than `f`'s frame). Guess I was wrong. – configurator Jul 21 '11 at 20:10
  • @configurator: I refer you to section 10.2.2 of the E3 spec:, which states "*Variable instantiation is performed using the calling context's variable object...*" – Eric Lippert Jul 21 '11 at 20:14
  • "How can "eval" work correctly if "notReferenced" is not captured?" Chrome actually parses your function and only includes the entire scope chain if `eval` is mentioned. So it optimises what it closes based on whether "it can get away with it". So it's still implementation specific. – Raynos Jul 21 '11 at 23:07
  • 2
    @Raynos: And how do you know at parse time if 'eval' is mentioned? You can't know that at *parse* time; you can only know it at *run* time. In E3 it is perfectly legal to say at the global scope `var whatever="hello"; window[whatever] = eval;` and then in a complete different function say `hello("code");` and hey, `hello` is a synonym for `eval`. Maybe in E5 you're not allowed to do crazy stuff with eval; I haven't read the spec. But in E3 you are most certainly allowed to do so. – Eric Lippert Jul 22 '11 at 03:57
  • @EricLippert your underestimating how good v8 is as a parser ;). It can track all of that – Raynos Jul 22 '11 at 07:59
  • I wasn't really aware I was asking two questions; I apologize. I simply assumed "closure" was a well-defined concept and as such, would behave identically in the different languages that incorporate it. – Protector one Jul 22 '11 at 11:27
  • @Raynos: doing that analysis *accurately* requires solving the Halting Problem. I imagine that there are good heuristics in place; we designed such heuristics while working on JScript .NET a decade ago. But the E3 spec has *many* features in it that require an optimizing compiler to either violate the specification or solve the Halting Problem to generate fast code *without runtime analysis*. A better approach is to do a first-cut analysis at parse time and then be willing to fall back to a slow path at runtime if your analysis proves to be wrong. – Eric Lippert Jul 22 '11 at 14:01
  • 1
    @protector one: Nope. Different languages are different. – Eric Lippert Jul 22 '11 at 14:03
  • @EricLippert I seem to have forgotten who you are. Your probably correct on this. Although I do know ES5 (especially strict mode) has clamped down on eval usage. – Raynos Jul 22 '11 at 14:04
  • @Raynos: I gather that it has gotten more strict, but I do not know the details. The impossibility of knowing for sure when you were calling a function like eval which precluded optimizations was identified as a major shortcoming of ECMAScript back in the late 1990s. We added a strict-and-fast mode to JScript .NET which disallowed things like renaming eval, modifying built-in methods, and so on, to enable compile-time optimizations. There's nothing stopping "v8" from doing all those things and more a mere ten or twelve years later. – Eric Lippert Jul 22 '11 at 14:16
  • 1
    @Eric Lippert: This fails in Chrome with a ReferenceError (x is not defined), supporting both what @Raynos says, and the impossibility of it. `(function(a,b,c,d){var x=1;return window[a+b+c+d]('x');})('e','v','a','l');` – configurator Jul 25 '11 at 13:08
8

You have two questions here. In the future you might consider posting two separate questions when you have two questions.

When creating a closure in C#, are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?

It depends on whether you're asking for a de jure or a de facto answer.

First off, let's clarify your question. We define an "outer variable" as a local variable, value parameter (that is, not ref or out), "params" parameter, or "this" of an instance method that occurs inside a method but outside of any lambda or anonymous method expression that is inside that method. (Yes, "this" is classifed as an "outer variable" even though it is not classified as a "variable". That's unfortunate, but I've learned to live with it.)

An outer variable that is used inside of a lambda or anonymous method expression is called a "captured outer variable".

So now let's reformulate the question:

When creating the delegate at runtime that corresponds to a lambda or anonymous method, are the lifetimes of all the outer variables that are in scope at the time of the delegate's creation extended to match (or exceed) the lifetime of the delegate? Or are only the lifetimes of captured outer variables extended?

De facto, only the lifetimes of captured outer variables are extended. De jure, the spec requires the lifetimes of captured outer variables to be extended, but does not forbid the lifetimes of uncaptured outer variables to be extended.

Read sections 5.1.7, 6.5.1 and 7.15.5 of the C# 4 specification for all the details.

As Jon notes, we do not do a particularly good job of ensuring that lifetimes of captured outer variables are minimized. We hope to someday do better.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
5

I can only answer on the C# side.

If the variable isn't actually captured by the closure, it will stay as a normal local variable in the method. It will only be hoisted to be an instance variable in a synthetic class if it's captured. (I'm assuming that's what you're talking about when you talk about variables being "enclosed".)

Note, however, that if two lambda expressions each capture different variables of the same scope, then in the current implementation both lambdas will use the same synthetic class:

private Action Foo() {
    var expensive = ...;
    var cheap = ...;
    Action temp = () => Console.WriteLine(expensive);
    return () => Console.WriteLine(cheap);
}

Here the returned action would still keep the "expensive" reference alive. All of this is an implementation detail of the Microsoft C# 4 compiler, however, and may change in the future.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • "enclosed" means the closure closes over the variable. Thus the variable is and stays inside the scope of that particular closure. From the sounds of it your synthetic class and capturing matches that. – Raynos Jul 21 '11 at 09:37
  • @Raynos: That description doesn't really work *in C# terms* though. Each language can use terminology in subtly different ways - which is why this is a slightly odd question, trying to cover two languages. What is the *observable* change in the case of an un-referenced variable being enclosed? – Jon Skeet Jul 21 '11 at 09:39
  • The only "observable change" change I can think of in terms of un-referenced variables being enclosed is that an enclosed un-referenced variable cannot be cleaned up by the GC until the closure gets cleaned up. Implementing this is basically implementing a weaker GC. I don't see any value in doing this, but again it's implementation specific. – Raynos Jul 21 '11 at 09:47
  • @Raynos: Right - okay, in that case I think we *are* talking about the same thing, and my answer is probably what the OP is looking for in terms of C#. – Jon Skeet Jul 21 '11 at 09:52
  • The issue Jon mentions is discussed in slightly more detail in [this question](http://stackoverflow.com/questions/3885106/discrete-anonymous-methods-sharing-a-class), where I asked about it explicitly. – Brian Jul 21 '11 at 19:37
3

I only can answer about C#:

No, it will not be enclosed, because it is not needed.

I interpret "will it be enclosed?" in this context like the following:

Will it be captured inside the closure that is generated for the lambda expression, i.e. will there be a field in the automatically generated class that contains the value of this variable?

Disclaimer:
This is an implementation detail. My answer is true for the current implementation of the C# compiler by MS for your specific case. It could be different with the Mono compiler or a newer version of the MS compiler. Or - as Jon's answer shows - it could be even different for a more complex example.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • If it's an implementation detail can you reference related information from the C# ECMA spec showing the spec doesn't care either way. – Raynos Jul 21 '11 at 09:42
  • @Raynos: No, because there is no ECMA spec for C# 3.0 or C# 4.0, see here: http://en.wikipedia.org/wiki/C_Sharp_(programming_language)#Versions and the ECMA spec for C# 2.0 naturally doesn't mention anything about lambdas or closures. – Daniel Hilgarth Jul 21 '11 at 10:04
  • thanks for pointing that out, I actually expected the specification to be kept upto date. – Raynos Jul 21 '11 at 10:26
  • 1
    You can have a look at the MS spec linked in the wikipedia article, chapter 7.15.5 – Daniel Hilgarth Jul 21 '11 at 10:42
  • 1
    @Raynos: You could read section 14.5.15 of the ECMA C# 2 spec; it goes into considerable detail about all the different things that are implementation-defined for anonymous methods. All those things are still implementation-defined for lambdas in C# 3 and 4. In particular see the first sentence of section 14.5.15.5. – Eric Lippert Jul 21 '11 at 21:56
1

When creating a closure (in Javascript or C#), are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?

I can only answer about JavaScript.

This is implementation specific. Some engines enclose all variables. Some engines optimise and only enclose referenced variables.

I know Chrome optimises and only encloses the variables it cares about.

Further reading:

It also seems the question originated from a concern about the old IE memory leak problem. This isn't an issue in modern browsers.

A good read on memory leaks that still exist in IE8 by making circular references between EcmaScript and Host objects :

IE8 memory leak

To clarify about the IE memory leak issues with closures, it's mainly about the circular references between Host objects (DOM) and JavaScript. So this issue does not exist unless you use the DOM (el.attachEvent or something similar)

Community
  • 1
  • 1
Raynos
  • 166,823
  • 56
  • 351
  • 396