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

  • 不得不说Eric Lippert同志疾呼的:Don't use closures unless you really need closure semantics. In most cases, non-nested functions are the right way to go. 是非常消极的应付之辞。今天关于IE内存泄漏的文章已有很多,而且很大部分就是微软的自己人在解释,甚至象Eric Lippert这样引擎开发组的成员。但是他们的文章始终没有正面承认其实这就是IE的bug,而且是非常严重的bug,这事情其实完全不关脚本引擎对象和DOM对象的事。就是微软对产品不负责任的表现,不说IE4,1997那个春天那是太遥远了点,但是IE6也是2001年随xp发布的。使用COM可以给他们的开发带来很多便利,当然也利用很多现成的东西,可是居然在带来这样的严重问题后,他们却把大部分责任归咎于不合理和不正确的使用Closures技术!对于循环引用产生Memory..

  • PingBack from http://czsilence.yo2.cn/articles/javascript-memory-leak-4.html

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

  • Isn't this a bit head in the sand? Rather than fix your memory leak problems, you ask people not to use a fundamental and powerful part of JavaScript (or the hacked JScript). No other browsers seem to suffer in this way.

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

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

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

  • PingBack from http://www.yaohaixiao.com/?p=256

Page 2 of 2 (23 items) 12