зеркало из https://github.com/mozilla/gecko-dev.git
177 строки
9.0 KiB
HTML
177 строки
9.0 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>
|
|
</p>
|
|
<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: </p>
|
|
<pre> Context cx = Context.enter();</pre>
|
|
Once you are done with execution, simply exit the Context:
|
|
<pre> 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>
|
|
</p>
|
|
<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>
|
|
<p>A top-level scope is created by calling <tt>Context.initStandardObjects</tt>
|
|
to create all the standard objects: </p>
|
|
<pre> 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>
|
|
|
|
<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></p>
|
|
</center>
|
|
|
|
<p>For a more concrete example, let's consider the following script: </p>
|
|
<blockquote><tt>var g = 7;</tt> <br>
|
|
<tt>function f(a) {</tt> <br>
|
|
<tt> var v = 8;</tt> <br>
|
|
<tt> 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></p>
|
|
</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. </p>
|
|
<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, and the parent of the new scope
|
|
to null: </p>
|
|
<pre> Scriptable newScope = cx.newObject(sharedScope);<br> newScope.setPrototype(sharedScope);<br> newScope.setParentScope(null);<br></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>
|
|
</p>
|
|
<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>
|
|
<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>
|
|
<br>
|
|
</p>
|
|
<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>
|
|
</p>
|
|
<h3>
|
|
<hr width="100%"><br>
|
|
<a href="index.html">back to top</a>
|
|
</h3>
|
|
|
|
</body>
|
|
</html>
|