What are closures?

What are closures?

Rate This
  • Comments 23

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;
  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);
  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:

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

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.

  • 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.
  • I'm confused... I thought that the definition of "functional programming" was that everything is accomplished by application of functions to existing values, thus producing new values -- the most obvious feature of such a language being the lack of "destructive" operators. The definition at the "Functional Programming FAQ" (see http://www.cs.nott.ac.uk/~gmh//faq.html#functional-languages) seems to agree. But Eric defines a functional language as one that has functions as first-class objects. This seems like a totally different issue. In fact, it seems like first-class functions aren't too rare in languages that wouldn't usually be considered "functional." Am I missing something?
  • 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?
  • 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 xs just 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 = x this 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.
  • Given the content of this discussion,I think it's appropriate to mention the BeyondJS JavaScript library developed by Sjoerd Visscher and myself. You can find it at http://w3future.com/html/beyondJS/ (a slightly dated version, I will upload a newer version any day now). Not your standard run-of-the-mill JavaScript library, BeyondJS targets the JavaScript language itself. Building on JavaScript's intrinsic functional capabilities it adds loads of features that transform it into a truly functional programming language. Not a pure functional language but a functional language non-the-less. Here are some of the features BeyondJS provides: 1. currying 2. functional composition 3. list comprehensions 4. lazy lists (lists whose members are lazy evaluated) 5. streams a lots more. For example, lets implement the example provided by elias using JavaScript with BeyondJS: (1).to(10).collect("^".curry(2)).filter("<".curry(5)); Actually, (1).to(10) computes the JavaScript Array [1,2,3,4,5,6,7,8,9,10]. If you want a lazy list you would use (1).lazy(10) instead. Speaking of lazy lists, BeyondJS even supports recursive list definitions. Consider this Haskell definition Fibonacci sequence: fibs = 0 : 1 : (zipWith (+) fibs (tail fibs)) Using BeyondJS you get the slightly more verbose: var fibs = (0).lazy(1); fibs.extend(fibs.zipWith("+", fibs.tail()); Summing the integers from 1 to 100: var sum = (1).lazy(100).fold("+"); A nice thing about BeyondJS is that it works with IE5+ (most of the stuff works in IE4 as well), with Mozilla and with Rhino. Here is another cool example which works both using WSH and Rhino: File.StdIn.collect(function(line) { return line.split(",").reverse(); }).feed(File.StdOut); This code reads coma delimited data from the standard input and outputs it to the standard output with the column order reversed (note: requires a version of BeyondJS not yet online). Because File.StdIn is a lazy sequence of the input lines, the data is never wholly stored in memory. Suppose I only want to retain lines the contain the word 'good', regardless of case: File.StdIn.filter(/good/i).collect(function(line) { return line.split(",").reverse(); }).feed(File.StdOut); Note that while JavaScript is obviously not a pure functional language, you can use BeyondJS to write fully functional programs (pun intended) without any variables.
  • The function literal is very handy when defining neat Objects. function Car() { this.broom = function(loud) { } }
  • General

    http://www.jnd.org/dn.pubs.html&amp;nbsp;- a collection of essays on various topics from design...
  • PingBack from https://bigxi.wordpress.com/2006/03/28/ie-memory-leak-revisited/
  • 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..

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

  • PingBack from http://sudarmuthu.com/blog/2005/06/23/is-firefox-104-unstable.html

  • We're getting hung up on the stack management aspects of recursive programming. Why do we need a stack

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

  • 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.

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

Page 1 of 2 (23 items) 12