Closure Inspector

From FirebugWiki

(Difference between revisions)
Jump to: navigation, search
(first draft)
(second draft)
 
(2 intermediate revisions not shown)
Line 1: Line 1:
-
Closure inspector is a feature available from Firebug 1.11.2 that aims at accessing JavaScript ''closure'' variables.
+
Closure inspector is a feature available from Firebug 1.11.2 that makes it possible to access JavaScript ''closure'' variables.
== Closures ==
== Closures ==
-
This section aims at defining what are closures. If you know them, then jump to [[#Closure_Inspector|Closure Inspector]].
+
If you already know what closures are, skip to [[#Closure_Inspector|Closure Inspector]].
=== Definition ===
=== Definition ===
-
You might know what are closures without knowing how to name them.
+
You might have seen or even used closures without knowing the formal definition.
[http://en.wikipedia.org/wiki/Closure_%28computer_science%29 Wikipedia says]:
[http://en.wikipedia.org/wiki/Closure_%28computer_science%29 Wikipedia says]:
<blockquote>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.</blockquote>
<blockquote>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.</blockquote>
-
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.
+
In other words, a closure combines both the function and its ''containing environment'', and stores references to the variables declared in its parent scopes. When the function is called, it can access those variables regardless of where the call site is.
-
If the concept of closure is still vague, just go on to the below example.
+
Take a look at how this looks in JavaScript:
=== Basic example ===
=== Basic example ===
-
Here is a basic example of the creation of a closure:
+
Here is a basic example of a closure:
<source lang="javascript">
<source lang="javascript">
-
function Person()
+
function Person(name)
{
{
-
var name = "Arthur";
+
    this.introduce = function()
-
+
    {
-
this.introduce = function()
+
        console.log("Hello, my name is %s", name);
-
{
+
    }
-
console.log("Hello, my name is " + name);
+
-
}
+
}
}
// create an instance of Person:
// create an instance of Person:
-
var someone = new Person();
+
var someone = new Person("Arthur");
-
// we call a closure !
+
// we call a closure!
someone.introduce(); // prints "Hello, my name is Arthur"
someone.introduce(); // prints "Hello, my name is Arthur"
</source>
</source>
-
''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).
+
''someone.introduce'' is a closure because this function uses a variable outside its own scope (notice that there is 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.
+
It is notable that, where ''someone.introduce'' is called in the above example (i.e. its call site), the ''name'' variable can not be accessed by any regular means. This is where the Closure Inspector comes in.
== Closure Inspector ==
== Closure Inspector ==
Line 50: Line 48:
</source>
</source>
-
In the [[#basic_example|above example]], you would access to ''name'' using this expression:
+
In the [[#Basic_example|above example]], you would access ''name'' using this expression:
<source lang="javascript">
<source lang="javascript">
-
someone.introduce.%name
+
someone.introduce.%name // ==> "Arthur"
</source>
</source>
-
Which returns: "Arthur".
+
Note that you can also change values:
-
 
+
-
Note that Closure Inspector also lets you change values too:
+
<source lang="javascript">
<source lang="javascript">
someone.introduce.%name = "Trillian";
someone.introduce.%name = "Trillian";
-
someone.introduce(); // ==> "Hello, my name is Trillian"
+
someone.introduce(); // prints "Hello, my name is Trillian"
</source>
</source>
-
=== Using the DOM Panel ===
+
The same syntax works everywhere in Firebug where JavaScript is required, such as in the [[Watch_Side_Panel|Watch Panel]] and conditional breakpoints.
 +
 
 +
=== Using the DOM/Watch panels ===
 +
 
 +
In order to see closures in the DOM/Watch panels, you need to activate the "Show Closure" option:
-
In order to go through closures in the DOM panel, you need to activate the "Show Closure" option:
 
[[File:Activate_Show_Closure.png|600px]]
[[File:Activate_Show_Closure.png|600px]]
-
Then you can access to closure variables by expanding the function properties and ''(closures)'', as below:
+
You can then access closure variables of a function by expanding its magic ''(closure)'' property:
 +
 
[[File:Access_Closure_Variables.png]]
[[File:Access_Closure_Variables.png]]
-
=== Using the Watch Panel ===
 
-
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 from optimizing away variables or closures ===
 +
 
 +
The browser often optimizes away the values of variables that are not used, or that only need to live for the duration of a call. Similarly, functions that don't refer to anything in their containing environment do not get treated as closures. There is no way for Firebug to prevent this from happening (even if it was possible, it would cause large performance regressions and memory leaks).
 +
 
 +
If you do need to keep one of these references to watch its value, you can temporarily modify the source code to include an ''eval("")'' nearby. (Like ''with'', direct ''eval'' calls disable much of the optimization JavaScript engines can do.) Here is an example:
 +
 
 +
<source lang="javascript">
 +
function modularCycle(start, modulo)
 +
{
 +
    var cur = start % modulo;
 +
 
 +
    // prevents ''start'' from being optimized away:
 +
    eval("");
 +
 
 +
    return function(addition)
 +
    {
 +
        cur = (cur + addition) % modulo;
 +
        return cur;
 +
    };
 +
}
 +
 
 +
var cycle = modularCycle(42, 30);
 +
cycle(20);
 +
 
 +
// using the command line:
 +
cycle.%start; // ==> 42 (would print "(optimized away)" without the ''eval'' call)
 +
</source>
 +
 
 +
Remember to remove it afterwards, as it can be a source of performance penalties or memory leaks.
 +
 
 +
=== Shorthand ===
 +
 
 +
Considering the [[#Basic_example|basic example]], in order to access to ''name'', you type the following:
 +
<source lang="javascript">
 +
someone.introduce.%name;
 +
</source>
 +
 
 +
As a handy shortcut, you can also use ''someone.%name'' to do the same thing. When Firebug encounters such an expression ''a.%b'' where ''a'' is not a function, it will look up the first member function of ''a'' which is a closure and use closed-over variables from there. In other words, these two syntaxes are equivalent:
 +
<source lang="javascript">
 +
someone.%name;          // ==> "Arthur"
 +
someone.introduce.%name; // ==> "Arthur"
 +
</source>
 +
 
 +
=== Getters and setters ===
 +
 
 +
Consider the case where the functions you are interested in are getters or setters on some object:
 +
 
 +
<source lang="javascript">
 +
function Person(birthYear)
 +
{
 +
    Object.defineProperty(this, "age", {
 +
        get: function()
 +
        {
 +
            return new Date().getFullYear() - birthYear;
 +
        }
 +
    });
 +
};
 +
 
 +
var person = new Person(1970);
 +
</source>
 +
 
 +
Then there is unfortunately no direct way of accessing ''birthYear'' — ''person.age.%birthYear'' does '''not''' work (it tries to get a closure variable of an integer). In such cases, you will have to use ''Object.getOwnPropertyDescriptor'' (or the deprecated ''__lookupGetter__''):
 +
 
 +
<source lang="javascript">
 +
Object.getOwnPropertyDescriptor(person, "age").get.%birthYear; // ==> 1970
 +
person.__lookupGetter__("age").%birthYear;                    // ==> 1970
 +
</source>
 +
 
 +
The shorthand version ''person.%birthYear'' might also work.

Latest revision as of 00:48, 6 February 2013

Closure inspector is a feature available from Firebug 1.11.2 that makes it possible to access JavaScript closure variables.

Contents

[edit] Closures

If you already know what closures are, skip to Closure Inspector.

[edit] Definition

You might have seen or even used closures without knowing the formal definition.

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, a closure combines both the function and its containing environment, and stores references to the variables declared in its parent scopes. When the function is called, it can access those variables regardless of where the call site is.

Take a look at how this looks in JavaScript:

[edit] Basic example

Here is a basic example of a closure:

function Person(name)
{
    this.introduce = function()
    {
        console.log("Hello, my name is %s", name);
    }
}
 
// create an instance of Person:
var someone = new Person("Arthur");
 
// 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 (notice that there is no name variable inside the introduce method).

It is notable that, where someone.introduce is called in the above example (i.e. its call site), the name variable can not be accessed by any regular means. This is where the Closure Inspector comes in.

[edit] Closure Inspector

[edit] Using the Command Line

The syntax to access the closure variables is as follow:

closure.%variable

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

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

Note that you can also change values:

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

The same syntax works everywhere in Firebug where JavaScript is required, such as in the Watch Panel and conditional breakpoints.

[edit] Using the DOM/Watch panels

In order to see closures in the DOM/Watch panels, you need to activate the "Show Closure" option:

Activate Show Closure.png


You can then access closure variables of a function by expanding its magic (closure) property:

Access Closure Variables.png


[edit] Tips

[edit] Prevent the browser from optimizing away variables or closures

The browser often optimizes away the values of variables that are not used, or that only need to live for the duration of a call. Similarly, functions that don't refer to anything in their containing environment do not get treated as closures. There is no way for Firebug to prevent this from happening (even if it was possible, it would cause large performance regressions and memory leaks).

If you do need to keep one of these references to watch its value, you can temporarily modify the source code to include an eval("") nearby. (Like with, direct eval calls disable much of the optimization JavaScript engines can do.) Here is an example:

function modularCycle(start, modulo)
{
    var cur = start % modulo;
 
    // prevents ''start'' from being optimized away:
    eval("");
 
    return function(addition)
    {
        cur = (cur + addition) % modulo;
        return cur;
    };
}
 
var cycle = modularCycle(42, 30);
cycle(20);
 
// using the command line:
cycle.%start; // ==> 42 (would print "(optimized away)" without the ''eval'' call)

Remember to remove it afterwards, as it can be a source of performance penalties or memory leaks.

[edit] Shorthand

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

someone.introduce.%name;

As a handy shortcut, you can also use someone.%name to do the same thing. When Firebug encounters such an expression a.%b where a is not a function, it will look up the first member function of a which is a closure and use closed-over variables from there. In other words, these two syntaxes are equivalent:

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

[edit] Getters and setters

Consider the case where the functions you are interested in are getters or setters on some object:

function Person(birthYear)
{
    Object.defineProperty(this, "age", {
        get: function()
        {
            return new Date().getFullYear() - birthYear;
        }
    });
};
 
var person = new Person(1970);

Then there is unfortunately no direct way of accessing birthYearperson.age.%birthYear does not work (it tries to get a closure variable of an integer). In such cases, you will have to use Object.getOwnPropertyDescriptor (or the deprecated __lookupGetter__):

Object.getOwnPropertyDescriptor(person, "age").get.%birthYear; // ==> 1970
person.__lookupGetter__("age").%birthYear;                     // ==> 1970

The shorthand version person.%birthYear might also work.

Personal tools