Book Excerpt: Object-Oriented JavaScript
|
|
This chapter excerpt from
Microsoft AJAX Library Essentials by Cristian Darie, Bogdan
Brinzarea, is printed with permission from Packt
Publishing, Copyright 2007.
The JavaScript Execution Context
In this section we'll take a peek under the hood of the JavaScript closures and
the mechanisms that allow us to create classes, objects, and object members in
JavaScript. For most cases, understanding these mechanisms isn't absolutely
necessary for writing JavaScript code—so you can skip it if it sounds too
advanced. If, on the contrary, you should be interested in learning more about
the JavaScript parser's inner workings, see the more advanced article at
http://www.jibbering.com/faq/ faq_notes/closures.html.
|
The JavaScript execution context is a concept that explains much of the behavior
of JavaScript functions, and of the code samples presented earlier. The
execution context represents the environment in which a piece of JavaScript
code executes. JavaScript knows of three execution contexts:
-
The global execution context is the implicit environment (context) in which the
JavaScript code that is not part of any function executes.
-
The function execution context is the context in which the code of a function
executes. A function context is created automatically when a function is
executed, and removed from the contexts stack afterwards.
-
The eval() execution context is the context in which JavaScript code executed
using the eval() function runs.
-
Each execution context has an associated scope, which specifi es the objects
that are accessible to the code executing within that context.
The scope of the global execution context contains the locally defi ned
variables and functions, and the browser's window object. In that context, this
is equivalent to window, so you can access, for example, the location property
of that object using either this.location or window.location.
The scope of a function execution context contains the function's parameters,
the locally defi ned variables and functions, and the variables and functions
in the scope of the calling code. This explains why the getCellCount() function
has access to the _rows and _columns variables that are defi ned in the outer
function (Table):
// Table class
function Table (rows, columns)
{
// save parameter values to local variables
var _rows = rows;
var _columns = columns;
// return the number of table cells
this.getCellCount = function()
{
return _rows * _columns;
};
}
The scope of the eval() execution context is identical to the scope of the
calling code context. The getCellCount() function from the above code snippet
could be written like this, without losing its functionality:
// return the number of table cells
this.getCellCount = function ()
{
return eval(_rows * _columns);
};
var x, this.x, and x
An execution context contains a collection of (key, value) associations
representing the local variables and functions, a prototype whose members can
be accessed through the this keyword, a collection of function parameters (if
the context was created for a function call), and information about the context
of the calling code.
Members accessed through this, and those declared using var, are stored in
separate places, except in the case of the global execution context where
variables and properties are the same thing. In objects, variables declared
through var are not accessible through function instances, which makes them
perfect for implementing private "class" members, as you could see in an
earlier exercise. On the other hand, members accessed through this are
accessible through function instances, so we can use them to implement public
members.
When a member is read using its literal name, its value is fi rst searched for
in the list of local variables. If it's not found there, it'll be searched for
in the prototype. To understand the implications, see the following function,
which defi nes a local variable x, and a property named x. If you execute the
function, you'll see that the value of x is read from the local variable, even
though you also have a property with the same name:
function BigTest()
{
var x = 1;
this.x = 2;
document.write(x); // displays "1"
document.write(this.x); // displays "2"
}
Calling this function, either directly or by creating an instance of it, will
display 1 and 2—demonstrating that variables and properties are stored
separately. Should you execute the same code in the global context (without a
function), for which variables and properties are the same, you'd get the same
value displayed twice.
When reading a member using its name literally (without this), if there's no
local variable with that name, the value from the prototype (property) will be
read instead, as this example demonstrates:
function BigTest()
{
this.x = 2;
document.write(x); // displays "2"
}
Using the Right Context
Wh en working with JavaScript functions and objects, you need to make sure the
code executes in the context it was intended for, otherwise you may get
unpredictable results. You saw earlier that the same code can have different
output when executing inside a function or in the global context.
Things get a little more complicated when using the this keyword. As you know,
each function call creates a new context in which the code executes. When the
context is created, the value of this is also decided:
-
When an object is created from a function, this refers to that object.
-
In the case of a simple function call, no matter if the function is defi ned
directly in the global context or in another function or object, this refers to
the global context.
The second point is particularly important. Using this in a function that is
meant to be called directly, rather than instantiated as an object, is a bad
programming practice, because you end up altering the global object. Take this
example that demonstrates how you can overwrite a global variable from within a
function:
x = 0;
function BigTest()
{
this.x = 1; // modify a variable of the global context
}
BigTest();
document.write(x); // displays "1"
Modifying the global object can be used to implement various coding
architectures or features, but abusing of this technique can be dangerous. On
the other hand, if BigTest is instantiated using the new keyword, the this
keyword will refer to the new object, rather than the global object. Modifying
the previous example as highlighted below, we can see the x variable of the
global context remains untouched:
x = 0;
function BigTest()
{
this.x = 1; // create an internal object property
}
v ar obj = new BigTest();
document.write(x); // displays "0"
When creating your own code framework, you can enforce that a function's code is
executed through a function instance. The little trick involves creating a new
object on the spot if the function was called directly, and using that object
for further processing. This allows you to ensure that a function call will not
modify any members of the global context. It works like this:
x = 0;
function BigTest()
{
if (!(this instanceof BigTest)) return new BigTest();
this.x = 1;
}
BigTest();
document.write(x); // displays "0"
The highlighted line simply checks if the this keyword refers to an instance of
BigTest (the instanceof keyword is used for this). If it's not, a new BigTest
instance is returned, and execution stops. The BigTest instance, however, is
executed, and this time this will be a BigTest instance, so the function will
continue executing in the context of that object.
This ends our little incursion into JavaScript's internals. The complete theory
is more complicated than that, and it's comprehensively covered by David
Flangan's JavaScript: The Defi nitive Guide, Fifth Edition (O'Reilly, 2006).
The FAQ at http://www.jibbering.com/faq/ will also be helpful if you need to
learn about the more subtle aspects of JavaScript.
Related Links
>Object
Oriented Programming Interview Questions
What
is object oriented programming (OOP)?
The object oriented programming is commonly known as OOP. Most of the languages
are developed using OOP concept. Object-oriented programming (OOP) is a
programming concept that uses "objects" to develop a system.........
Define
Encapsulation and Information Hiding in OOP.
Encapsulation means keeping actions and attributes together under a single unit.
This can also be understood using a motor bike example. A bike has actions such
as 'switch on light', 'horn' etc. and attributes such specific color, size,
weight etc..............
What
are the advantages of OOP?
It presents a simple, clear and easy to maintain structure. It enhances program
modularity since each object exists independently............
What
encapsulation, inheritance, and polymorphism mean
In OOP's world everything revolves around objects and classes, and OOP languages
usually offer three specifi c features for manipulating them—encapsulation,
inheritance, and polymorphism........
|