Closure Inspector
From FirebugWiki
(Add some examples) |
(second draft) |
||
| Line 1: | Line 1: | ||
| - | Closure inspector is a feature available from Firebug 1.11.2 that | + | Closure inspector is a feature available from Firebug 1.11.2 that makes it possible to access JavaScript ''closure'' variables. |
== Closures == | == Closures == | ||
| - | + | If you already know what closures are, skip to [[#Closure_Inspector|Closure Inspector]]. | |
=== Definition === | === Definition === | ||
| - | You might | + | 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, | + | 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: | |
=== Basic example === | === Basic example === | ||
| - | Here is a basic example | + | Here is a basic example of a closure: |
<source lang="javascript"> | <source lang="javascript"> | ||
| - | function Person() | + | function Person(name) |
{ | { | ||
| - | + | this.introduce = function() | |
| - | + | { | |
| - | + | console.log("Hello, my name is %s", 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 ( | + | ''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. | |
== Closure Inspector == | == Closure Inspector == | ||
| Line 50: | Line 48: | ||
</source> | </source> | ||
| - | In the [[# | + | 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> | ||
| - | + | Note that you can also change values: | |
| - | + | ||
| - | Note that | + | |
<source lang="javascript"> | <source lang="javascript"> | ||
someone.introduce.%name = "Trillian"; | someone.introduce.%name = "Trillian"; | ||
| - | someone.introduce(); // | + | someone.introduce(); // prints "Hello, my name is Trillian" |
</source> | </source> | ||
| - | === Using the DOM | + | 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 | + | In order to see closures in the DOM/Watch panels, you need to activate the "Show Closure" option: |
[[File:Activate_Show_Closure.png|600px]] | [[File:Activate_Show_Closure.png|600px]] | ||
| Line 71: | Line 69: | ||
| - | + | 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]] | ||
| - | |||
| - | |||
== Tips == | == Tips == | ||
| - | === Prevent the browser | + | === Prevent the browser from optimizing away variables or closures === |
| - | + | ||
| - | If you do need to keep one of these references to watch its value, ''eval("")'' | + | 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"> | <source lang="javascript"> | ||
| - | function | + | 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 | + | var cycle = modularCycle(42, 30); |
| - | + | cycle(20); | |
// using the command line: | // using the command line: | ||
| - | + | cycle.%start; // ==> 42 (would print "(optimized away)" without the ''eval'' call) | |
</source> | </source> | ||
| + | |||
| + | Remember to remove it afterwards, as it can be a source of performance penalties or memory leaks. | ||
=== Shorthand === | === Shorthand === | ||
| Line 116: | Line 114: | ||
</source> | </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"> | <source lang="javascript"> | ||
someone.%name; // ==> "Arthur" | someone.%name; // ==> "Arthur" | ||
someone.introduce.%name; // ==> "Arthur" | someone.introduce.%name; // ==> "Arthur" | ||
</source> | </source> | ||
| - | |||
| - | + | === Getters and setters === | |
| - | + | Consider the case where the functions you are interested in are getters or setters on some object: | |
| - | + | ||
| - | + | ||
| - | + | ||
<source lang="javascript"> | <source lang="javascript"> | ||
| - | function Person( | + | function Person(birthYear) |
{ | { | ||
| - | + | Object.defineProperty(this, "age", { | |
| - | + | get: function() | |
| - | + | { | |
| - | + | return new Date().getFullYear() - birthYear; | |
| - | + | } | |
| - | + | }); | |
| - | + | ||
| - | + | ||
| - | + | ||
}; | }; | ||
| - | var | + | var person = new Person(1970); |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
</source> | </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"> | <source lang="javascript"> | ||
| - | + | Object.getOwnPropertyDescriptor(person, "age").get.%birthYear; // ==> 1970 | |
| - | + | person.__lookupGetter__("age").%birthYear; // ==> 1970 | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
</source> | </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.
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.%variableIn 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:
You can then access closure variables of a function by expanding its magic (closure) property:
[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 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__):
Object.getOwnPropertyDescriptor(person, "age").get.%birthYear; // ==> 1970 person.__lookupGetter__("age").%birthYear; // ==> 1970
The shorthand version person.%birthYear might also work.
