gecko-dev/js/rhino/docs/scopes.html
nboyd%atg.com 711c087f8e Massive reconfiguration of the cvs directory structure:
mozilla/js/rhino/org is now distributed between
mozilla/js/rhino/src and mozilla/js/rhino/toolsrc.
The build.xml has been split in three.
Docs now live in the project directory.

These changes mean that the cvs directories mirror the distribution and thus a distribution
will build the same way as a cvs build.
2001-04-24 20:31:31 +00:00

175 lines
8.7 KiB
HTML

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Norris Boyd">
<meta name="GENERATOR" content="Mozilla/4.72 [en]C-NSCP (WinNT; U) [Netscape]">
<meta name="KeyWords" content="Rhino, JavaScript, Java">
<title>Scopes and Contexts</title>
</head>
<body bgcolor="#FFFFFF">
<script src="owner.js"></script>
<center>
<h1>
Scopes and Contexts</h1></center>
<script>document.write(owner());</script>
<br><script>
var d = new Date(document.lastModified);
document.write((d.getMonth()+1)+"/"+d.getDate()+"/"+d.getFullYear());
document.write('<br>');
</script>
<center>
<hr WIDTH="100%"></center>
<p>Before using Rhino in a concurrent environment, it is important to understand
the distinction between Contexts and scopes. Both are required to execute
scripts, but they play different roles. Simple embeddings of Rhino probably
won't need any of the information here, but more complicated embeddings
can gain performance and flexibility from the techniques described below.
<br>&nbsp;
<h2>
Contexts</h2>
The Rhino Context object is used to store thread-specific information about
the execution environment. There should be one and only one Context associated
with each thread that will be executing JavaScript.
<p>To associate the current thread with a Context, simply call the <tt>enter</tt>
method of Context:
<pre>&nbsp;&nbsp;&nbsp; Context cx = Context.enter();</pre>
Once you are done with execution, simply exit the Context:
<pre>&nbsp;&nbsp;&nbsp; Context.exit();</pre>
These calls will work properly even if there is already a Context associated
with the current thread. That context will be returned and an internal
counter incremented. Only when the counter reaches zero will it be disassociated
from the thread.
<p>Remember to put the <tt>exit()</tt> call in a <tt>finally</tt> block
if you're executing code that could throw an exception.
<br>&nbsp;
<h2>
Scopes</h2>
A scope is a set of JavaScript objects. Execution of scripts requires a
scope for top-level script variable storage as well as a place to find
standard objects like <tt>Function</tt> and <tt>Object</tt>.
<p>It's important to understand that a scope is independent of the Context
that created it. You can create a scope using one Context and then evaluate
a script using that scope and another Context (either by exiting the current
context and entering another, or by executing on a different thread). You
can even execute scripts on multiple threads simultaneously in the same
scope. Rhino guarantees that accesses to properties of JavaScript objects
are atomic across threads, but doesn't make any more guarantees for scripts
executing in the same scope at the same time. If two scripts use the same
scope simultaneously, the scripts are responsible for coordinating any
accesses to shared variables.
<p>A top-level scope is created by calling <tt>Context.initStandardObjects</tt>
to create all the standard objects:
<pre>&nbsp;&nbsp;&nbsp; Scriptable scope = cx.initStandardObjects(null);</pre>
The easiest way to embed Rhino is just to create a new scope this way whenever
you need one. However, <tt>initStandardObjects</tt> is an expensive method
to call and it allocates a fair amount of memory. We'll see below that
there are ways to share a scope created this way among multiple scopes
and threads.
<br>&nbsp;
<h2>
Name Lookup</h2>
So how are scopes used to look up names? In general, variables are looked
up by starting at the current variable object (which is different depending
on what code is being executed in the program), traversing its prototype
chain, and then traversing the parent chain. In the diagram below, the
order in which the six objects are traversed is indicated.
<center>
<p><img SRC="lookup.gif" height=194 width=500>
<br><i><font size=-1>Order of lookups in a two-deep scope chain with prototypes.</font></i></center>
<p>For a more concrete example, let's consider the following script:
<blockquote><tt>var g = 7;</tt>
<br><tt>function f(a) {</tt>
<br><tt>&nbsp;&nbsp;&nbsp; var v = 8;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; x = v + a;</tt>
<br><tt>}</tt>
<br><tt>f(6);</tt></blockquote>
We have a top-level variable <tt>g</tt>, and the call to <tt>f</tt> will
create a new top-level variable <tt>x</tt>. All top-level variables are
properties of the scope object. When we start executing <tt>f</tt>, the
scope chain will start with the function's activation object and will end
with the top-level scope (see diagram below). The activation object has
two properties, 'a' for the argument, and 'v' for the variable. The top-level
scope has properties for the variable <tt>g</tt> and the function <tt>f</tt>.
<center>
<p><img SRC="scopes.gif" height=496 width=820>
<br><i><font size=-1>An example scope chain for a simple script.</font></i></center>
<p>When the statement <tt>x = v + a;</tt> is executed, the scope chain
is traversed looking for a 'x' property. When none is found, a new property
'x' is created in the top-level scope.
<h2>
Sharing Scopes</h2>
JavaScript is a language that uses delegation rather than traditional class-based
inheritance. This is a large topic in itself, but for our purposes it gives
us an easy way to share a set of read-only variables across multiple scopes.
To do this we set an object's prototype. When accessing a property of an
object in JavaScript, the object is first searched for a property with
the given name. If none is found, the object's prototype is searched. This
continues until either the object is found or the end of the prototype
chain is reached.
<p>So to share information across multiple scopes, we first create the
object we wish to share. Typically this object will have been created with
<tt>initStandardObjects</tt> and may also have additional objects specific
to the embedding. Then all we need to do is create a new object and call
its <tt>setPrototype</tt> method to set the prototype to the shared object:
<pre>&nbsp;&nbsp;&nbsp; Scriptable newScope = cx.newObject(sharedScope);
&nbsp;&nbsp;&nbsp; newScope.setPrototype(sharedScope);</pre>
The call to <tt>newObject</tt> simply creates a new JavaScript object with
no properties. It uses the <tt>sharedScope</tt> passed in to initialize
the prototype with the standard <tt>Object.prototype</tt> value.
<p>We can now use <tt>newScope</tt> as a scope for calls to evaluate scripts.
Let's call this scope the <i>instance scope</i>. Any top-level functions
or variables defined in the script will end up as properties of the instance
scope. Uses of standard objects like <tt>Function</tt>, <tt>String</tt>,
or <tt>RegExp</tt> will find the definitions in the shared scope. Multiple
instance scopes can be defined and have their own variables for scripts
yet share the definitions in the shared scope. These multiple instance
scopes can be used concurrently.
<br>&nbsp;
<h2>
Dynamic Scopes</h2>
There's one problem with the setup outlined above. Calls to functions in
JavaScript use <i>static scope</i>, which means that variables are first
looked up in the function and then, if not found there, in the lexically
enclosing scope. This causes problems if functions you define in your shared
scope need access to variables you define in your instance scope.
<p>With Rhino 1.5, it is possible to compile functions to use <i>dynamic
scope</i>. With dynamic scope, functions look at the top-level scope of
the calling function rather than their lexical scope. So we can store information
that varies across scopes in the instance scope yet still share functions
that manipulate that information reside in the shared scope.
<p>The <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/DynamicScopes.java">DynamicScopes
example</a> illustrates all the points discussed above.
<br>&nbsp;
<br>&nbsp;
<h2>
More on Scopes</h2>
The key things to determine in setting up scopes for your application are
<br>(1) What scope should global variables be created in when your script
executes an assignment to an undefined variable, and
<br>(2) What variables should your script have access to when it references
a variable?
<p>The answer to (1) determines which scope should be the ultimate parent
scope: Rhino follows the parent chain up to the top and places the variable
there. After you've constructed your parent scope chain, the answer to
question (2) may indicate that there are additional scopes that need to
be searched that are not in your parent scope chain. You can add these
as prototypes of scopes in your parent scope chain. When Rhino looks up
a variable, it starts in the current scope, walks the prototype chain,
then goes to the parent scope and its prototype chain, until there are
no more parent scopes left.
<br>&nbsp;
<h3>
<hr WIDTH="100%"><br>
<a href="index.html">back to top</a></h3>
</body>
</html>