为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

合集:Javascript闭包[Closures]

2017-11-12 50页 doc 219KB 18阅读

用户头像

is_014457

暂无简介

举报
合集:Javascript闭包[Closures]合集:Javascript闭包[Closures] Javascript Closures FAQ > FAQ Notes • Introduction • The Resolution of Property Names on Objects o Assignment of Values o Reading of Values • Identifier Resolution, Execution Contexts and scope chains o The Execution Context o scope chains...
合集:Javascript闭包[Closures]
合集:Javascript闭包[Closures] Javascript Closures FAQ > FAQ Notes • Introduction • The Resolution of Property Names on Objects o Assignment of Values o Reading of Values • Identifier Resolution, Execution Contexts and scope chains o The Execution Context o scope chains and [[scope]] o Identifier Resolution • Closures o Automatic Garbage Collection o Forming Closures • What can be done with Closures? o Example 1: setTimeout with Function References o Example 2: Associating Functions with Object Instance Methods o Example 3: Encapsulating Related Functionality o Other Examples • Accidental Closures • The Internet Explorer Memory Leak Problem Introduction Closure A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression). Closures are one of the most powerful features of ECMAScript (javascript) but they cannot be property exploited without understanding them. They are, however, relatively easy to create, even accidentally, and their creation has potentially harmful consequences, particularly in some relatively common web browser environments. To avoid accidentally encountering the drawbacks and to take advantage of the benefits they offer it is necessary to understand their mechanism. This depends heavily on the role of scope chains in identifier resolution and so on the resolution of property names on objects. The simple explanation of a Closure is that ECMAScript allows inner functions; function definitions and function expressions that are inside the function bodes of other functions. And that those inner functions are allowed access to all of the local variables, parameters and declared inner functions within their outer function(s). A closure is formed when one of those inner functions is made accessible outside of the function in which it was contained, so that it may be executed after the outer function has returned. At which point it still has access to the local variables, parameters and inner function declarations of its outer function. Those local variables, parameter and function declarations (initially) have the values that they had when the outer function returned and may be interacted with by the inner function. Unfortunately, properly understanding closures requires an understanding of the mechanism behind them, and quite a bit of technical detail. While some of the ECMA 262 specified algorithms have been brushed over in the early part of the following explanation, much cannot be omitted or easily simplified. Individuals familiar with object property name resolution may skip that section but only people already familiar with closures can afford to skip the following sections, and they can stop reading now and get back to exploiting them.The Resolution of Property Names on Objects ECMAScript recognises two categories of object, "Native Object" and "Host Object" with a sub-category of native objects called "Built-in Object" (ECMA 262 3rd Ed Section 4.3). Native objects belong to the language and host objects are provided by the environment, and may be, for example, document objects, DOM nodes and the like. Native objects are loose and dynamic bags of named properties (some implementations are not that dynamic when it comes to the built in object sub-category, though usually that doesn't matter). The defined named properties of an object will hold a value, which may be a reference to another Object (functions are also Objects in this sense) or a primitive value: String, Number, Boolean, Null or Undefined. The Undefined primitive type is a bit odd in that it is possible to assign a value of Undefined to a property of an object but doing so does not remove that property from the object; it remains a defined named undefinedproperty, it just holds the value . The following is a simplified description of how property values are read and set on objects with the internal details brushed over to the greatest extent possible. Assignment of Values Named properties of objects can be created, or values set on existing named properties, by assigning a value to that named property. So given:- var objectRef = new Object(); //create a generic javascript object. A property with the name "testNumber" can be created as:- objectRef.testNumber = 5; /* - or:- */ objectRef["testNumber"] = 5; The object had no "testNumber" property prior to the assignment but one is created when the assignment is made. Any subsequent assignment does not need to create the property, it just re-sets its value:- objectRef.testNumber = 8; /* - or:- */ objectRef["testNumber"] = 8; Javascript objects have prototypes that can themselves be objects, as will be described shortly, and that prototype may have named properties. But this has no role in assignment. If a value is assigned and the actual object does not have a property with the corresponding name a property of that name is created and the value is assigned to it. If it has the property then its value is re-set. Reading of Values It is in reading values from object properties that prototypes come into play. If an object has a property with the property name used in the property accessor then the value of that property is returned:- /* Assign a value to a named property. If the object does not have a property with the corresponding name prior to the assignment it will have one after it:- */ objectRef.testNumber = 8; /* Read the value back from the property:- */ var val = objectRef.testNumber; /* and - val - now holds the value 8 that was just assigned to the named property of the object. */ But all objects may have prototypes, and prototypes are objects so they, in turn, may have prototypes, which may have prototypes, and so on forming what is called the prototype chain. The prototype chain ends when one of the objects in the chain has a null prototype. The default prototype for Objectthe constructor has a null prototype so:-var objectRef = new Object(); //create a generic javascript object. Object.prototypeCreates an object with the prototype that itself has a objectRefnull prototype. So the prototype chain for contains only one Object.prototypeobject: . However:- /* A "constructor" function for creating objects of a -MyObject1 - type. */ function MyObject1(formalParameter){ /* Give the constructed object a property called - testNumber - and assign it the value passed to the constructor as its first argument:- */ this.testNumber = formalParameter; } /* A "constructor" function for creating objects of a -MyObject2 - type:- */ function MyObject2(formalParameter){ /* Give the constructed object a property called - testString - and assign it the value passed to the constructor as its first argument:- */ this.testString = formalParameter; } /* The next operation replaces the default prototype associated withall MyObject2 instances with an instance of MyObject1, passing the argument - 8 - to the MyObject1 constructor so that its - testNumber - property will be set to that value:- */ MyObject2.prototype = new MyObject1( 8 ); /* Finally, create an instance of - MyObject2 - and assign a reference to that object to the variable - objectRef - passing a string as the first argument for the constructor:- */ var objectRef = new MyObject2( "String_Value" ); MyObject2objectRefThe instance of referred to by the variable has a prototype chain. The first object in that chain is the instance MyObject1of that was created and assigned to the prototype property of MyObject2MyObject1the constructor. The instance of has a prototype, MyObject1the object that was assigned to the function 's prototype property by the implementation. That object has a prototype, the Objectdefault prototype that corresponds with the object referred to Object.prototypeObject.prototypeby . has a null prototype so the prototype chain comes to an end at this point. When a property accessor attempts to read a named property form the object objectRefreferred to by the variable the whole prototype chain can enter into the process. In the simple case:- var val = objectRef.testString; MyObject2objectRef- the instance of referred to by has a property with the name "testString" so it is the value of that property, set to "String_Value", valthat is assigned to the variable . However:- var val = objectRef.testNumber; MyObject2- cannot read a named property form the instance of itself as it val8has no such property but the variable is set to the value of rather than undefined because having failed to find a corresponding named property on the object itself the interpreter then examines the object that is its prototype. Its MyObject1prototype is the instance of and it was created with a property 8named "testNumber" with the value assigned to that property, so the 8property accessor evaluates as the value . MyObject1MyObject2toStringNeither or have defined a method, toStringbut if a property accessor attempts to read the value of a property objectReffrom :- var val = objectRef.toString; val- the variable is assigned a reference to a function. That function is toStringObject.prototypethe property of and is returned because objectRefthe process of examining the prototype of , objectRefwhen turns out not to have a "toString" property, is acting on an object, so when that prototype is found to lack the property its prototype is Object.prototypeexamined in turn. Its prototype is , which does have toStringa method so it is a reference to that function object that is returned. Finally:- var val = objectRef.madeUpProperty; undefined- returns , because as the process of working up the prototype chain finds no properties on any of the object with the name "madeUpPeoperty" it eventually gets to the prototype Object.prototypeof , which is null, and the process ends undefinedreturning . The reading of named properties returns the first value found, on the object or then from its prototype chain. The assigning of a value to a named property on an object will create a property on the object itself if no corresponding property already exists. objectRef.testNumber = This means that if a value was assigned as 3 a "testNumber" property will be created on the instance MyObject2of itself, and any subsequent attempts to read that value will retrieve that value as set on the object. The prototype chain no longer needs to be examined to resolve the property accessor, but the instance MyObject18of with the value of assigned to its "testNumber" property is objectRefunaltered. The assignment to the object masks the corresponding property in its prototype chain. [[prototype]]Note: ECMAScript defines an internal property of the internal Object type. This property is not directly accessible with scripts, but it [[prototype]]is the chain of objects referred to with the internal property that is used in property accessor resolution; the object's prototype chain. A prototypepublic property exists to allow the assignment, definition and manipulation of prototypes in association with the [[prototype]]internal property. The details of the relationship between to two are described in ECMA 262 (3rd edition) and are beyond the scope of this discussion. Identifier Resolution, Execution Contexts and scope chains The Execution Context An execution context is an abstract concept used by the ECMSScript specification (ECMA 262 3rd edition) to define the behaviour required of ECMAScript implementations. The specification does not say anything about how execution contexts should be implemented but execution contexts have associated attributes that refer to specification defined structures so they might be conceived (and even implemented) as objects with properties, though not public properties. All javascript code is executed in an execution context. Global code (code executed inline, normally as a JS file, or HTML page, loads) gets executed in global execution context, and each invocation of a function (possibly as a constructor) has an associated execution context. Code executed with evalevalthe function also gets a distinct execution context but as is never normally used by javascript programmers it will not be discussed here. The specified details of execution contexts are to be found in section 10.2 of ECMA 262 (3rd edition). When a javascript function is called it enters an execution context, if another function is called (or the same function recursively) a newexecution context is created and execution enters that context for the duration of the function call. Returning to the original execution context when that called function returns. Thus running javascript code forms a stack of execution contexts. When an execution context is created a number of things happen in a defined order. First, in the execution context of a function, an "Activation" object is created. The activation object is another specification mechanism. It can be considered as an object because it ends up having accessible named properties, but it is not a normal object as it has no prototype (at least not a defined prototype) and it cannot be directly referenced by javascript code.The next step in the creation of the execution context for a function call is the argumentscreation of an object, which is an array-like object with integer indexed members corresponding with the arguments passed to the function lengthcalleecall, in order. It also has and properties (which are not relevant to this discussion, see the spec for details). A property of the Activation object is created with the name "arguments" and a reference to arguments object is assigned to that property.the Next the execution context is assigned a scope. A scope consists of a list (or [[scope]]chain) of objects. Each function object has an internal property (which we will go into more detail about shortly) that also consists of a list (or chain) of objects. The scope that is assigned to the execution context of a [[scope]]function call consists of the list referred to by the property of the corresponding function object with the Activation object added at the front of the chain (or the top of the list). Then the process of "variable instantiation" takes place using an object that ECMA 262 refers to as the "Variable" object. However, the Activation object is used as the Variable object (note this, it is important: they are the same object). Named properties of the Variable object are created for each of the function's formal parameters, and if arguments to the function call correspond with those parameters the values of those arguments are assigned to the undefinedproperties (otherwise the assigned value is ). Inner function definitions are used to create function objects which are assigned to properties of the Variable object with names that correspond to the function name used in the function declaration. The last stage of variable instantiation is to create named properties of the Variable object that correspond with all the local variables declared within the function. The properties created on the Variable object that correspond with declared undefinedlocal variables are initially assigned values during variable instantiation, the actual initialisation of local variables does not happen until the evaluation of the corresponding assignment expressions during the execution of the function body code. argumentsIt is the fact that the Activation object, with its property, and the Variable object, with named properties corresponding with function local argumentsvariables, are the same object, that allows the identifier to be treated as if it was a function local variable. thisFinally a value is assigned for use with the keyword. If the value assigned refers to an object then property accessors prefixed with thisthe keyword reference properties of that object. If the value assigned this(internally) is null then the keyword will refer to the global object. The global execution context gets some slightly different handling as it does not have arguments so it does not need a defined Activation object to refer to them. The global execution context does need a scope and its scope chain consists of exactly one object, the global object. The global execution context does go through variable instantiation, its inner functions are the normal top level function declarations that make up the bulk of javascript code. The global object is used as the Variable object, which is why globally declared functions become properties of the global object. As do globally declared variables. The global execution context also uses a reference to the global object for thisthe object. scope chains and [[scope]] The scope chain of the execution context for a function call is constructed by adding the execution context's Activation/Variable object to the front of [[scope]]the scope chain held in the function object's property, so it is [[scope]]important to understand how the internal property is defined. In ECMAScript functions are objects, they are created during variable instantiation from function declarations, during the evaluation of function Functionexpressions or by invoking the constructor. FunctionFunction objects created with the constructor always have [[scope]]a property referring to a scope chain that only contains the global object. Function objects created with function declarations or function expressions have the scope chain of the execution context in which they are created [[scope]]assigned to their internal property. In the simplest case of a global function declaration such as:-function exampleFunction(formalParameter){ ... // function body code } - the corresponding function object is created during the variable instantiation for the global execution context. The global execution context has a scope chain consisting of only the global object. Thus the function object that is created and referred to by the property of the global object with the name [[scope]]"exampleFunction" is assigned an internal property referring to a scope chain containing only the global object. A similar scope chain is assigned when a function expression is executed in the global context:- var exampleFuncRef = function(){ ... // function body code } - except in this case a named property of the global object is created during variable instantiation for the global execution context but the function object is not created, and a reference to it assigned to the named property of the global object, until the assignment expression is evaluated. But the creation of the function object still happens in the global execution context so [[scope]]the property of the created function object still only contains the global object in the assigned scope chain. Inner function declarations and expressions result in function objects being created within the execution context of a function so they get more elaborate scope chains. Consider the following code, which defines a function with an inner function declaration and then executes the outer function:-function exampleOuterFunction(formalParameter){ function exampleInnerFuncitonDec(){ ... // inner function body } ... // the rest of the outer function body. } exampleOuterFunction( 5 ); The function object corresponding with the outer function declaration is created during variable instantiation in the global execution context so [[scope]]its property contains the one item scope chain with only the global object in it. When the global code executes the call to exampleOuterFunctionthe a new execution context is created for that function call and an Activation/Variable object along with it. The scope of that new execution context becomes the chain consisting of the new Activation object followed by the chain refereed to by the outer function [[scope]]object's property (just the global object). Variable instantiation for that new execution context results in the creation of a function object that [[scope]]corresponds with the inner function definition and the property of that function object is assigned the value of the scope from the execution context in which it was created. A scope chain that contains the Activation object followed by the global object. So far this is all automatic and controlled by the structure and execution of the source code. The scope chain of the execution context defines [[scope]] properties of the function objects created and the [[scope]]the properties of the function objects define the scope for their execution contexts (along with the corresponding Activation object). But withECMAScript provides the statement as a means of modifying the scope chain. withThe statement evaluates an expression and if that expression is an object it is added to the scope chain of the current execution context (in front of withthe Activation/Variable object). The statement then executes another statement (that may itself be a block statement) and then restores the execution context's scope chainto what it was before. withA function declaration could not be affected by a statement as they result in the creation of function objects during variable instantiation, but a withfunction expression can be evaluated inside a statement:- /* create a global variable - y - that refers to an object:- */var y = {x:5}; // object literal with an - x - property function exampleFuncWith(){ var z; /* Add the object referred to by the global variable - y - to the front of he scope chain:- */ with(y){ /* evaluate a function expression to create a function object and assign a reference to that function object to the local variable - z - :- */ z = function(){ ... // inner function expression body; } } ... } /* execute the - exampleFuncWith - function:- */ exampleFuncWith(); exampleFuncWithWhen the function is called the resulting execution context has a scope chain consisting of its Activation object followed by the with statement adds the object referred to global object. The execution of the yby the global variable to the front of that scope chainduring the evaluation of the function expression. The function object created by the evaluation of the [[scope]]function expression is assigned a property that corresponds with the scope of the execution context in which it is created. A scope ychain consisting of object followed by the Activation object from the execution context of the outer function call, followed by the global object. withWhen the block statement associated with the statement terminates ythe scope of the execution context is restored (the object is removed), but the function object has been created at that point and [[scope]]its property assigned a reference to a scope chain with ythe object at its head. Identifier Resolution Identifiers are resolved against the scope chain. ECMA 262 thiscategorises as a keyword rather than an identifier, which is not thisunreasonable as it is always resolved dependent on the value in the execution context in which it is used, without reference to the scope chain.Identifier resolution starts with the first object in the scope chain. It is checked to see if it has a property with a name that corresponds with the identifier. Because the scope chain is a chain of objects this checking encompasses the prototype chain of that object (if it has one). If no corresponding value can be found on the first object in the scope chain the search progresses to the next object. And so on until one of the objects in the chain (or one of its prototypes) has a property with a name that corresponds with the identifier or the scope chain is exhausted. The operation on the identifier happens in the same way as the use of property accessors on objects described above. The object identified in the scope chain as having the corresponding property takes the place of the object in the property accessor and the identifier acts as a property name for that object. The global object is always at the end of the scope chain. As execution contexts associated with function calls will have the Activation/Variable object at the front of the chain, identifiers used in function bodies are effectively first checked to see whether they correspond with formal parameters, inner function declaration names or local variables. Those would be resolved as named properties of the Activation/Variable object.Closures Automatic Garbage Collection ECMAScript uses automatic garbage collection. The specification does not define the details, leaving that to the implementers to sort out, and some implementations are known to give a very low priority to their garbage collection operations. But the general idea is that if an object becomes un-referable (by having no remaining references to it left accessible to executing code) it becomes available for garbage collection and will at some future point be destroyed and any resources it is consuming freed and returned to the system for re-use. This would normally be the case upon exiting an execution context. The scope chain structure, the Activation/Variable object and any objects created within the execution context, including function objects, would no longer be accessible and so would become available for garbage collection.Forming Closures A closure is formed by returning a function object that was created within an execution context of a function call from that function call and assigning a reference to that inner function to a property of another object. Or by directly assigning a reference to such a function object to, for example, a global variable, a property of a globally accessible object or an object passed by reference as an argument to the outer function call. e.g:- function exampleClosureForm(arg1, arg2){ var localVar = 8; function exampleReturned(innerArg){ return ((arg1 + arg2)/(innerArg + localVar)); } /* return a reference to the inner function defined as - exampleReturned -:- */ return exampleReturned; } var globalVar = exampleClosureForm(2, 4); Now the function object created within the execution context of the call exampleClosureFormto cannot be garbage collected because it is referred to by a global variable and is still accessible, it can even be executed globalVar(n)with . But something a little more complicated has happened because the function globalVar was created with object now referred to by [[scope]]a property referring to a scope chain containing the Activation/Variable object belonging to the execution context in which it was created (and the global object). Now the Activation/Variable object cannot be garbage collected either as the execution of the function object referred to globalVarby will need to add the whole scope chain from [[scope]]its property to the scope of the execution context created for each call to it. A closure is formed. The inner function object has the free variables and the Activation/Variable object on the function's scope chain is the environment that binds them. The Activation/Variable object is trapped by being referred to in the scope [[scope]]chain assigned to the internal property of the function object globalVarnow referred to by the variable. The Activation/Variable object is preserved along with its state; the values of its properties. Scope resolution in the execution context of calls to the inner function will resolve identifiers that correspond with named properties of that Activation/Variable object as properties of that object. The value of those properties can still be read and set even though the execution context for which it was created has exited.In the example above that Activation/Variable object has a state that represents the values of formal parameters, inner function definitions and local variables, at the time when the outer function returned (exited its execution arg12arg2context). The property has the value ,the property the 4localVar8exampleReturnedvalue , the value and an property that is a reference to the inner function object that was returned form the outer function. (We will be referring to this Activation/Variable object as "ActOuter1" in later discussion, for convenience.) exampleClosureFormIf the function was called again as:- var secondGlobalVar = exampleClosureForm(12, 3); - a new execution context would be created, along with a new Activation object. And a new function object would be returned, with its own [[scope]]distinct property referring to a scope chain containing the Activation object form this second execution context, arg112arg23with being and being . (We will be referring to this Activation/Variable object as "ActOuter2" in later discussion, for convenience.) A second and distinct closure has been formed by the second execution exampleClosureForm.of The two function objects created by the execution exampleClosureFormof to which references have been assigned to the globalVarsecondGlobalVarglobal variable and respectively, return ((arg1 + arg2)/(innerArg + localVar))the expression . Which applies various operators to four identifiers. How these identifiers are resolved is critical to the use and value of closures. globalVarConsider the execution of the function object referred to by , globalVar(2)as . A new execution context is created and an Activation object (we will call it "ActInner1"), which is added to the head of the scope [[scope]]chain referred to the property of the executed function object. innerArgActInner1 is given a property named , after its formal parameter 2and the argument value assigned to it. The scope chain for this new ActInner1->ActOuter1->global objectexecution context is: . Identifier resolution is done against the scope chain so in order to return the ((arg1 + arg2)/(innerArg + value of the expression localVar))the values of the identifiers will be determined by looking for properties, with names corresponding with the identifiers, on each object in the scope chain in turn. The first object in the chain is ActInner1 and it has a property innerArg2named with the value . All of the other 3 identifiers correspond with named properties of arg12arg24localVar8ActOuter1; is , is and is . The function call ((2 + 4)/(2 + 8))returns . Compare that with the execution of the otherwise identical function object secondGlobalVarsecondGlobalVar(5)referred to by , as . Calling the Activation object for this new execution context "ActInner2", the scope ActInner2->ActOuter2->global objectchain becomes: . ActInner2 innerArg5returns as and ActOuter2 arg1arg2localVar1238returns , and as , and respectively. The value ((12 + 3)/(5 + 8))returned is. secondGlobalVarExecute again and a new Activation object will appear at the front of the scope chain but ActOuter2 will still be next object in the chain and the value of its named properties will again be used in the resolution of the arg1arg2localVaridentifiers , and . This is how ECMAScript inner functions gain, and maintain, access to the formal parameters, declared inner functions and local variables of the execution context in which they were created. And it is how the forming of a closure allows such a function object to keep referring to those values, reading and writing to them, for as long as it continues to exist. The Activation/Variable object from the execution context in which the inner function was created remains on the scope chain referred to by the function [[scope]] property, until all references to the inner function are object's freed and the function object is made available for garbage collection (along with any now unneeded objects on its scope chain). Inner function may themselves have inner functions, and the inner functions returned from the execution of functions to form closures may themselves return inner functions and form closures of their own. With each nesting the scope chain gains extra Activation objects originating with the execution contexts in which the inner function objects were created. The ECMAScript specification requires a scope chain to be finite, but imposes no limits on their length. Implementations probably do impose some practical limitation but no specific magnitude has yet been reported. The potential for nesting inner functions seems so far to have exceeded anyone's desire to code them.What can be done with Closures? Strangely the answer to that appears to be anything and everything. I am told that closures enable ECMAScript to emulate anything, so the limitation is the ability to conceive and implement the emulation. That is a bit esoteric and it is probably better to start with something a little more practical. Example 1: setTimeout with Function References A common use for a closure is to provide parameters for the execution of a function prior to the execution of that function. For example, when a function is setTimoutto be provided as the first argument to the function that is common in web browser environments. setTimeout schedules the execution of a function (or a string of javascript source code, but not in this context), provided as its first argument, after an interval expressed in milliseconds (as its second argument). If a piece of code setTimeoutsetTimeoutwants to use it calls the function and passes a reference to a function object as the first argument and the millisecond interval as the second, but a reference to a function object cannot provide parameters for the scheduled execution of that function. However, code could call another function that returned a reference to an inner function object, with that inner function object being passed by reference to setTimeout function. The parameters to be used for the execution of the the inner function are passed with the call to the function that returns setTimoutit. executes the inner function without passing arguments but that inner function can still access the parameters provided by the call to the outer function that returned it:- function callLater(paramA, paramB, paramC){ /* Return a reference to an anonymous inner function created with a function expression:- */ return (function(){ /* This inner function is to be executed with - setTimeout - and when it is executed it can read, and act upon, the parameters passed to the outer function:- */ paramA[paramB] = paramC; }); } ... /* Call the function that will return a reference to the inner function object created in its execution context. Passing the parameters that the inner function will use when it is eventually executed as arguments to the outer function. The returned reference to the inner function object is assigned to a local variable:- */ var functRef = callLater(elStyle, "display", "none"); /* Call the setTimeout function, passing the reference to the innerfunction assigned to the - functRef - variable as the first argument:- */ hideMenu=setTimeout(functRef, 500); Example 2: Associating Functions with Object Instance Methods There are many other circumstances when a reference to a function object is assigned so that it would be executed at some future time where it is useful to provide parameters for the execution of that function that would not be easily available at the time of execution but cannot be known until the moment of assignment. One example might be a javascript object that is designed to encapsulate the interactions with a particular DOM element. It doOnClickdoMouseOverdoMouseOuthas , and methods and wants to execute those methods when the corresponding events are triggered on the DOM element, but there may be any number of instances of the javascript object created associated with different DOM elements and the individual object instances do not know how they will be employed by the code that instantiated them. The object instances do not know how to reference themselves globally because they do not know which global variables (if any) will be assigned references to their instances. So the problem is to execute an event handling function that has an association with a particular instance of the javascript object, and knows which method of that object to call. The following example uses a small generalised closure based function that associates object instances with element event handlers. Arranging that the execution of the event handler calls the specified method of the object instance, passing the event object and a reference to the associated element on to the object method and returning the method's return value./* A general function that associates an object instance with an event handler. The returned inner function is used as the event handler. The object instance is passed as the - obj - parameter and the name of the method that is to be called on that object is passed as the - methodName - (string) parameter. */ function associateObjWithEvent(obj, methodName){ /* The returned inner function is intended to act as an event handler for a DOM element:- */ return (function(e){ /* The event object that will have been parsed as the - e - parameter on DOM standard browsers is normalised to the IE event object if it has not been passed as an argument to the event handling inner function:- */ e = e||window.event; /* The event handler calls a method of the object - obj - with the name held in the string - methodName - passing the now normalised event object and a reference to the element to which the event handler has been assigned using the - this - (which works because the inner function is executed as a method of that element because it has been assigned as an event handler):- */ return obj[methodName](e, this); }); } /* This constructor function creates objects that associates themselveswith DOM elements whose IDs are passed to the constructor as a string. The object instances want to arrange than when the corresponding element triggers onclick, onmouseover and onmouseoutevents corresponding methods are called on their object instance. */ function DhtmlObject(elementId){ /* A function is called that retrieves a reference to the DOM element (or null if it cannot be found) with the ID of the required element passed as its argument. The returned value is assigned to the local variable - el -:- */ var el = getElementWithId(elementId); /* The value of - el - is internally type-converted to boolean for the - if - statement so that if it refers to an object the result will be true, and if it is null the result false. So that the following block is only executed if the - el - variable refers to a DOM element:- */ if(el){ /* To assign a function as the element's event handler this object calls the - associateObjWithEvent - function specifying itself (with the - this - keyword) as the object on which a method is to be called and providing the name of the method that is to be called. The - associateObjWithEvent - function will return a reference to an inner function that is assigned to the event handler of the DOM element. That inner function will call the required method on the javascript object when it is executed in response to events:- */ el.onclick = associateObjWithEvent(this, "doOnClick"); el.onmouseover = associateObjWithEvent(this, "doMouseOver"); el.onmouseout = associateObjWithEvent(this, "doMouseOut"); ... } } DhtmlObject.prototype.doOnClick = function(event, element){ ... // doOnClick method body. } DhtmlObject.prototype.doMouseOver = function(event, element){ ... // doMouseOver method body. } DhtmlObject.prototype.doMouseOut = function(event, element){ ... // doMouseOut method body. } DhtmlObjectAnd so any instances of the can associate themselves with the DOM element that they are interested in without any need to know anything about how they are being employed by other code, impacting on the global namespace or risking clashes with other instances of DhtmlObjectthe. Example 3: Encapsulating Related Functionality Closures can be used to create additional scopes that can be used to group interrelated and dependent code in a way that minimises the risk of accidental interaction. Suppose a function is to build a string and to avoid the repeated concatenation operations (and the creation of numerous intermediate strings) the desire is to use an array to store the parts of the string in sequence and Array.prototype.jointhen output the results using the method (with an empty string as its argument). The array is going to act as a buffer for the output, but defining it locally to the function will result in its re-creation on each execution of the function, which may not be necessary if the only variable content of that array will be re-assigned on each function call. One approach might make the array a global variable so that it can be re-used without being re-created. But the consequences of that will be that, in addition to the global variable that refers to the function that will use the buffer array, there will be a second global property that refers to the array itself. The effect is to render the code less manageable, as, if it is to be used elsewhere, its author has to remember to include both the function definition and the array definition. It also makes the code less easy to integrate with other code because instead of just ensuring that the function name is unique within the global namespace it is necessary to ensure that the Array on which it is dependent is using a name that is unique within the global namespace.A Closure allows the buffer array to be associated (and neatly packaged) with the function that is dependent upon it and simultaneously keep the property name to which the buffer array as assigned out of the global namespace and free of the risk of name conflicts and accidental interactions. The trick here is to create one additional execution context by executing a function expression in-line and have that function expression return an inner function that will be the function that is used by external code. The buffer array is then defined as a local variable of the function expression that is executed in-line. That only happens once so the Array is only created once, but is available to the function that depends on it for repeated use. The following code creates a function that will return a string of HTML, much of which is constant, but those constant character sequences need to be interspersed with variable information provided as parameter to the function call. A reference to an inner function object is returned from the in-line execution of a function expression and assigned to a global variable so that it can be called as a global function. The buffer array is defined as a local variable in the outer function expression. It is not exposed in the global namespace and does not need to be re-created whenever the function that uses it is called./* A global variable - getImgInPositionedDivHtml - is declared and assigned the value of an inner function expression returned from a one-time call to an outer function expression. That inner function returns a string of HTML that represents an absolutely positioned DIV wrapped round an IMG element, such that all of the variable attribute values are provided as parameters to the function call:- */ var getImgInPositionedDivHtml = (function(){ /* The - buffAr - Array is assigned to a local variable of the outer function expression. It is only created once and that one instance of the array is available to the inner function so that it can be used on each execution of that inner function. Empty strings are used as placeholders for the date that is to be inserted into the Array by the inner function:- */ var buffAr = [ '
\"',<\/div>' ]; /* Return the inner function object that is the result of the evaluation of a function expression. It is this inner function object that will be executed on each call to - getImgInPositionedDivHtml( ... ) -:- */ return (function(url, id, width, height, top, left, altText){ /* Assign the various parameters to the corresponding locations in the buffer array:- */ buffAr[1] = id; buffAr[3] = top; buffAr[5] = left; buffAr[13] = (buffAr[7] = width); buffAr[15] = (buffAr[9] = height); buffAr[11] = url; buffAr[17] = altText; /* Return the string created by joining each element in the array using an empty string (which is the same as just joining the elements together):- */ return buffAr.join(''); }); //:End of inner function expression. })(); /*^^- :The inline execution of the outer function expression. */ If one function was dependent on one (or several) other functions, but those other functions were not expected to be directly employed by any other code, then the same technique could be used to group those functions with the one that was to be publicly exposed. Making a complex multi-function process into an easily portable and encapsulated unit of code. Other Examples Probably one of the best known applications of closures is Douglas Crockford's technique for the emulation of private instance variables in ECMAScript objects. Which can be extended to all sorts of structures of scope contained nested accessibility/visibility, including the emulation of private static members for ECMAScript objects. The possible application of closures are endless, understanding how they work is probably the best guide to realising how they can be used.Accidental Closures Rendering any inner function accessible outside of the body of the function in which it was created will form a closure. That makes closures very easy to create and one of the consequences is that javascript authors who do not appreciate closures as a language feature can observe the use of inner functions for various tasks and employ inner functions, with no apparent consequences, not realising that closures are being created or what the implications of doing that are. Accidentally creating closures can have harmful side effects as the following section on the IE memory leak problem describes, but they can also impact of the efficiency of code. It is not the closures themselves, indeed carefully used they can contribute significantly towards the creation of efficient code. It is the use of inner functions that can impact on efficiency. A common situation is where inner functions are used is as event handlers for DOM elements. For example the following code might be used to add an onclick handler to a link element:- /* Define the global variable that is to have its value added to the- href - of a link as a query string by the following function:- */ var quantaty = 5; /* When a link passed to this function (as the argument to the function call - linkRef -) an onclick event handler is added to the link that will add the value of a global variable - quantaty - to the - href - of that link as a query string, then return true so that the link will navigate to the resource specified by the - href - which will by then include the assigned query string:- */ function addGlobalQueryOnClick(linkRef){ /* If the - linkRef - parameter can be type converted to true (which it will if it refers to an object):- */ if(linkRef){ /* Evaluate a function expression and assign a reference to the function object that is created by the evaluation of the function expression to the onclick handler of the link element:- */ linkRef.onclick = function(){ /* This inner function expression adds the query string to the - href - of the element to which it is attached as an event handler:- */ this.href += ('?quantaty='+escape(quantaty)); return true; }; } } addGlobalQueryOnClickWhenever the function is called a new inner function is created (and a closure formed by its assignment). From the efficiency point of view that would not be significant if addGlobalQueryOnClickthe function was only called once or twice, but if the function was heavily employed many distinct function objects would be created (one for each evaluation of the inner function expression).The above code is not taking advantage of the fact that inner functions are becoming accessible outside of the function in which they are being created (or the resulting closures). As a result exactly the same effect could be achieved by defining the function that is to be used as the event handler separately and then assigning a reference to that function to the event handling property. Only one function object would be created and all of the elements that use that event handler would share a reference to that one function:-/* Define the global variable that is to have its value added to the - href - of a link as a query string by the following function:- */ var quantaty = 5; /* When a link passed to this function (as the argument to the function call - linkRef -) an onclick event handler is added to the link that will add the value of a global variable - quantaty - to the - href - of that link as a query string, then return true so that the link will navigate to the resource specified by the - href - which will by then include the assigned query string:- */ function addGlobalQueryOnClick(linkRef){ /* If the - linkRef - parameter can be type converted to true (which it will if it refers to an object):- */ if(linkRef){ /* Assign a reference to a global function to the event handling property of the link so that it becomes the element's event handler:- */ linkRef.onclick = forAddQueryOnClick; } } /* A global function declaration for a function that is intended to act as an event handler for a link element, adding the value of a global variable to the - href - of an element as an event handler:- */ function forAddQueryOnClick(){ this.href += ('?quantaty='+escape(quantaty)); return true; } As the inner function in the first version is not being used to exploit the closures produced by its use, it would be more efficient not to use an inner function, and thus not repeat the process of creating many essentially identical function objects. A similar consideration applies to object constructor functions. It is not uncommon to see code similar to the following skeleton constructor:- function ExampleConst(param){ /* Create methods of the object by evaluating function expressions and assigning references to the resulting function objects to the properties of the object being created:- */ this.method1 = function(){ ... // method body. }; this.method2 = function(){ ... // method body. }; this.method3 = function(){ ... // method body. }; /* Assign the constructor's parameter to a property of the object:- */ this.publicProp = param; } new Each time the constructor is used to create an object, with ExampleConst(n), a new set of function objects are created to act as its methods. So the more object instances that are created the more function objects are created to go with them. Douglas Crockford's technique for emulating private members on javascript objects exploits the closure resulting form assigning references to inner function objects to the public properties of a constructed object from within its constructor. But if the methods of an object are not taking advantage of the closure that they will form within the constructor the creation of multiple function objects for each object instantiation will make the instantiation process slower and more resources will be consumed to accommodate the extra function objects created. In that case it would be more efficient to create the function object once and assign references to them to the corresponding properties of the prototypeconstructor's so they may be shared by all of the objects created with that constructor:- function ExampleConst(param){ /* Assign the constructor's parameter to a property of the object:- */ this.publicProp = param; } /* Create methods for the objects by evaluating function expressions and assigning references to the resulting function objects to the properties of the constructor's prototype:- */ ExampleConst.prototype.method1 = function(){ ... // method body. }; ExampleConst.prototype.method2 = function(){ ... // method body. }; ExampleConst.prototype.method3 = function(){ ... // method body. }; The Internet Explorer Memory Leak Problem The Internet Explorer web browser (verified on versions 4 to 6 (6 is current at the time of writing)) has a fault in its garbage collection system that prevents it from garbage collecting ECMAScript and some host objects if those host objects form part of a "circular" reference. The host objects in question are any DOM Nodes (including the document object and its descendants) and ActiveX objects. If a circular reference is formed including one or more of them, then none of the objects involved will be freed until the browser is closed down, and the memory that they consume will be unavailable to the system until that happens. A circular reference is when two or more objects refer to each other in a way that can be followed and lead back to the starting point. Such as object 1 has a property that refers to object 2, object 2 has a property that refers to object 3 and object 3 has a property that refers back to object 1. With pure ECMAScript objects as soon as no other objects refer to any of objects 1, 2 or 3 the fact that they only refer to each other is recognised and they are made available for garbage collection. But on Internet Explorer, if any of those objects happen to be a DOM Node or ActiveX object, the garbage collection cannot see that the circular relationship between them is isolated from the rest of the system and free them. Instead they all stay in memory until the browser is closed.Closures are extremely good at forming circular references. If a function object that forms a closure is assigned as, for example, and event handler on a DOM Node, and a reference to that Node is assigned to one of the Activation/Variable objects in its scope chain then a circular reference DOM_Node.onevent ->function_object.[[scope]] exists. ->scope_chain ->Activation_object.nodeRef ->DOM_Node . It is very easy to do, and a bit of browsing around a site that forms such a reference in a piece of code common to each page can consume most of the systems memory (possibly all). Care can be taken to avoid forming circular references and remedial action can be taken when they cannot otherwise be avoided, such as using IE's onunload event to null event handling function references. Recognising the problem and understanding closures (and their mechanism) is the key to avoiding this problem with IE. comp.lang.javascript FAQ notes T.O.C. •Written by Richard Cornford. March 2004. •With corrections and suggestions by:- oMartin Honnen. oYann-Erwan Perio (Yep). oLasse Reichstein Nielsen. (definition of closure) oMike Scirocco. oDr John Stockton. oGarrett Smith. Table of contents 1. Good/reference articles 2. Preliminary note: Nested functions3. Closures 4. When are closures useful ? 5. Browser specific hacks and bugs1. 1. Good/reference articles 2. 2. Preliminary note: Nested functions3. 3. Closures 4. 4. When are closures useful ?5. 5. Browser specific hacks and bugs 1. 1. Good/reference articles First, read the following links: 2. 2. Preliminary note: Nested functions,A nested function is defined within another function. ,function foo() , { , function bar() { } , } function barfunction foo is not reachable from outside by saying foo.bar()something like: ,Inner functions can be exposed outside the outer function.,function foo() , { , bar = function () { } , } , ,foo(); //=> assigns the inner (anonymous) function to global variable bar ,bar(); //=>invokes nested function barfunction barfunction foo is now reachable from outside , since it has been assigned to a global variable bar (since bar is not preceded with "var" it is a global variable). foo()barNote, has to be invoked at least once for global variable to be set. ,Inner functions can also be returned from the outer function,function foo() , { , var bar = function () { } , return bar; , } , ,var bar_reference = foo(); ,bar_reference(); //=>invokes nested function barfunction barfunction foo is now reachable from outside , since it has bar_referencebeen assigned to a global variable . foo()Note, has to be invoked at least once for it to return the barreference to . 3. 3. Closures •As shown above, nested functions can be accessed from outside the outer function. A closure is created the moment a nested function isdefined and returned (or defined and invoked). A closure simply means that the inner function has access to internal (local) variables of the outer function. The interesting part is that it has access to those local variables even after the outer function has finished executing and no longer exists. This is achieved by Javascript internally saving the state of the local outer variables in a separate "closure" object. function foo() { var x = "hello"; function bar() { alert(x); } return bar; } var bar_ref = foo(); //foo has finished executing at this point and local //variable x does not exist anymore bar_ref(); //=> alerts "hello" bar_ref() correctly alerts "hello" in this example because the variable x is still accessible via the closure. •Local variables saved in closures are anything declared via the "var" keyword in the outer function and any method parameters of the outer function. •function foo(x) • { • function bar() { • alert(x); • } • return bar; • } • •var bar_ref = foo("hello"); •bar_ref(); bar_ref() correctly alerts "hello" in this example because the local method parameter variable is accessible via the closure. •Important note: A new closure is created everytime a inner function is assigned or returned. function foo(x) { function bar() { alert(x); } return bar; } var bar_ref_1 = foo("hello"); var bar_ref_2 = foo("world"); bar_ref_1(); //alerts hello bar_ref_2(); //alerts world Note: this means that multiple closures are created by the same nested function (every time the nested function is called) and modifying/changing variables contained in one closure cannot affect any other closures. 4. 4. When are closures useful ? Closures essentially save us from the need to create global variables. They are mostly useful in event handlers/callback functions, when such functions need to access some callback specfic data.In Java, we can specify a method as a so-called "callback" function. We can create several different objects (each with different instance data) and can specify a particular object upon which the callback method will be invoked. This method, when invoked, will have access to its own object data. class MyObject { String greeting; MyObject(String word) { this.greeting = word; } void showGreeting() { System.out.print(greeting); } } MyObject obj1 = new MyObject("hello"); MyObject obj2 = new MyObject("world"); SomeEventHandler.addCallBack(obj1); //invokes showGreeting() and prints "hello" SomeEventHandler2.addCallBack(obj2); //invokes showGreeting() and prints "world" Note, the event handler runs on a different thread (typically awt/swing event handler thread) but the method that is invoked has access to it's own object data (since all methods are tied to objects).Alternately, (in Java) it's also common to pass a "this" object pointer to an event handler (when the event handler is added by the object on itself) and the event handler can use the "this" pointer to access that particular object's data. The object passed to the event handler typically implements a interface, such that the event hander (and the compiler) know that the object has a well known method that will be called back. (showGreeting() in this example). By contrast, in Javascript (back in the day, things started out in this fashion: var greeting = "hello"; function foo() { alert(greeting); } SomeElement.addEventHandler("onclick", foo); SomeElement2.addEventHandler("onclick", foo); There were event handlers that would invoke global functions (which could access only global variables). In this example, we are limited to greetingonly one value for the variable, every time it is read foo()from (since it is a global variable). If we now try and follow the Java model and object-ify things, we can say: function MyObject(word) { this.greeting = word; this.handle = function() { alert(this.greeting); } } var obj1 = new MyObject("hello"); var obj2 = new MyObject("world"); SomeElement.addEventHandler("onclick", obj1); SomeElement2.addEventHandler("onclick", obj2); This may or may not work. It depends on how the callback mechanism is coded: 1.The callback mechanism could invoke the callback method as obj1.handle()a function. If instead of calling , the event handler handle()thiscalls , then '' will not point to the right object (it will obj1point to the global object, not ). 2.The callback mechanism may not even accept an object, it may only accept a function. This is very common in Javascript. For setTimeoutexample, only accepts a function. 3.SomeElement.addEventHandler("onclick", obj1, some_function); In this case, it's imposssible to access function specific data (unless either a global variable or a closure is used).So how do closures solve this problem ? function remember_me(word) { function inner() { alert(word); } return inner; } SomeElement.addEventHandler("onclick", remember_me("hello")); In this example, the function and associated data ("hello") is remembered together, in the form of a closure. The event handler takes a function to a "callback" function (not an object). Yet, the function has access to it's own separate data and each closure-function has independent separate data. This is conceptually similar to having an method based callback, where the method has access to it's object data. I feel that closures tend to make things a whole lot more complicated and hard to understand in general. They are not really a feature in a programming language, just a source of confusion (and in Javascript, sometimes necessary for the above reasons).5. 5. Browser specific hacks and bugs Internet explorer has some garbage collection bugs. These are not closure-specific but are easily exposed when when using closures.As shown in the above diagram, IE (version 7 and earlier) has separate garbage collectors for native Javascript and HTML DOM that is reflected as Javascript objects. If there is a circular link between a HTML DOM object and a native object, then neither is garbage colleted for the duration of the IE browser session (even if the user navigates away from the page). function addHandler() { var mydiv = document.getElementById("myid"); mydiv.onclick = function() { alert(this.innerHTML + "\n"); } In this example, mydiv (in the HTML DOM side) refers to native JS onclickcode via its event handler. The JS code for the event handler, refers back to mydiv via the inadvertent closure formed by the inner function. This circular reference leaks memory in IE.One way around this is to say: function addHandler() { var mydiv = document.getElementById("myid"); mydiv.onclick = handler; } function handler() { alert(this.innerHTML + "\n"); } handlerSince the function is not a nested function, no closure is formed. Another way to do this without a closure is: function addHandler() { var mydiv = document.getElementById("myid"); mydiv.onclick = new Function( 'alert(this.innerHTML)' ); } Even though the handler function is defined inline, the use of Functionthe constructor prevents a implicit closure (functions created via a Function constructor do not save local scope). JavaScript Closures for DummiesSubmitted by Morris on Tue, 2006-02-21 10:19. Closures Are Not Magic This page explains closures so that a programmer can understand them - using working JavaScript code. It is not for gurus nor functional programmers.Closures are not hard to understand once the core concept is grokked. However, they are impossible to understand by reading any academic papers or academically oriented information about them! This article is intended for programmers with some programming experience in a main-stream language, and who can read the following JavaScript function:function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { alert(text); } sayAlert(); } An Example of a Closure Two one sentence summaries: •a closure is the local variables for a function - kept alive after the function has returned, or •a closure is a stack-frame which is not deallocated when the function returns. (as if a 'stack-frame' were malloc'ed instead of being on the stack!) The following code returns a reference to a function: function sayHello2(name) { var text = 'Hello ' + name; // local variable var sayAlert = function() { alert(text); } return sayAlert; } Most JavaScript programmers will understand how a reference to a function is returned to a variable in the above code. If you don't, then you need to before you can learn closures. A C programmer would think of the function as returning a pointer to a function, and that the variables sayAlert and say2 were each a pointer to a function. There is a critical difference between a C pointer to a function, and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure. The above code has a closure because the anonymous function function() { alert(text); } is declared inside another function,sayHello2() in this example. In JavaScript, if you use the function keyword inside another function, you are creating a closure. In C, and most other common languages after a function returns, all the local variables are no longer accessable because the stack-frame is destroyed.In JavaScript, if you declare a function within another function, then the local variables can remain accessable after returning from the function you called. This is demonstrated above, because we call the function say2(); after we have returned fromsayHello2(). Notice that the code that we call references the variable text, which was a local variable of the function sayHello2(). function() { alert(text); } Click the button above to get JavaScript to print out the code for the anonymous function. You can see that the code refers to the variable text. The anonymous function can reference text which holds the value 'Jane' because the local variables ofsayHello2() are kept in a closure. The magic is that in JavaScript a function reference also has a secret reference to the closure it was created in - similar to how delegates are a method pointer plus a secret reference to an object. More examples For some reason closures seem really hard to understand when you read about them, but when you see some examples you can click to how they work (it took me a while). I recommend working through the examples carefully until you understand how they work. If you start using closures without fully understanding how they work, you would soon create some very wierd bugs! Example 3 This example shows that the local variables are not copied - they are kept by reference. It is kind of like keeping a stack-frame in memory when the outer function exits! function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } Example 4 All three global functions have a common reference to the same closure because they are all declared within a single call tosetupSomeGlobals().function setupSomeGlobals() { // Local variable that ends up within closure var num = 666; // Store some references to functions as global variables gAlertNumber = function() { alert(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } The three functions have shared access to the same closure - the local variables of setupSomeGlobals() when the three functions were defined. Note that in the above example, if you click setupSomeGlobals() again, then a new closure (stack-frame!) is created. The oldgAlertNumber, gIncreaseNumber, gSetNumber variables are overwritten with new functions that have the new closure. (In JavaScript, whenever you declare a function inside another function, the inside function(s) is/are recreated again each time the outside function is called.) Example 5 This one is a real gotcha for many people, so you need to understand it.Be very careful if you are defining a function within a loop: the local variables from the closure do not act as you might first think. function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() {alert(item + ' ' + list[i])} ); } return result; } function testList() { var fnlist = buildList([1,2,3]); // using j only to help prevent confusion - could use i for (var j = 0; j < fnlist.length; j++) { fnlist[j](); } } The line result.push( function() {alert(item + ' ' + list[i])} adds a reference to an anonymous function three times to the result array. If you are not so familiar with anonymous functions think of it like: pointer = function() {alert(item + ' ' + list[i])}; result.push(pointer); Note that when you run the example, "item3 undefined" is alerted three times! This is because just like previous examples, there is only one closure for the local variables for buildList. When the anonymous functions are called on the line fnlist[j]();they all use the same single closure, and they use the current value for i and item within that one closure (where i has a value of3 because the loop had completed, and item has a value of 'item3'). Example 6 This example shows that the closure contains any local variables that were declared inside the outer function before it exited. Note that the variable alice is actually declared after the anonymous function. The anonymous function is declared first: and when that function is called it can access the alice variable because alice is in the closure. Also sayAlice()(); just directly calls the function reference returned from sayAlice() - it is exactly the same as what was done previously, but without the temp variable. function sayAlice() { var sayAlert = function() { alert(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return sayAlert; } Tricky: note also that the sayAlert variable is also inside the closure, and could be accessed by any other function that might be declared within sayAlice() or it could be accessed recursively within the inside function. Example 7 This final example shows that each call creates a separate closure for the local variables. There is not a single closure per function declaration. There is a closure for each call to a function. function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); alert('num: ' + num + '\nanArray ' + anArray.toString() + '\nref.someVar ' + ref.someVar); } } Summary If everything seems completely unclear then the best thing to do is to play with the examples. Reading an explanation is much harder than understanding examples. My explanations of closures and stack-frames etc are not technically correct - they are gross simplifications intended to help understanding. Once the basic idea is grokked, you can pick up the details later. Final points: •Whenever you use function inside another function, a closure is used. •Whenever you use eval() inside a function, a closure is used. The text you eval can reference local variables of the function, and within eval you can even create new local variables by using eval('var foo = … •When you use Function() inside a function, it does not create a closure. (The new function cannot reference the local variables of the function calling Function()). •A closure in JavaScript is like keeping a copy of the all the local variables, just as they were when a function exited. •It is probably best to think that a closure is always created just on entry to a function, and the local variables are added to that closure. •A new set of local variables is kept every time a function with a closure is called (Given that the function contains a function declaration inside it, and a reference to that inside function is either returned or an external reference is kept for it in some way). •Two functions might look like they have the same source text, but have completely different behaviour because of their 'hidden' closure. I don't think JavaScript code can actually find out if a function reference has a closure or not. •If you are trying to do any dynamic source code modifications ( for example: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ), it won't work if myFunction is a closure (Of course, you would never even think of doing source code string substitution at runtime, but...). •It is possible to get function declarations within function declarations within functions - and you can get closures at more than one level. •I think normally a closure is the term for both the function along with the variables that are captured. Note that I do not use that definition in this article! •I suspect that closures in JavaScript differ from those normally found in functional languages. Links •TrimBreakpoint is a tricky use of closures to let you inspect local variables for a function from a popup breakpoint window. •Douglas Crockford's simulated private attributes and private methods for an object, using closures. •A great explanation of how closures can cause memory leaks in IE if you are not careful. Thanks If you have just learnt closures (here or elsewhere!), then I am interested in any feedback from you about any changes you might suggest that could make this article clearer. Send an email to morrisjohns.com (morris_closure @). Please note that I am not a guru on JavaScript - nor on closures. Thanks for reading. Closing?The?Book?On? Javascript?Closures The time has come to address one of Javascript's more powerful features: closures. Simply put, a closure is a variable, created inside a function, which continues to exist after the function has finished executing. Understanding closures will allow you to avoid damaging memory leaks in Internet Explorer and also allow you to fully exploit private variables and methods when creating Javascript objects. Hello?Closure! Here's an example which will vividly illustrate the concept. try { alert(closureDemo); } catch(err) { alert("closureDemo doesn't exist yet, hit 'OK', we'll call the function that creates it and try again"); } var theDemo = function () { closureDemo = "This is a closure! This string is displayed in an alertBox outside of the function which created it."; } theDemo(); alert(closureDemo); If you follow along with the code you'll see we try to do an alert with a variable which hasn't been defined yet. We wrapped this in try/catch statements so the error we know closureDemowill happen won't generate a real error message. Of course, doesn't exist yet so the catch block is executed. Next we define a function which will closureDemotheDemo()declare , then we call the function itself: . Finally we issue an closureDemoalert with as the string, and it works!. theDemoThe last alert works because we created a closure in the function. We did it because Javascript has a little quirk where if you don't declare a variable with varthe keyword then the variable becomes global, regardless of where it was declared. So at one point in time you've probably created a closure without even varrealizing it simply because it's easy to forget adding a keyword before each and every variable in your scripts. Go ahead and try executing the example with this convenient handy, dandy button: Start Demo Child?Functions?&?Closures So you're probably scratching your head and saying "So what?".Well there's actually a lot of good reasons to know how and why Javascript creates closures, if only because Internet Explorer tends to leak memory when they're used. A side effect of this is that there's an admonition to never use child functions because they create closures! Which CAN be true, but also can be false -- it all depends on how you write your code. Lets take a look at a child function/closure.theParent = function () { theChild() = function() { alert("I'm the baby!); } } theParent(); theChild(); theChild()Now any classically trained programmer will look at this code and say "" theParent()can only be called within "". But in Javascript that is not the case as you theChild()can see by clicking this button which will directly call "" without going theParent()through . theChild() The answer as to why this is happening of course is that we are declaring the varfunctions like variables and we are leaving off the keyword, so theChild becomes global and a closure. This same example without creating the closure would like like this…var theParent = function () { var theChild() = function() { alert("I'm the baby!); } } theParent(); theChild(); theChild()In this example, call at the end of the script will generate an error theChild()because to access the function the call must actually be made theParent()within . More?than?global?variablesA closure is more than a global variable. Take a look at the following object which has become popular at the advanced end of Javascript coding.var myObject = function() { var privateVar = 'This variable is private!'; var privateFunction = function() { alert('this function is Private!'); } return { showPrivateVar : function () { alert(privateVar); }, changePrivateVar : function(val) { privateVar = val; }, callPrivateFunc : function() { privateFunction(); } } }(); The key to understanding what is happening here is to look at the first line and then the last, ignoring everything in between.var myObject = function() {} (); myObjectYou can see we're creating a variable called and that it's a function that won't be receiving any arguments. The braces {} denote where the code will go and it's terminated with another set of parenthesis (), these trailing parenthesis tell javascript to execute the function immediately as it's parsed. What this means is that myObject isn't going to be equal to the function you see, but rather WHAT THAT FUNCTION RETURNS. So javascript creates the function, executes it, and places the return contents myObjectin . The return contents in this case is a javascript object containing 3 showPrivateVarchangePrivateVarcallPrivateFuncmethods, , , and . What's cool and closurey about all of this is privateVarprivateFunctionthat and can ONLY be accessed by those methods that were returned. The function that creates them doesn't exist now, only the results of the functions exist and those functions have access privateVarprivateFunctionto and because they are closures, they exist but can be accessed in a very limited way. Now when we call myObject.showPriavteVar() we'll get an alert box that pops up and says "This variable is private!". Likewise when we call myObject.privateVar('new privateVarvalue'), we can change the value of and that's the only privateVarway can be changed! And finally we call the private function through the myObject.callPrivateFunc(). So closures are a bit more useful than accidentally creating global variables (potentially interfering with other variables of the same name), and causing inadvertent memory leaks in Internet Explorer. As you start to move from beginning and intermediate javascript to full object-oriented applications, closures can help you protect critical variables and methods both from your own code and other modules and libraries you elect to use. Working?with?and?around?closures varSo the first rule of Javascript closures is to always use the keyword inside your functions unless you are deliberately setting out to create a closure, this even extends to functions you declare with the variable declaration style syntax.The second rule is to use closures whenever you need to hide variables and methods of an object. This is a fairly advance technique but one which is easily mastered and ultimately very useful in creating true object-oriented applications. Leak Free Javascript Closures October 20, 2005 Javascript closures can be a powerful programming technique. Unfortunately in Internet Explorer they are a common source of memory leaks. Therefore I propose a method to create closures that don't leak memory. Problem First start with a short explanation of the problem I tried to fix. Here is an example of a simple event handler (IE only for clarity): function attach() { var element = document.getElementById("my-element"); element.attachEvent("onclick", function(){ alert("Clicked: " + element.innerHTML); }); } This seems harmless enough, but the function (closure) is created in a scope elementelementwhich contains . Since we attach the function to , a circular elementreference is created and IE no longer can garbage collect . This can elementeasily bedemonstrated by adding a large string to . There are a lot of solutions for this problem, of which most focus on event attaching. But this problem can also occur when Javascript objects are set as a property on an HTML element. Solution So we need a function that can access an HTML element without creating an inline closure that leaks memory. closureclosure method to each function. wraps the The following code adds a thisoriginal function in such a way that is set to the given object.Function.prototype.closure = function(obj) { // Init object storage. if (!window.__objs) window.__objs = []; // Init closure storage. if (!this.__closureFuncs) this.__closureFuncs = []; // Make sure the object has an id and is stored in the object store. var objId = obj.__closureObjId; if (!objId) __objs[objId = obj.__closureObjId = __objs.length] = obj; // See if we previously created a closure for this object/function pair. var closureFunc = this.__closureFuncs[objId]; if (closureFunc) return closureFunc; // Clear reference to keep the object out of the closure scope. obj = null; // Create the closure, store in cache and return result. var me = this; return this.__closureFuncs[objId] = function() { return me.apply(__objs[objId], arguments); }; }; So now we can do: function attach() { var element = document.getElementById("my-element"); element.attachEvent("onclick", clickHandler.closure(element));} function clickHandler() { alert("Clicked: " + this.innerHTML); } Which doesn't leak. And can also be used to run any function in a given context: function myObject() { this.status = "waiting"; setTimeout(this.delayedCode.closure(this), 1000); } myObject.prototype = { delayedCode: function() { this.status = "done waiting"; } }; var o = new myObject(); Some might argue that this fixes one leak with another since all closure context objects are stored in an array. Though this array will be freed on reload, it will stay in memory as long as the user stays on the page.A simulation of a highly dynamic webpage shows that this isn't a big problem in practise. This shows that an html element takes about 1KB and even an application like Xopus doesn't create more than 10000 elements in a single session. And even if it would, it would only take about 10MB which I think is acceptable. Update: new version with less prerequisitesThe above mentioned closure function will only work if the original function does not have a (indirect) reference to the object to which the closure is attached. So this will still leak: function attach() { function clickHandler() { alert("Clicked: " + this.innerHTML); } var element = document.getElementById("my-element"); element.attachEvent("onclick", clickHandler.closure(element));} This is caused by the fact that the created closure function still has a mereference to it's original function (). A new version of the closure function fixes that problem: Function.prototype.closure = function(obj) { // Init object storage. if (!window.__objs) { window.__objs = []; window.__funs = []; } // For symmetry and clarity. var fun = this; // Make sure the object has an id and is stored in the object store. var objId = obj.__objId; if (!objId) __objs[objId = obj.__objId = __objs.length] = obj; // Make sure the function has an id and is stored in the function store. var funId = fun.__funId; if (!funId) __funs[funId = fun.__funId = __funs.length] = fun; // Init closure storage. if (!obj.__closures) obj.__closures = []; // See if we previously created a closure for this object/function pair. var closure = obj.__closures[funId]; if (closure) return closure; // Clear references to keep them out of the closure scope. obj = null; fun = null; // Create the closure, store in cache and return result. return __objs[objId].__closures[funId] = function () { return __funs[funId].apply(__objs[objId], arguments); }; }; We can now use the common pattern of creating event handlers inline: function attach() { var element = document.getElementById("my-element"); element.attachEvent("onclick", function() { alert("Clicked: " + this.innerHTML); }.closure(element)); } So now we have truly leak free closures. In addition we can also easily remove an object from the global array. The following code allows the garbage collector to free an object if there are no other references to it: window.__objs[obj.__objId] = null; Last updated: October 20, 2005 by laurens@vd.oever.nl
/
本文档为【合集:Javascript闭包[Closures]】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索