In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article is about what JavaScript closures are. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.
Introduction
Before discussing ECMAScript closures, let's introduce some basic definitions in functional programming (independent of the ECMA-262-3 standard). However, in order to better explain these definitions, let's take ECMAScript as an example.
As we all know, in functional languages (which ECMAScript also supports), functions are data. For example, functions can be stored in variables, passed as arguments to other functions, returned as return values, and so on. Such functions have special names and structures.
Define
Functional parameters ("Funarg")-parameters whose values are functions.
Examples are as follows:
Function exampleFunc (funArg) {funArg ();} exampleFunc (function () {alert ('funArg');})
In the above example, the argument to funArg is an anonymous function passed to exampleFunc.
Conversely, functions that accept functional parameters are called higher-order functions (high-order function for short: HOF). It can also be called: functional function or partial mathematical theory: operator function. In the above example, exampleFunc is such a function.
As mentioned earlier, a function can be used not only as an argument, but also as a return value. This kind of function with function as return value is called _ function with function value (functions with functional value or function valued functions).
(function functionValued () {
Return function () {
Alert ('returned function is called')
}
}) () (); / / be familiar with the direct execution of ().
Functions that can exist in the form of normal data (for example, when parameters are passed, accept functional parameters or return as function values) are called the first type of function (generally the first kind of object). In ECMAScript, all functions are first-class objects.
A function that accepts itself as an argument is called a self-application function (auto-applicative function or self-applicative function):
(function selfApplicative (funArg) {if (funArg & & funArg = selfApplicative) {alert ('self-applicative'); return;} selfApplicative (selfApplicative);}) ()
A function that returns its own value is called a self-replicating function (auto-replicative function or self-replicative function). Usually, the word "self-reproduction" is used in literary works:
(function selfReplicative () {
Return selfReplicative
) ()
Variables defined in functional parameters can be accessed when "funArg" is activated (because the variable object that stores the context data is created every time it enters the context):
Function testFn (funArg) {/ / activate funArg, local variable localVar can access funArg (10); / / 20 funArg (20); / / 30} testFn (function (arg) {var localVar = 10; alert (arg + localVar);})
However, we know that in ECMAScript, a function can be encapsulated in a parent function and can use variables in the context of the parent function. This feature can cause funArg problems.
FunArg problem
In a stack-oriented programming language, the local variables of a function are stored on the stack, and these variables and function parameters are pushed onto the stack whenever the function is activated.
When the function returns, these parameters are removed from the stack. This model has great restrictions on using a function as a function value (for example, returning from a parent function as a return value). In most cases, the problem occurs when the function has free variables.
A free variable is a variable used in a function, but it is neither a function parameter nor a local variable of the function.
As follows:
Function testFn () {var localVar = 10; function innerFn (innerParam) {alert (innerParam + localVar);} return innerFn;} var someFn = testFn (); someFn (20); / / 30
In the above example, for the innerFn function, localVar is a free variable.
For a system that uses a stack-oriented model to store local variables, it means that when the testFn function call ends, its local variables are removed from the stack. In this way, an error occurs when a function call is made to innerFn from the outside (because the localVar variable no longer exists).
Moreover, in the stack-oriented implementation model, it is simply impossible to return innerFn with a return value in the above example. Because it is also a local variable of the testFn function, it is also removed with the return of testFn.
There is also a problem with function objects when the system uses dynamic scope and functions are used as function parameters.
Look at the following example:
Var z = 10; function foo () {alert (z);} foo (); / 10-static scope and dynamic scope (function () {var z = 20; foo (); / 10-static scope, 20-dynamic scope}) (); / / the same is true for passing foo functions as parameters (function (funArg) {var z = 30; funArg () / / 10-static scope, 30-dynamic scope}) (foo)
We see that with dynamic scope, variable (identifier) processing is managed through the dynamic stack. Therefore, free variables are queried in the currently active dynamic chain, not in the static scope chain that is saved when the function is created.
This will lead to conflict. For example, even if Z still exists (as opposed to the previous example of removing variables from the stack), there is still a question: in different function calls, which value of Z is taken to the bottom (from which context, which scope is queried)?
The above describes two types of funArg problems-- depending on whether the function is returned with a return value (the first problem) and whether the function is used as a function argument (the second problem).
In order to solve the above problems, the concept of closure is introduced. Here comes the main point, and the veil of closure is unveiled.
Closure
A closure is a combination of a code block and the data in the context in which it was created.
Let's look at the following example:
Var x = 20; function foo () {alert (x); / / closure of the free variable "x" = = 20} / / foo fooClosure = {call: foo / / reference to a pair of functions lexicalEnvironment: {x: 20} / / query the context of the free variable}
In the above example, the "fooClosure" part is pseudocode. Correspondingly, in ECMAScript, the "foo" function already has an internal attribute-- the chain of scopes that create the function context.
Here "lexical" is self-evident and is usually omitted. The purpose of the above example is to emphasize that the contextual data is saved while the closure is created. The next time the function is called, the free variable can be found in the saved (closure) context, and as shown in the above code, the value of the variable "z" is always 10.
The more general word we use in the definition is "code block". However, we usually use functions that we often use (in ECMAScript). Of course, not all implementations of closures tie closures to functions. For example, in Ruby, closures can be: a program object (procedure object), a Ruby expression, or a block of code.
The stack-based implementation is obviously not applicable for the implementation to save the local variable after the context is destroyed (because it contradicts the stack-based structure). Therefore, in this case, the closure data of the upper scope is implemented by dynamically allocating memory (based on the "heap" implementation), using garbage collector (garbage collector referred to as GC) and reference counting (reference counting). This implementation performs less than a stack-based implementation, however, any implementation can always be optimized: you can analyze whether a function uses free variables, function parameters, or function values. and then decide on a case-by-case basis-whether to store the data in the stack or in the heap.
Implementation of ECMAScript closure
After discussing the theory section, let's take a look at how closures are implemented in ECMAScript. It is important to emphasize here that ECMAScript uses only static (lexical) scopes (while languages such as Perl can use both static and dynamic scopes for variable declaration).
Var x = 10; function foo () {alert (x);} (function (funArg) {var x = 20; / / the variable "x" of funArg is statically saved and funArg () is saved when the function is created; / / 10 instead of 20}) (foo)
From a technical point of view, the data of the upper context in which the function is created is stored in the function's internal property [[Scope]]. If you have a complete understanding of [[Scope]] and scope chains, then you have a complete understanding of closures.
According to the algorithm of function creation, we can see that in ECMAScript, all functions are closures, because they all save the scope chain of the upper context when they are created (except for exceptions) (regardless of whether the function will be activated later-- [Scope]] when the function is created):
Var x = 10; function foo () {alert (x);} / / foo is a closure foo: FunctionObject = {[[Call]]: code block of foo, [[Scope]]: [global: {x: 10}],. / / other properties}
As mentioned earlier, for optimization purposes, the implementation layer may not hold the upper scope chain when the function does not use free variables. However, this is not specified in the ECMAScript-262-3 standard; therefore, strictly speaking-- all functions are created with the upper-level scope chain in [[Scope]].
Omnipotent [[Scope]]
Here we have to say that the domain chain of JavaScript
Also note here that in ECMAScript, closures created in the same context share a [[Scope]] attribute. That is, changes made by one closure to its variables will affect the reading of its variables by other closures:
Var firstClosure; var secondClosure; function foo () {var x = 1; firstClosure = function () {return + + x;}; secondClosure = function () {return-- x;}; x = 2; / / an effect on AO ["x"], whose value is alert (firstClosure ()) in [[Scope]] of two closures; / / 3, through firstClosure. [[Scope]]} foo (); alert (firstClosure ()); / / 4 alert (secondClosure ()); / 3
Because of this feature, many people make a very common mistake: when you create a function in a loop and then bind the index value of the loop to each function, usually the result is not expected (the expectation is that each function will be able to get its own corresponding index value). This error is also the biggest error in the code mentioned in the title. Let's reveal why button clicks and pops up 5. 5.
Var data = []; for (var k = 0; k < 3; kits +) {data [k] = function () {alert (k);};} data [0] (); / / 3 instead of 0 data [1] (); / / 3 instead of 1 data [2] (); / / 3, not 2
The above example proves that closures created in the same context share a [[Scope]] attribute. So the variable "k" in the upper context can be easily changed.
ActiveContext.Scope = [... / higher variable objects {data: [...], k: 3} / / activation object]; data [0]. [[Scope]] = = Scope; data [1]. [[Scope]] = = Scope; data [2]. [Scope]] = Scope
In this way, when the function is activated, the final k used has become 3.
As shown below, creating an additional closure will solve this problem:
Var data = []; for (var k = 0; k < 3; data +) {data [k] = (function _ helper (x) {return function () {alert (x);};}) (k); / / pass the "k" value in} / / right now data [0] (); / 0 data [1] (); / / 1 data [2] (); / / 2
In the above example, after the function "_ helper" is created, it is activated with the parameter "k". The return value is also a function, which is stored in the corresponding array element. This technique has the following effect: each time the function is activated, "_ helper" creates a new variable object with the parameter "x", and the value of "x" is the value of the "k" passed in. In this way, the [[Scope] of the returned function looks like this:
Data [0]. [[Scope]] = = [. / / the AO of the upper context of the upper variable object: {data: [...], k: 3}, _ helper context AO: {x: 0}] Data [1]. [[Scope]] = = [. / / the AO of the upper context of the upper variable object: {data: [...], k: 3}, _ helper context AO: {x: 1}] Data [2]. [[Scope]] = = [. / / the AO of the upper context of the upper variable object: {data: [...], k: 3}, _ helper context AO: {x: 2}]
We see that at this point the function's [[Scope]] property has the really desired value, and to do this, we have to create additional variable objects in [[Scope]]. Note that in the returned function, if you want to get the value of "k", then the value will still be 3.
By the way, a large number of articles about JavaScript believe that only extra functions are closures, which is wrong (I almost got this wrong, thanks again to the author.) . Practice shows that this approach is the most effective, however, in theory, all functions in ECMAScript are closures.
However, the method mentioned above is not the only one. The correct value of "k" can also be obtained in other ways, as follows:
Var data = []; for (var k = 0; k < 3; arguments.callee.x +) {(data [k] = function () {alert (arguments.callee.x);}). X = k; / / storing "k" as an attribute of the function} / / is also feasible data [0] (); / 0 data [1] (); / / 1 data [2] (); / / 2
However, arguments.callee is removed from ECMAScript (ES5), so just learn about this method.
FunArg and return
Another feature is the return from the closure. In ECMAScript, the return statement in the closure returns the control flow to the calling context (the caller). In other languages, such as Ruby, there are many forms of closures, and the corresponding handling of closure returns are different, and the following ways are possible: they may be returned directly to the caller, or, in some cases, exit directly from the context.
The exit behavior of the ECMAScript standard is as follows:
Function getElement () {[1,2,3] .forEach (function (element) {if (element% 2 = = 0) {/ / returns the function "forEach" and / / does not return alert ('found:' + element); / / found: 2 return element;}}) from the getElement function; return null;} alert (getElement ()); / / null instead of 2
However, the following effects can be achieved with try catch in ECMAScript (this method is awesome!) :
Var $break = {}; function getElement () {try {[1,2,3] .forEach (function (element) {if (element% 2 = = 0) {/ / directly "return" alert ('found:' + element) from getElement; / / found: 2$ break.data = element; throw $break;}});} catch (e) {if (e = $break) {return $break.data;} return null;} alert (getElement ()); / 2 theoretical version
Often, programmers mistakenly assume that only anonymous functions are closures. In fact, this is not the case, as we can see-- it is because of the scope chain that all functions are closures (regardless of function type: anonymous functions, FE,NFE,FD are closures), except for one class of functions, which are created through the Function constructor, because its [[Scope]] contains only global objects. In order to better clarify this problem, we make two definitions of closures in ECMAScript (that is, two closures):
In ECMAScript, closure refers to:
In theory: all the functions. Because they all save the data in the upper context when they are created. This is true even for simple global variables, because accessing global variables in a function is equivalent to accessing free variables, which uses the outermost scope.
From a practical point of view, the following functions are considered closures:
♦ still exists even if the context in which it was created has been destroyed (for example, the internal function returns from the parent function)
♦ refers to free variables in the code
Closure practice
When actually used, closures can create a very elegant design that allows customization of a variety of computing methods defined on funArg. Here is an example of array sorting, which accepts a sort condition function as an argument:
[1,2,3] .sort (function (a, b) {
. / / sort criteria
});
The same example is the array map method (not all implementations support the array map method, and SpiderMonkey has supported it since version 1.6), which maps the original array to a new array according to the conditions defined in the function:
[1, 2, 3] .map (function (element) {
Return element * 2
}); / / [2,4,6]
Using functional parameters, you can easily implement a search method and support an infinite number of search conditions:
SomeCollection.find (function (element) {
Return element.someProperty = 'searchCondition'
});
There are also application functions, such as the common forEach method, that apply funArg to each array element:
[1, 2, 3] .forEach (function (element) {
If (element% 2! = 0) {
Alert (element)
}
}); / / 1,3
By the way, the apply and call methods of function objects can also be used as application functions in functional programming. Here, we think of them as application functions-functions that are applied to parameters (a list of parameters in apply and independent parameters in call):
(function () {
Alert ([] .join.call (arguments,';')); / / 1joint2ter3
}) .apply (this, [1,2,3])
Closures have another very important application-deferred invocation:
Var a = 10
SetTimeout (function () {
Alert (a); / / 10, one second later
}, 1000)
It can also be used for callback functions:
... Var x = 10; / / only for example xmlHttpRequestObject.onreadystatechange = function () {/ / it is called only when the data is ready; / / here, no matter in which context it is created, the value of the variable "x" already exists in alert (x); / / 10};.
It can also be used to encapsulate scopes to hide helpers:
Var foo = {}; / / initialization (function (object) {var x = 10; object.getX = function _ getX () {return x;};}) (foo); alert (foo.getX ()); / / get closured "x"-10 sample closures
Example 1: local variables in closures are references rather than copies
Function say667 () {/ / Local variable that ends up within closure var num = 666; var sayAlert = function () {alert (num);} num++; return sayAlert;} var sayAlert = say667 (); sayAlert ()
Therefore, the execution result should pop up 667 instead of 666.
Example 2: multiple functions are bound to the same closure because they are defined in the same function.
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;}} setupSomeGlobals (); / / assign gAlertNumber () to three global variables; / / 666 gIncreaseNumber (); gAlertNumber (); / / 667 gSetNumber (12); / / gAlertNumber (); / / 12
Example 3: when assigning functions in a loop, these functions are bound to the same closure
Function buildList (list) {var result = []; for (var I = 0; I < list.length; iTunes +) {var item = 'item' + list [I]; result.push (function () {alert (item +' + list [I]));} return result;} function testList () {var fnList = buildList ([1 Mei 2 and 3]); / / using j only to help prevent confusion-could use i for (var j = 0; j < fnList.length; jacks +) {fnList [j] () }}
The execution result of testList is to pop up the item3 undefined window three times, because the three functions are bound to the same closure, and the value of item is the result of the final calculation, but when I jumps out of the loop, the value of I is 4, so the result of list [4] is undefined. (the specific reason has been explained earlier.)
Example 4: all local variables of the external function are in the closure, even if the variable is declared after the internal function definition.
Function sayAlice () {var sayAlert = function () {alert (alice);} / / Local variable that ends up within closure var alice = 'Hello Alice'; return sayAlert;} var helloAlice=sayAlice (); helloAlice ()
The result of execution is a window that pops up "Hello Alice". Even if the local variable is declared after the function sayAlert, the local variable can still be accessed.
Example 5: create a new closure each time a function is called
Function newClosure (someNum, someRef) {/ / Local variables that end up within closure var num = someNum; var anArray = [1pje 2p3]; var ref = someRef; return function (x) {num + = x; anArray.push (num); alert ('num:' + num +'\ nanArray'+ anArray.toString () +'\ nref.someVar'+ ref.someVar);} closure1=newClosure (40, {someVar:'closure 1'}); closure2=newClosure (1000, {someVar:'closure 2'}); closure1 (5) / / num:45 anArray [1rem 2jue 3je 45] ref:'someVar closure1' closure2 (- 10); / / num:990 anArray [1meme 2m 3990] ref:'someVar closure2'
Singleton single piece:
Var singleton = function () {var privateVariable; function privateFunction (x) {... privateVariable... } return {firstMethod: function (a, b) {... privateVariable... }, secondMethod: function (c) {... privateFunction (). }}
This singleton is implemented through closures. Private members and methods are encapsulated through closures. The anonymous main function returns an object. The object contains two methods, method 1 can method private variables, and method 2 interrogates internal private functions. It is important to note that the'()'at the end of the anonymous main function cannot be generated without this'()'. Because anonymous functions can only return unique objects and cannot be called elsewhere. This is the method of using closures to generate singletons.
Thank you for reading! This is the end of this article on "what is a JavaScript closure". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it out for more people to see!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.