Closure Inspector

From FirebugWiki

(Difference between revisions)
Jump to: navigation, search
m (Using the DOM Panel: just correct the layout of the pictures)
(Add some examples)
Line 78: Line 78:
Watch panel allows you to access to closure variables both via an expression as [[#Using_the_Command_Line|for the Command Line]] and via object browsing as [[#Using_the_DOM_panel|for the DOM Panel]] (you would also need to enable the "Show Closures" option).
Watch panel allows you to access to closure variables both via an expression as [[#Using_the_Command_Line|for the Command Line]] and via object browsing as [[#Using_the_DOM_panel|for the DOM Panel]] (you would also need to enable the "Show Closures" option).
 +
 +
== Tips ==
 +
 +
=== Prevent the browser to optimize away variables ===
 +
The browser optimizes away variables that are not used anymore. Firebug can't obviously prevent that behaviour (that would be lots of memory leaks!).
 +
 +
If you do need to keep one of these references to watch its value, ''eval("")'' do the job (for once its use can really be justified...). Here is an example:
 +
 +
<source lang="javascript">
 +
function modularAddition(start, modulo)
 +
{
 +
var res = start % modulo;
 +
 +
// prevents ''start'' to be optimized away:
 +
eval("");
 +
 +
return {
 +
add: function(addition)
 +
{
 +
return res = (res + addition) % modulo;
 +
}
 +
};
 +
}
 +
 +
var myModularAddition = modularAddition(42, 30);
 +
myModularAddition.add(20);
 +
 +
// using the command line:
 +
myModularAddition.%start; // ==> 42 (would print "(optimized away)" without eval(""))
 +
</source>
 +
 +
=== Shorthand ===
 +
 +
Considering the [[#Basic_example|basic example]], in order to access to ''name'', you type the following:
 +
<source lang="javascript">
 +
someone.introduce.%name;
 +
</source>
 +
 +
Closure Inspector proposes a shorthand: if you type ''someone.%name'', it will lookup the first closure ''someone'' has (i.e. ''someone.introduce''), and then request the closure variable. In other words, these two syntax are equivalent:
 +
<source lang="javascript">
 +
someone.%name;          // ==> "Arthur"
 +
someone.introduce.%name; // ==> "Arthur"
 +
</source>
 +
== Examples ==
 +
 +
This part gives examples to show cases where Closure Inspector is a helpful tool.
 +
 +
=== Getters / Setters (Functions) ===
 +
If you're familiar with Object Oriented Programmation, you often deal with encapsulation, in particular with getters / setters.
 +
 +
But sometimes, you also need to access to private variables while debugging:
 +
 +
<source lang="javascript">
 +
function Person(name, birthDate)
 +
{
 +
this.getName = function()
 +
{
 +
return name;
 +
};
 +
 +
this.getAge = function()
 +
{
 +
return new Date().getYear() - birthDate;
 +
}
 +
};
 +
 +
var arthur = new Person("Arthur", new Date("1970/01/01"));
 +
 +
// now you can't access to the birth date anymore, you can only get the age...
 +
// but using the command line:
 +
arthur.%birthDate.toString(); // ==> "Sun Feb 01 1970 00:00:00 GMT+0100 (CET)"
 +
</source>
 +
 +
=== Getters / Setters (Properties) ===
 +
 +
Same as above, but using properties:
 +
 +
<source lang="javascript">
 +
function Person(name, birthDate)
 +
{
 +
Object.defineProperty(this, "name", {
 +
get: function(){ return name }
 +
});
 +
 +
Object.defineProperty(this, "age", {
 +
get: function()
 +
{
 +
return new Date().getYear() - birthDate;
 +
}
 +
});
 +
};
 +
 +
var arthur = new Person("Arthur", new Date("1970/01/01"));
 +
</source>
 +
 +
Unfortunately, you can't access to birthDate using ''arthur.age.%birthDate''.
 +
 +
To get the ''birthDate'' variable, you'd need to do as below:
 +
<source lang="javascript">
 +
var descriptor = Object.getOwnPropertyDescriptor(arthur, "age");
 +
descriptor.%birthDate;
 +
 +
// or the equivalent, using the shorthand:
 +
arthur.%birthDate;
 +
</source>
 +
 +
=== Counters ===
 +
In some case, we would like to access to the closure variables without calling the function which uses it. Closure Inspector allows you to do so:
 +
<source lang="javascript">
 +
var count = (function ()
 +
{
 +
var counter = 0;
 +
return function()
 +
{
 +
return ++counter;
 +
};
 +
})();
 +
 +
// calling count() increaments the counter
 +
count(); // ==> 1
 +
// if you don't want to increment it:
 +
count.%counter; // ==> 1
 +
</source>

Revision as of 16:33, 3 February 2013

Closure inspector is a feature available from Firebug 1.11.2 that aims at accessing JavaScript closure variables.

Contents

Closures

This section aims at defining what are closures. If you know them, then jump to Closure Inspector.

Definition

You might know what are closures without knowing how to name them.

Wikipedia says:

In computer science, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables) of that function. A closure—unlike a plain function pointer—allows a function to access those non-local variables even when invoked outside of its immediate lexical scope.

In other words, the closures combines both the function and its referencing environment, which stores the references of the variables declared in its parent scopes. So when the function is called, it can access to variables wherever the call site is.

If the concept of closure is still vague, just go on to the below example.

Basic example

Here is a basic example of the creation of a closure:

function Person()
{
	var name = "Arthur";
 
	this.introduce = function()
	{
		console.log("Hello, my name is " + name);
	}
}
 
// create an instance of Person:
var someone = new Person();
 
// we call a closure !
someone.introduce(); // prints "Hello, my name is Arthur"

someone.introduce is a closure because this function uses a variable outside its own scope (you must have noticed there was no name variable inside the introduce method).

But where someone.introduce is called in the above example (i.e. its call site), the name variable cannot be accessed... except with specific tools like Closure Inspector.

Closure Inspector

Using the Command Line

The syntax to access the closure variables is as follow:

closure.%variable

In the above example, you would access to name using this expression:

someone.introduce.%name

Which returns: "Arthur".

Note that Closure Inspector also lets you change values too:

someone.introduce.%name = "Trillian";
someone.introduce(); // ==> "Hello, my name is Trillian"

Using the DOM Panel

In order to go through closures in the DOM panel, you need to activate the "Show Closure" option:

Activate Show Closure.png


Then you can access to closure variables by expanding the function properties and (closures), as below:

Access Closure Variables.png

Using the Watch Panel

Watch panel allows you to access to closure variables both via an expression as for the Command Line and via object browsing as for the DOM Panel (you would also need to enable the "Show Closures" option).

Tips

Prevent the browser to optimize away variables

The browser optimizes away variables that are not used anymore. Firebug can't obviously prevent that behaviour (that would be lots of memory leaks!).

If you do need to keep one of these references to watch its value, eval("") do the job (for once its use can really be justified...). Here is an example:

function modularAddition(start, modulo)
{
	var res = start % modulo;
 
	// prevents ''start'' to be optimized away:
	eval("");
 
	return { 
		add: function(addition)
		{
			return res = (res + addition) % modulo;
		}
	};
}
 
var myModularAddition = modularAddition(42, 30);
myModularAddition.add(20);
 
// using the command line:
myModularAddition.%start; // ==> 42 (would print "(optimized away)" without eval(""))

Shorthand

Considering the basic example, in order to access to name, you type the following:

someone.introduce.%name;

Closure Inspector proposes a shorthand: if you type someone.%name, it will lookup the first closure someone has (i.e. someone.introduce), and then request the closure variable. In other words, these two syntax are equivalent:

someone.%name;           // ==> "Arthur"
someone.introduce.%name; // ==> "Arthur"

Examples

This part gives examples to show cases where Closure Inspector is a helpful tool.

Getters / Setters (Functions)

If you're familiar with Object Oriented Programmation, you often deal with encapsulation, in particular with getters / setters.

But sometimes, you also need to access to private variables while debugging:

function Person(name, birthDate)
{
	this.getName = function()
	{
		return name;
	};
 
	this.getAge = function()
	{
		return new Date().getYear() - birthDate;
	}
};
 
var arthur = new Person("Arthur", new Date("1970/01/01"));
 
// now you can't access to the birth date anymore, you can only get the age...
// but using the command line:
arthur.%birthDate.toString(); // ==> "Sun Feb 01 1970 00:00:00 GMT+0100 (CET)"

Getters / Setters (Properties)

Same as above, but using properties:

function Person(name, birthDate)
{
	Object.defineProperty(this, "name", {
		get: function(){ return name }
	});
 
	Object.defineProperty(this, "age", {
		get: function()
		{
			return new Date().getYear() - birthDate;
		}
	});
};
 
var arthur = new Person("Arthur", new Date("1970/01/01"));

Unfortunately, you can't access to birthDate using arthur.age.%birthDate.

To get the birthDate variable, you'd need to do as below:

var descriptor = Object.getOwnPropertyDescriptor(arthur, "age");
descriptor.%birthDate; 
 
// or the equivalent, using the shorthand:
arthur.%birthDate;

Counters

In some case, we would like to access to the closure variables without calling the function which uses it. Closure Inspector allows you to do so:

var count = (function ()
{
	var counter = 0;
	return function()
	{
		return ++counter;
	};
})();
 
// calling count() increaments the counter
count(); // ==> 1
// if you don't want to increment it:
count.%counter; // ==> 1
Personal tools