JavaScript Closures for Dummies
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:
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:
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 from sayHello2(). Notice that the code that we call references the variable text, which was a local variable of the function sayHello2().
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 of sayHello2() 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!
// 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 to 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 old gAlertNumber, 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.
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:
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 fori and item within that one closure (where i has a value of 3 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 becausealice 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.
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.
// 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 callingFunction()).
- 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.
The original article is in : http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies
相关推荐
secrets_of_javascript_closures.pdf
《Programming Paradigms for Dummies》一书由Peter Van Roy撰写,旨在为具备一定编程经验的读者提供一个全面且深入的理解编程范式的起点。书中介绍了各种编程范式的基础概念、它们之间的联系以及如何选择合适的范式...
JavaScript,作为一种广泛应用于Web开发的脚本语言,其高级特性如闭包(closures)、原型(prototype)和继承(inheritance)是理解其精髓的关键。本文将深入探讨这些概念,帮助开发者更好地掌握JavaScript的核心。 ...
If you are a JavaScript developer interested in learning functional programming, looking for the quantum leap toward mastering the JavaScript language, or just want to become a better programmer in ...
根据提供的文件内容,我们可以提炼出关于《JavaScript Functional Programming for JavaScript Developers》这本电子书的知识点如下: 1. 函数式编程概念:函数式编程是一种编程范式,强调通过函数来编写代码。它的...
深入理解JavaScript系列(16):闭包(Closures) 深入理解JavaScript系列(17):面向对象编程之一般理论 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现 深入理解JavaScript系列(19):求值策略...
Classes, closures, persistence, Flash, and JavaScript embedded in Java applications Part I explains the core JavaScript language in detail. If you are new to JavaScript, it will teach you the ...
深入理解JavaScript系列(16):闭包(Closures) 深入理解JavaScript系列(17):面向对象编程之一般理论 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现 深入理解JavaScript系列(19):求值策略...
Get a run through of the basic language constructs, Functions, and Closures of JavaScript Code using the powerful object-oriented feature in JavaScript Master DOM manipulation, cross-browser ...
scope-chains-closures, Javascript作用域链和闭包 workshop 范围链和闭包 workshop正在启动$ npm install -g scope-chains-closures$ scope-chains-closures # or, shorter: sccjs使用箭头
JavaScript has become one of the most widely used—and essential—programming languages for the Web, on both the client-side and server-side. In the real world, JavaScript applications are fragile, ...
Classes, closures, persistence, Flash, and JavaScript embedded in Java applications Part I explains the core JavaScript language in detail. If you are new to JavaScript, it will teach you the ...
- Get familiar with the Functions and Closures of JavaScript - Explore Regular Expressions in JavaScript - Code using the powerful object-oriented feature in JavaScript - Test and debug your code ...
1. **闭包(Closures)**:闭包是一种特殊的函数,它能够记住并访问其定义时的作用域,即使这个作用域在函数执行时已经不存在了。利用闭包可以实现数据封装和私有变量,是JavaScript中实现模块化的重要手段。 2. **...
同时,合理利用函数作用域和闭包(closures)能够减少全局变量的污染,进而提高代码的执行速度。 在性能调优的过程中,作者还提到了JavaScript引擎的工作原理,包括如何编译和执行JavaScript代码。理解这些底层细节...
If you want to write ... If you’re familiar with concepts such as closures and prototypal inheritance, you’ll be able to determine why some patterns may be more suitable for your projects than others.
Variables: Scopes, Environments, and Closures Chapter 17. Objects and Inheritance Chapter 18. Arrays Chapter 19. Regular Expressions Chapter 20. Dates Chapter 21. Math Chapter 22. JSON Chapter 23. ...