We've said that the Window object is really the most central one
in client-side JavaScript. This is because it is the object that
defines the name space of a program. We saw
earlier that every JavaScript expression implicitly refers to
the current window. This includes expressions as simple as
window, which is a reference to a property
within the current window that happens to refer to that window
itself.

But if every expression refers to the current window, then so does
code like this:

var i; // declare a variable i
i = 0; // assign the variable a value

The assignment i = 0 is actually the same as writing

window.i = 0;

This is an important point to understand about client-side
JavaScript: variables are nothing more than properties
of the current window. (This is not true for local
variables declared within a function, however.)

One implication of the fact that variables are properties of the
current Window object is that two variables with the same name may
be declared in different windows or different frames, and they
will not overwrite or conflict with each other.

Another implication is that JavaScript code running in one window
or frame may read and write variables declared by code in another
window or frame, as long as the first window knows how to refer to
the second window.[2]
So, if a top-level window has two frames, and code in the first
frame does the following:

parent.frames[1].i = 3;

it is equivalent to code in the second frame doing the following:

i = 3;

[2]
See Chapter 20, JavaScript Security, however, for a
discussion of a "security hobble" that prevents scripts from
one web server from reading values from windows that contain
data from other web servers.

The final implication of the equivalence between variables and
window properties is that there is no such thing as a "global
variable" in client-side JavaScript--i.e., there are no
user-created variables that are global to Navigator as a whole,
across all windows and frames. Each variable is defined only
within one window.

Recall that the function keyword that defines
functions declares a variable just like the var
keyword does. Since functions are referred to by variables, they
to are defined only within the window in which they are declared.
That is, if you define a function in one window, you cannot use it
in another, unless you explicitly assign the function to a
variable in the other window.

Remember that constructors are also functions, so when you define
a class of objects with a constructor function and an associated
prototype object, that class is only defined for a single window.
(See Chapter 7, Objects, for details on
constructor functions and prototype objects.) This is true of
predefined constructors as well as constructors you define
yourself. The String constructor is available in all windows, but
that is because all windows automatically are given a property
that refers to this predefined constructor function. Just as
each window has its own separate reference to the constructor,
each window has a separate copy of the prototype object for a
constructor. So if you write a new method for manipulating
JavaScript strings, and make it a method of the String class by
assigning it to the String.prototype object in
the current window, then all strings in that window will be able
to use the new method. But the new method will not be accessible
to strings defined in other windows.

Bear in mind that this discussion of variables and Window object
properties does not apply to variables declared within functions.
These "local" variables exist only within the function body and
are not accessible outside of the function. Also, note that there
is one difference between variables and properties of the current
window. This difference is revealed in the behavior of the
for/in loop. Window properties that were
created by variable declarations are not returned by the
for/in loop, while "regular" properties of the
Window are. See Chapter 5, Statements, for
details.

We saw above that top-level variables are implemented as
properties of the current window or frame object. In Chapter 6, Functions, we saw that local variables
in a function are implemented as transient properties of the
function object itself. From these facts, we can begin to
understand variable scoping in JavaScript; we can begin to see
how variable names are looked up.

Suppose a function f uses the identifier
x in an expression. In order to evaluate the
expression, JavaScript must look up the value of this
identifier. To do so, it first checks if f
itself has a property named x. If so, the
value of that property is used; it is an argument, local
variable, or static variable assigned to the function. If
f does not have a property named
x, then JavaScript next checks to see if the
window that f is defined in has a property
named x, and, if so, it uses the value of
that property. In this case x would be a
top-level or "global" (to that window) variable. Note that
JavaScript looks up x in the window in which
f was defined, which may not be the same as
the window that is executing the script that called
f. This is a subtle but important
difference that can arise in some circumstances.

A similar process occurs if the function f
uses document.title in an expression. In
order to evaluate document.title, JavaScript
must first evaluate document. It does this
in the same way it evaluated x. First it
sees if f has a property named
document. If not, it checks whether its
Window object has such a property. Once it has obtained a value
for document, it proceeds to look up
title as a property that object--it does not
check the properties of the function or window, in this case, of
course. In this example, the code probably refers to the
document property of the Window object, and
if the function inadvertently defined a local variable named
document, the
document.title expression might well
be evaluated incorrectly.

What we learn from these examples is that identifiers are
evaluated in two scopes: the current function, and the window in
which the function is defined. In Chapter 5, Statements we saw that the
with statement can be used to add additional
scopes. When an identifier is evaluated, it is first looked up
in the scopes specified by any containing
with statements. For example, if a top-level
script runs the following code:

with(o) {
document.write(x);
}

Then the identifier x is evaluated first in
the scope of the object o. If no definition
is found in that object's properties, then x
is evaluated in the context of the current window. If the same
code occurred within a function f then
x would be looked up first as a property of
o, then as a property of f
and finally as a property of the current window.

Recall that with statements can be nested
arbitrarily, creating a variable "scope" of any depth.
One interesting way to use with is with a
window reference:

with(parent.frames[1]) {
...
}

This technique allows code in one window to easily read
properties of another window. Another technique that is
sometimes of interest is to place the entire body of a
function within the block of a with(this)
statement. What this does is create a method that evaluates
identifiers by looking them up first as properties of the
object that it is a method of. Note, however, that such a
method would find properties of its object
before it found its own local variables
and arguments, which is unusual behavior!

Event handlers are scoped differently than regular functions
are. Consider the onChange() event
handler of a text input field named t
within an HTML form named f. If this
event handler wants to evaluate the identifier
x, it first uses the scope of any
with statements of course, and then looks
at local variables and arguments, as we saw above. If the
event handler were a standalone function, it would look in
the scope of the containing window next and stop there. But
because this function is an event handler, it next looks in
the scope of the text input element t. If
the property x is not defined there, it
looks at the properties of the form object
f. If f does not have
a property named x, JavaScript next
checks to see if the Document object that contains the form
has a definition of this property. Finally, if no definition
of x is found in any of these objects,
the containing window is checked.

If all identifiers had unique names, scope would never
matter. But identifiers are not always unique, and we have
to pay attention to scope. One important case is the
Window.open() method and the
Document.open() method. If a top-level
script of a regular function calls
open(), JavaScript's scoping rules will
find the open property of the Window
object and use this method. On the other hand, if an event
handler calls open(), the scoping rules
are different, and JavaScript will find the definition of
open in the Document object before it
finds it in the Window object. The same code may work in
different ways depending on its context. The moral of this
particular example is to never use the
open() method without explicitly
specifying whether you mean
document.open() or
window.open(). Be similarly cautious when
using location; it, too, is a property of
both the Window and Document objects.

Finally, note that if an event handler doesn't call
open() directly but instead calls a
function that calls open(), the function
does not inherit the scope of the
event handler that invoked it. The function's scope would be
the function itself, and then the window that contains it, so
in this case, the open() method would be
interpreted as the Window.open() method, not
Document.open().