Just JavaScript - The Function Object |
Written by Ian Elliot | ||||
Thursday, 01 May 2014 | ||||
Page 2 of 3
ScopeNow we come to the much talked about idea of scope and how JavaScript implements it. Scope refers to what variables you can access at any point in code. Before we go on it is worth considering where the variables that are declared the function body live. They are clearly not properties of the Function object. In fact they don't even exist except when the function body is being evaluated. That is all of the variables in the function body are created when the function body is evaluated and destroyed again when it finishes. What happens is that when a function body is evaluated an Execution Context EC is created which stores all of the variables that are accessible from the code. You can think of the EC as being an object with properties that are the variables that are in scope - although you can't directly access the EC and its is an implementation detail. If you remember from chapter 1 that variables you create outside of a function are in fact properties of the global object you can see that the global object plays the role of an EC object for code that lives outside of a function. You can also think of the code that you write outside of a function as being the function body of the global object - but this is perhaps an abstraction too far. That is when the function body is evaluated the variables that are in scope form the execution context. In the case of JavaScript the scope rules are very simple but only if you realize that you are working with Function objects. In JavaScript variables said to have function scope. This means that when a function body is evaluated the variables that are created are only accessible within that function. That is all variables declared within a function are essentially "local" variables and override any variables already declared elsewhere. Things are a tiny bit more complicated than this because JavaScript actually allows nested function scope in that any function can make use of variables defined in any function that contains it. We need to consider what "contain" means. In JavaScript objects can contain other objects as properties. In the same way a Function object can contain other objects as properties but in addition a Function object can create additional Function objects when its function body is evaluated. For example
Notice that we have a new Function myInnerF created when myFunction is evaluated. In this case myInnerF is "nested" within or contained within myFunction. This doesn't mean that myInnerF is somehow a property of the myFunction Function object. What it means is that myInnerF is created when myFunction is evaluated. That is the Function object that myInnerF references is created whenever the Function object myFunction is evaluated. The "nested" scope means that myInnerF can not only access its own local variables, it can also access any variables that are local to the containing Function object i.e. myFunction. As you can see in this example it makes use of ans which is local to myFunction. The nested idea applies when multiple functions are nested inside each other. Each inner function has access to its own local variables and the variables of all of the functions that contain it. So in this case both myFunction and myInnerF also have access to the variables defined within the Global object. In this sense all Function objects are nested within the Global object. This is a very subtle idea. The reason is when you look at the function body of a Function object there is a tendency to think that it is being executed as you read it. This isn't the case. If you look at our example first the outer Function object is created and its function body is stored. When you execute the outer function body at some later time the inner Function object is created but its code isn't evaluated again until some time later. That is when you run the code:
a single Function object is created containing the function body:
Later when you evaluate that function body:
a second Function object is created which stores the function body:
The second function body is never evaluated in this example. LifetimesThe fact that a function is actually a Function object makes it possible for the function to exist in its own right independent irrespective of how and when its function body is evaluated. If you are going to understand some of the more subtle ways that JavaScript works you have to be 100% clear about the lifetime of a function object. When a Function object is created as part of some code the Function object is created and it exists within the whole program just like any other object. JavaScript provides automatic garbage collection which removes any object that has no references to it. So an object, any object including a Function object exists as long as there is at least one reference to it. Now consider the implication for the nested function:
The inner Function object referenced by myInnerF doesn't exist until the outer function is evaluated. That is when you write
the inner Function object is created when the system reaches the line var myInner= etc. This Function object exists from that point on till the end of the outer function i.e. until after the return ans; statement. At this point the variable myInner goes out of scope and no longer exists. With no variables referencing the inner Function object it is soon garbage collected and so in effect as soon as the outer function ends the inner function no longer exists - no quite true but close enough for most purposes. Notice that this implies that each time the outer function runs you get a completely new inner Function object. So this is a bit restrictive in that the only code that can ever evaluate the inner function has to be part of the outer function body. The reason is that this is the only place that the myInnerF variable is in scope. Now consider what happens if the inner function has a variable that references it that isn't local to the outer function. In this case the inner function can actually live longer than the outer function because it still has a variable that references it and hence it wont be garbage collected. This is a strange idea but perfectly natural if you think in terms of Function objects. Function objects stay alive and potentially active as long as there is a variable that references them. This means that an inner function object created by an outer function body can live longer than its creator as long as it has a variable that isn't local to its creator that references it. Sounds complicated - but you get used to it. For example
Now the inner function is referenced by a Global variable - recall that this references the Global object - to be more precise it references the execution context but more of this later. Now we can call the outer function:
and when the function returns the global variable myInner references the Function object created within the outer function. And note myInner is a global variable and isn't destroyed when then myFunction ends. As there is still a reference to the Function object it isn't garbage collected and so still exists after the outer function has finished evaluating. Notice that, as myFunction is also a Global variable, the outer Function object also exists, but in general this is not important - even if the outer Function object is garbage collected the inner Function object will still exist. What this means is that you can still evaluate the inner function long after the function body that created it has finished executing and long after the Function object that stores the function body has been destroyed. For example:
will display the Alert box and you will see the value in ans at the time the inner function was created. OK this is all fine, but how can the inner function have access to a variable - ans - that doesn't exist any more? This is what closures are all about. |
||||
Last Updated ( Sunday, 10 May 2015 ) |