user3365609
user3365609

Reputation:

Clarification regrading circular reference

I don't think I quite understand this concept.

I get how having stuff that require each other leads to a infinite loop but I don't see how this happens in some examples I view

function setHandler() {  
  var elem = document.getElementById('id')

  elem.onclick = function() {
    // ...
  }
}

How is this a problem? I just see a element being attached a function on click.

Screenshot

It says there's a reference through the outer LexicalEnvironment - but isn't this occurring once?

Thanks in advance.

Upvotes: 1

Views: 61

Answers (2)

Tomalak
Tomalak

Reputation: 338228

JavaScript uses something called lexical scoping. Scopes

  • define what variables are visible in a certain context (corollary: they define what variables are no longer visible to anyone and may be garbage collected safely)
  • are created for every function (every time you use the keyword, a new scope is created)
  • can be nested (because functions can be nested)
  • are persisted for every function (when you call a function it is guaranteed that it can see all variables in all scopes (!) that were available when it was created)

That means:

                                            // +- global scope
                                            // |
function setHandler() {                     // |+- scope for setHandler()
                                            // ||  sees: own, global
  var elem = document.getElementById('id'); // ||
                                            // ||
  elem.onclick = function() {               // ||+- Scope for anonymous function
    // ...                                  // |||  sees: own, setHandler, global
  }                                         // |||
}                                           // ||
                                            // |

Now the anonymous function you assign to onclick can see the variables elem and setHandler and everything from the global scope.

If the element is destroyed (removed from the DOM), the garbage collector considers what variables went out of scope. A less advanced garbage collector might do this by keeping a reference count for any in-memory object and clean up that object (free the memory) when the reference count reaches zero.

Now the anonymous click handler function cannot be cleaned up because it holds a reference to elem (via the scope) which in turn holds a reference to the anonymous function (via its onclick property). The reference count for both objects stays at 1 because the two refer to each other.

Note point 4 from above: Scopes are persistent, so the "lexical environment" of setHandler() is kept alive even though program execution has already left that function - because it is needed by the anonymous inner function. (This is called "closure" and is one of the cornerstones of JS.)

Modern garbage collectors can spot that situation and clean up the event handler appropriately, but notably old versions if IE misbehaved and leaked the memory.

Upvotes: 0

RobG
RobG

Reputation: 147403

How is this a problem?

It was a major problem in early versions of IE 6 as it created "memory leaks" because circular references involving DOM elements led to memory not being released when a page was unloaded, so they consumed more and more memory, making the browser less responsive. There's a good article here.

In regard to the particular pattern in the OP, the circular reference is easily avoided:

function setHandler() {  
  var elem = document.getElementById('id')

  elem.onclick = function() {
    // ...
  }

  // Break circular reference
  elem = null;
}

Upvotes: 1

Related Questions