Book Image

Learning jQuery, Third Edition

Book Image

Learning jQuery, Third Edition

Overview of this book

To build interesting, interactive sites, developers are turning to JavaScript libraries such as jQuery to automate common tasks and simplify complicated ones. Because many web developers have more experience with HTML and CSS than with JavaScript, the library's design lends itself to a quick start for designers with little programming experience. Experienced programmers will also be aided by its conceptual consistency.Learning jQuery Third Edition is revised and updated for version 1.6 of jQuery. You will learn the basics of jQuery for adding interactions and animations to your pages. Even if previous attempts at writing JavaScript have left you baffled, this book will guide you past the pitfalls associated with AJAX, events, effects, and advanced JavaScript language features.Starting with an introduction to jQuery, you will first be shown how to write a functioning jQuery program in just three lines of code. Learn how to add impact to your actions through a set of simple visual effects and to create, copy, reassemble, and embellish content using jQuery's DOM modification methods. The book will step you through many detailed, real-world examples, and even equip you to extend the jQuery library itself with your own plug-ins.
Table of Contents (24 chapters)
Learning jQuery Third Edition
Credits
Foreword
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Memory leak hazards


JavaScript manages its memory using a technique known as garbage collection . This is in contrast to low-level languages such as C, which require programmers to explicitly reserve blocks of memory and free them when they are no longer being used. Other languages such as Objective-C assist the programmer by implementing a reference counting system, which allows the user to note how many pieces of the program are using a particular piece of memory so it can be cleaned up when no longer used. JavaScript is a high-level language, on the other hand, and generally takes care of this bookkeeping behind the scenes.

Whenever a new memory-resident item such as an object or function comes into being in JavaScript code, a chunk of memory is set aside for this item. As the object is passed around to functions and assigned to variables, more pieces of code begin to point to the object. JavaScript keeps track of these pointers , and when the last one is gone, the memory taken by the object is released. Consider a chain of pointers as shown in the following diagram:

Here object A has a property that points to B, and B has a property that points to C. Even if object A here is the only one that is a variable in the current scope, all three objects must remain in memory because of the pointers to them. When A goes out of scope, however (such as at the end of the function it was declared in), then it can be released by the garbage collector. Now B has nothing pointing to it, so can be released, and finally C can be released as well.

More complicated arrangements of references can be harder to deal with:

Now we have added a property to object C that refers back to B. In this case, when A is released, B still has a pointer to it from C. This reference loop needs to be handled specially by JavaScript, which must notice that the entire loop is isolated from the variables that are in scope.

Accidental reference loops

Closures can cause reference loops to be inadvertently created. As functions are objects that must be kept in memory, any variables they have in their closing environment are also kept in memory:

function outerFn() {
  var outerVar = {};
  function innerFn() {
    $.print(outerVar);
  }
  outerVar.fn = innerFn;
  return innerFn;
};

Here, an object named outerVar is created and referenced from within the inner function innerFn(). Then, a property of outerVar that points to innerFn() is created, and innerFn() is returned. This creates a closure on innerFn() that refers to outerVar, which in turn refers back to innerFn().

However, the loop can be even more insidious than this:

function outerFn() {
  var outerVar = {};
  function innerFn() {
    $.print('hello');
  }
  outerVar.fn = innerFn;
  return innerFn;
};

Here, we have changed innerFn(), so that it no longer refers to outerVar. However, this does not break the loop! Even though outerVar is never referred to from innerFn(), it is still in innerFn()'s closing environment . All variables in the scope of outerFn() are implicitly referred to by innerFn() due to the closure. Therefore, closures make it easy to accidentally create these loops.

The Internet Explorer memory leak problem

All of this is generally not an issue because JavaScript is able to detect these loops and clean them up when they become orphaned. Some versions of Internet Explorer, however, have difficulty handling one particular class of reference loops. When a loop contains both DOM elements and regular JavaScript objects, IE cannot release either one because they are handled by different memory managers. These loops are never freed until the browser is closed, which can eat up a great deal of memory over time. A common cause of such a loop is a simple event handler:

$(document).ready(function() {
  var button = document.getElementById('button-1');
  button.onclick = function() {
    $.print('hello');
    return false;
  };
});

When the click handler is assigned, this creates a closure with button in the closing environment. However, button now contains a reference back to the closure—the onclick property itself. Thus, the resulting loop cannot be released by Internet Explorer even when we navigate away from the page.

In order to release the memory, we would need to break the loop, such as by getting rid of the onclick property before the window is closed (taking care not to introduce a new loop between window and its onunload hander). Alternatively, we could rewrite the code to avoid the closure:

function hello() {
  $.print('hello');
  return false;
}
$(document).ready(function() {
  var button = document.getElementById('button-1');
  button.onclick = hello;
});

As the hello() function no longer closes over button, the reference only goes one way (from button to hello) and there is no loop and, therefore, no memory leak.

The good news

Now we will write the same code, but using normal jQuery constructs as follows:

$(document).ready(function() {
  var $button = $('#button-1');
  $button.click(function() {
    $.print('hello');
    return false;
  });
});

Even though a closure is still created, causing the same kind of loop as before, we do not get an IE memory leak from this code. Fortunately, jQuery is aware of the potential for leaks, and manually releases all of the event handlers that it assigns. As long as we faithfully adhere to using jQuery event binding methods for our handlers, we need not fear leaks caused by this particular common idiom.

This does not mean that we are completely out of the woods; we must continue to take care when we are performing other tasks with DOM elements. Attaching JavaScript objects to DOM elements can still cause memory leaks in Internet Explorer; jQuery just helps make this situation far less prevalent.

As a result of this, jQuery gives us another tool to help avoid these leaks. In Chapter 12, Advanced DOM Manipulation, we saw that the .data() method allows us to attach information to DOM elements in much the same way as we can with expando properties. Since this data is not stored directly as an expando (jQuery uses an internal map to store the data using IDs it creates), the reference loop is never formed and we sidestep the memory leak issue. Whenever an expando seems like a convenient data storage mechanism, we should consider .data() a safer alternative.