Udostępnij za pośrednictwem


What are closures?

JScript, as I noted yesterday, is a functional language. That doesn't mean that it works particularly well (though I hope it does) but rather that it treats functions as first-class objects. Functions can be passed around and assigned to variables just as strings or integers can be.

A reader commented yesterday that "closures are your friends". Well, actually there are important situations where closures are NOT your friends! Let's talk a bit about those. First off, what's a closure?

Consider the following (contrived and silly, but pedagocially clear) code:

function AddFive(x) {
return x + 5;
}

function AddTen(x) {
return x + 10;
}

var MyFunc;
if (whatever)
MyFunc = AddFive;
else
MyFunc = AddTen;
print(MyFunc(123)); // Either 133 or 128.

Here we have a typical functional scenario. We're deciding which function to call based on some runtime test. Now, one could imagine that you'd want to generalize this notion of an "adder function", and you would not want to have to write dozens and dozens of adders. What we can do is create an adder factory:

function AdderFactory(y) {
return function(x){return x + y;}
}

var MyFunc;
if (whatever)
MyFunc = AdderFactory(5);
else
MyFunc = AdderFactory(10);
print(MyFunc(123)); // Either 133 or 128.

The anonymous inner function remembers what the value of y was when it was returned, even though y has gone away by the time the inner function is called! We say that the inner function is closed over the containing scope, or for short, that the inner function is a closure.

This is an extremely powerful functional language feature, but it is important to not misuse it. There are ways to cause memory-leak-like situations using closures. Here's an example:

<body>
<div class='menu-bar' id='myMenu'></div>
<script language='javascript'>
var menu = document.getElementById('myMenu');
AttachEvent(menu);
function AttachEvent(element) {
element.attachEvent( "onmouseover", mouseHandler);
function mouseHandler(){ /* whatever */ }
}
</script></body>

Someone has, for whatever reason, nested the handler inside the attacher. This means that the handler is closed over the scope of the caller; the handler keeps around a reference to element which is equal to menu, which is that div. But the div has a reference to the handler.

That's a circular reference.

Now, the JScript garbage collector is a mark-and-sweep GC so you'd think that it would be immune to circular references. But the IE div isn't a JScript object; it is not in the JScript GC, so the circular reference between the div and the handler will not be broken until the browser completely tears down the div.

Which never happens.

This page used to say that IE tears down the div when the page is navigated away, but it turns out that that's not right. Though IE did briefly do that, the application compatibility lab discovered that there were actually web pages that broke when those semantics were implemented. (No, I don't know the details.) The IE team considers breaking existing web pages that used to work to be way, way worse than leaking a little memory here and there, so they've decided to take the hit and leak the memory in this case.

Don't use closures unless you really need closure semantics. In most cases, non-nested functions are the right way to go.

Comments

  • Anonymous
    September 17, 2003
    For the record, the 'closures are you friend' comment was meant to be humor and not a blanket recommendation.One unexpected place I have found JScript's closures particularly useful is in breaking up long calculations that have a lot of state on the stack. You can't pump messages manually in the browser but you can window.setTimeout(function() { /* next block of code */ }, 0) which permits you progress bar to repaint. No memory is leaked in this scenario.Obviously this is not the sort of use of closures one sees in a more traditionally functional language, but it solves an otherwise intractable problem.

  • Anonymous
    September 18, 2003
    The comment has been removed

  • Anonymous
    September 18, 2003
    Look at it this way: functional programming is as you define it. A "functional language" is a language in which one can do functional programming. Therefore JScript is a functional language. It is also a procedural language and an object oriented language, as it supports those programming styles as well.The fact that JScript allows you to do non-functional programming doesn't, by the definition above, make it not a functional language. After all, you can write non-pure-functional programs in Scheme too.Does that make more sense?

  • Anonymous
    September 19, 2003
    It is not clear what to call functional programming. Scheme may be considered an imperative language, because it allows assignment and other imperative features like i/o "functions". On the other hand languages like Miranda, or Haskell are pure functional languages, because of the lack of states (introduced by assignment, and imperative procedures) semantics is clear. More over those languages are lazy, it means they do not perform a computation if it is not needed, it is an advantage see the next pseudocode f(x,y) = if x > 5 then x else y g(x,y) = f(x,x/y) h = g(10,0)What is the value of h? 10 of course, try this in Scheme or Java or any eager (non lazy) language, and you get a division by 0 error exception (another imperative feature) the reason is that 10/0 is never computed in a lazy language, the if-then-else function is lazy in many languages (test?thenpart:elsepart in C).Other important feature in functional languages is that you can build programs just by function composition.Not by applications, it is you do not need variables. h = filter (>5) . map (^2) h [1..10]computes: [9,16,25,36,49,64,81,100] [1..10] is the list [1,2,3,4,5,6,7,8,9,10] may be defined (in a lisp style) as filter test list = if (null list) then [ ] else if test (hd list) then (hd list):filter test (tl list) else filter test (tl list) but functional languages allow pattern matching in definitions it is shorter and more clear filter test [ ] = [ ] filter test (x : xs) = if (test x) then x : filter test xs else test xsjust write a case for every constructor (for lists [], and : (cons) ). and map is defined by: map f [ ] = [ ] map f (x : xs) = f x : map f xs(>5) in Lisp/Scheme you would write (lambda (x)(> x 5))and (lambda(x)(^ x 2)) instead of (^2), you may have noticed the absence of parethesis in function arguments map f list instead of map (f, list) the reason is that functions are in curried form (curried in honor of Haskell Brooks Curry, founder of combinatory logic) it means they take one argument at a time, for that reason(1 +) denotes a function expecting a number calculating its sucessor. the dot is functional composition you use it in mathematics. ( f . g ) x = f ( g x )it is possible, but not practical, to never use variables, more over you can build every function with just 2 elementary funtions S and K defined as follows K x y = x and S x y z = x y ( x z )combining SKK you get I the identity function I x = xthis is of theoretical interest, but also practical interest, Erik said "closures are your friends", well one may say "thunks are your friends" instead, thunks are closure like structures used to implement lazy evaluation, the other way to imlement it is by transforming the funtion into elementary functions or combinators SKI would be enough but David Turner (also the author of Miranda) developed a better set known as Turner's combinators.I can't see the lenght of this comment, but I think it is large enough, so my conclusion:Higher Order Functions in Java/JavaScript is a good thing. Although they use closures not thunks, but is ok for a non lazy language. The lack of lists in imperative languages (you can define your data type i know) may limit its use because problems with stack overflow due to parameter passing semantics on those languages and the lack of efficient garbage collector.The lack of curried functions (although you may define a curried version with higher order functions) do not encourage to think programs as function composition, it encourage the applicative style (Scheme has the same limitant (I am a radical pure functional programer)).You want to write functional programs, then it would be better to embed a functional language in browsers, otherwise the patch to imperative laguages wont be easy to use. However is a nice feature to have higher order functions, no doubt. But not enough.

  • Anonymous
    September 21, 2003
    The comment has been removed

  • Anonymous
    September 23, 2003
    The function literal is very handy when defining neat Objects.function Car() { this.broom = function(loud) { }}

  • Anonymous
    June 09, 2005
    General

    http://www.jnd.org/dn.pubs.html&amp;nbsp;- a collection of essays on various topics from design...

  • Anonymous
    March 28, 2006
    PingBack from https://bigxi.wordpress.com/2006/03/28/ie-memory-leak-revisited/

  • Anonymous
    April 01, 2007
    The Evolution of the Web Developer In the past, memory leaks haven't posed huge problems for Web developers. Pages were kept relatively simple and navigation between different locations within a site was a great way to clean up any loose memory. If there..

  • Anonymous
    June 01, 2007
    PingBack from http://amref.wordpress.com/2007/06/01/fabulous-adventures-in-coding/

  • Anonymous
    August 22, 2007
    PingBack from http://sudarmuthu.com/blog/2005/06/23/is-firefox-104-unstable.html

  • Anonymous
    September 06, 2007
    The comment has been removed

  • Anonymous
    September 15, 2007
    在接下来的内容中,我们会讨论内存泄露方式,并为每种方式给出示例。其中一个重要的示例是JScript中的Closure技术,另一个示例是在事件执行中使用Closures。当你熟悉本示例后,你就能找出并修改你已有的大多数内存泄漏问题,但是其它Closure相关的问题可能又会被忽视。

  • Anonymous
    September 18, 2007
    Closers are a wonderful concept that works very well on all other languages that have it, even server side JavaScript. Too bad the implementers of JavaScript from browser makers have decided that garbage collection is done on two layers, one from page DOM and one from the JavaScript engine, so from what could be a great tool in making better code in a real functional way, we have to write procedural code because the language implementers messed up.

  • Anonymous
    February 26, 2008
    PingBack from http://journal.suteki.nu/2008/02/26/scheme-programming-guides/

  • Anonymous
    April 11, 2008
    The comment has been removed

  • Anonymous
    May 12, 2008
    PingBack from http://czsilence.yo2.cn/articles/javascript-memory-leak-4.html

  • Anonymous
    June 10, 2008
    mootools / prototype libraries heavily use closures, seems to be working ok on IE.

  • Anonymous
    October 28, 2008
    The comment has been removed

  • Anonymous
    October 28, 2008
    Dave, I wrote this article over five years ago. Those problems have been fixed in that time. However, I still stand by my advice. Using closure semantics unnecessarily adds complexity to the analysis of a program that many people do not intend or fully understand, and that's badness.

  • Anonymous
    February 08, 2009
    A disclaimer first: This post will make sense to you only if you have read and understood my post on

  • Anonymous
    March 06, 2009
    Why does the circular reference matter here? The browser never tears down the div, the reference to the function remains anyway, the GC cannot collect. In which point did the reference the function had to the div matter at all?

  • Anonymous
    April 08, 2009
    PingBack from http://www.yaohaixiao.com/?p=256