JavaScript™ (also known as ECMAScript or JScript) is an interpreted, object-oriented programming language commonly used throughout the web to provide interactive features to web pages. This article introduces JavaScript, outlines some of the basic principles behind programming using JavaScript and illustrates the differences between the different implementations available in Mozilla and Internet Explorer.
This article is intended to provide an introduction to
JavaScript and a reference on the differences in the
implementations of JavaScript in Mozilla and Microsoft Internet
Explorer (MSIE). Examples which can be run directly from the
article using Mozilla, Mozilla Firefox, Internet Explorer and
Opera are used to illustrate general concepts as well as the
use of each feature and Object in JavaScript. This is a
work in progress and is provided in the current state
in the hope that you will find it useful. I plan to continue
work on this article in the hope it can become the definitive
online resource for JavaScript. Email to
feedback@bclary.com regarding this article is
encouraged and welcome.
The article is organized into two parts.
Part I is intended as a general introduction to JavaScript. It is primarily targeted for beginning to intermediate level programmers but hopefully contains some information which will be useful for those with more advanced skills.
Part II serves as a reference for JavaScript.
Distinctive formatting is used for specific specialized topics:
Code samples and examples are presented in a monospace
font. Variables are presented in an italic
font in the text and as monospace
italic in code samples.
Boxes with distinctive formatting are used to highlight specific sections.
Boxes with single line black borders are used to contain runnable examples.
Please note that the examples require the use of a pop-up window and you must enable pop-ups on this site in order to run them.
Boxes with double line red borders enclose notes which explain differences in how Mozilla and Internet Explorer implement JavaScript.
Boxes with notes.
Boxes with with todo notes.
Boxes indicating that the topic has yet to be written.
JavaScript is a powerful, object-oriented programming language which, like Rodney Dangerfield, does not get the respect it deserves. It is available in stand-alone, open source implementations in C and Java for a variety of platforms from mozilla.org and as part of modern web browsers such as Mozilla, Mozilla Firefox, Internet Explorer, Opera and Safari. JavaScript is available on practically every computer world-wide.
JavaScript was created by Brendan Eich, then of Netscape and now with Mozilla Foundation, and was first introduced in the Netscape Navigator 2.0 web browser in early 1996. Originally the language was called LiveScript and was later renamed to JavaScript as part of a marketing effort of Netscape and Sun Microsystems although it has little to do with Java. Microsoft incorporated an implementation of JavaScript called JScript into Internet Explorer 3.
JavaScript's syntax resembles that of the computer languages C and Java although several of its features such as typeless variables, automatic semi-colon insertion, and automatic type coversions were designed to make it more accessible to non-professional programmers. Like C, the JavaScript language does not define any means of performing input and output and relies entirely upon its host environment (web browser) for such features. Unlike C++ or Java, JavaScript is not a strongly typed computer language nor does it currently support class-based object oriented programming but instead relies on the use of Prototypes which serve as templates for creating new objects.
JavaScript was standardized as ECMAScript in ECMA 262 in June 1997. A second edition of the standard was released in August 1998 which consisted primarily of editorial changes to the first edition. The most recent edition is ECMAScript Language Specification ECMA-262 3rd Edition (ECMAScript Language Specification ECMA-262 3rd Edition (Unofficial HTML version)) which was released in August 1998 and added a number of new features such as exception processing. JavaScript is an evolving language with future editions expected to include native XML processing ( ECMAScript for XML (E4X) Specification ), strongly typed variables, and class-based inheritance in JavaScript 2.0 and ECMAScript Language Specification 4th Edition (Proposed).
Any one who is familiar with C or Java, can easily learn JavaScript. Conversely, JavaScript can be learned as a first programming language and the skills transferred to programming in other languages such as C and Java.
Comparing the support for various implementations of
different versions of JavaScript can be difficult due to the
different versioning schemes used by Netscape and Microsoft.
Netscape traditionally released different versions of their
JavaScript interpreter to be associated with distinct
script element language JavaScript
versions such as JavaScript1.1 or
JavaScript1.3 which did not necessarily map
directly to an edition of the ECMA 262 standard. Microsoft used
a different versioning scheme for JScript and only used the
script language attribute in a very
loose association with the actual JScript version.
JavaScript in Netscape Navigator, Mozilla and Mozilla Firefox
| JavaScript version | ECMA 262 | Introduced |
|---|---|---|
| JavaScript 1.0 | N/A | Navigator 2.0 |
| JavaScript 1.1 | N/A | Navigator 3.0 |
| JavaScript 1.2 | N/A | Navigator 4.0-4.05 |
| JavaScript 1.3 | 1st | Navigator 4.06-4.7x |
Mozilla and its family of browsers (Mozilla Firefox, Mozilla
Application Suite, Netscape 6.x, Netscape 7.x, …)
introduced support for JavaScript 1.5. Mozilla performs a
partial emulation of earlier versions of JavaScript if the
version is specified in the script element. For
compatibility with other modern browsers, it is best to only
specify the JavaScript language without version either through
the language or type attributes.
| JavaScript version | ECMA 262 | Introduced |
|---|---|---|
| JavaScript 1.5 | 3rd | Mozilla / Firefox / Netscape 6.x, 7.x, … |
Guides and References for the various editions of JavaScript used in Netscape Navigator and Mozilla were available on Netscape DevEdge however are no longer available since the site was taken down by AOL. Copies of the Netscape JavaScript Guides and References can be found on the web, however they are not authorised.
JavaScript in Internet Explorer
A history of Internet Explorer is available from Microsoft. The mapping in the following table of JScript version to maximum supported JavaScript language version for each version of MSIE is based upon guess work since it is not possible to test multiple versions of MSIE on the same machine and there is a dearth of documentation available from Microsoft regarding JavaScript language versions. Feedback and corrections are welcome.
A list of feature availability for the various JScript versions is also available from Microsoft.
Guides and references for Internet Explorer's implementation of JavaScript are available from MSDN
| JavaScript version | JScript version | ECMA 262 | Introduced |
|---|---|---|---|
| JavaScript 1.0 | JScript 1.0 | N/A | MSIE 3.0 |
| JavaScript 1.1 | JScript 3.0 | 1st | MSIE 4.0 |
| JavaScript 1.3 | JScript 5.0 | 3rd | MSIE 5.0 |
| JavaScript 1.3 | JScript 5.1 | 3rd | MSIE 5.01 |
| JavaScript 1.3 | JScript 5.5 | 3rd | MSIE 5.5 |
| JavaScript 1.3 | JScript 5.6 | 3rd | MSIE 6.0 |
| N/A | JScript 7.0 | N/A | .Net |
Much of the information in this article regarding differences in the implementations of JavaScript in Mozilla and Internet Explorer were developed using mozilla.org's JavaScript Test Library, the experimental online JavaScript Test Suite and JavaScript Test Results
JavaScript is an example of an interpreted
programming language. Unlike other interpreted languages which
compile (translate source code into machine code) each
statement each time it is executed, JavaScript first compiles
the entire JavaScript program before beginning to execute it.
JavaScript also provides the ability to compile
source
code on the fly.
Interpreted computer languages have the advantage that they can be run in any environment so long as an interpreter program for that environment is available. A disadvantage of interpreted computer languages is the need to compile the programs each time they are run. Other examples of interpreted programming languages are Basic, Perl and Python. Python has the additional capability to save compiled programs to disk where they can be run later without having to be interpreted.
JavaScript programs can be run either from the command-line using a stand-alone JavaScript interpreter or executed in Web Pages using Web browsers. By far, the most common use of JavaScript is in web pages and all examples in this article will use such an approach.
Script programs are included into web pages through the use
of the script element
anywhere inside the HTML head or body elements of an HTML
document. The program code can be included in the HTML document
by placing it inside of the script element or by
placing it in an external file and referencing the file through
the src attribute of the script
element. The language or type
attributes of the script element can be used to
specify the programming language and version the script is
written. If no scripting language or version is specified, the
web browser will use its default values which is typically the
latest version of JavaScript the browser supports.
Specifying Script Language Versions
Script authors typically use the script language version to hide advanced scripts from older browsers which do not support the language features being used. This approach has some drawbacks since older browsers may still compile the script even if they do not execute it. This can lead to compile time errors for unsupported language features in older browsers such as Netscape Navigator 3.x and 4.x although this is less of a problem today since the market-share of these older browsers has diminished to insignificance.
There are two methods for specifing the language version
in the script tag.
specify the language and version in the
language attribute of the
script tag.
<script language="javascript"> // default version of javascript </script>
<script language="javascript1.3"> // javascript version 1.3 </script>
specify the language mime type and version in the
type attribute in the script
tag.
<script type="text/javascript"> // default version of javascript </script>
<script type="text/javascript;version=1.3"> // javascript version 1.3 </script>
The Mozilla family of browsers (Mozilla Firefox, Mozilla
Application Suite, …) up to Gecko 1.7 support
JavaScript versions 1.1, 1.2, 1.3, 1.4 and 1.5 specified
either as the language attribute or as the type attribute of
the script tag. If a higher version is specified, the script
is ignored. If no version is specified in the
script element, Mozilla will default to
JavaScript 1.5. An additional feature of Mozilla's current
implementation is that it will emulate the features
of the specified version of the language if the language
version is specified. This can cause some
browser-compatibility problems since Internet Explorer does
not attempt such emulation. A change to Mozilla's
implementation to not emulate older JavaScript versions is
under consideration in Bugzilla
255895 although this change would not appear before
Mozilla 1.8.
Internet Explorer 6 supports JavaScript versions 1.1, 1.2
and 1.3 specified as the language attribute and ignores
scripts which specify a higher version. Internet Explore 6
only supports type="text/javascript" and ignores
any other value of type which includes
version or other information. Internet Explorer
does not emulate the behavior of older versions of JavaScript
when older versions are specified.
Opera 7.54 supports JavaScript versions 1.1, 1.2, 1.3,
1.4, 1.5 when specified as the language
attribute. Opera will execute any JavaScript language version
if it is specified in the type attribute.
Konqueror 3.2.1 will execute any version of JavaScript
when specified as the language attribute or the
type attribute.
Work is underway to develop the next generation of
JavaScript, JavaScript 2.0, which will introduce many new
advanced features to the language. When an implementation of
JavaScript 2.0 becomes available, the proper use of the
script element language version should be able
to hide the new scripts from older versions of Mozilla and
Internet Explorer although other browsers such as Opera and
Konqueror may have problems.
<html>
<head>
<script language="javascript">
// inline JavaScript code here
</script>
<script language="javascript1.3">
// inline JavaScript 1.3 code here
</script>
<script type="text/javascript">
// inline JavaScript code here
</script>
<script type="text/javascript;version=1.3">
// inline JavaScript 1.3 code here
</script>
</head>
<body>
stuff
</body>
</html>
<html>
<head>
<-- external JavaScript code in file somefileuri -->
<script language="javascript" src="somefileurl" ></script>
<-- external JavaScript 1.3 code in file somefileuri -->
<script language="javascript1.3" src="somefileurl" ></script>
<-- external JavaScript 1.3 code in file somefileuri -->
<script type="text/javascript" src="somefileurl" ></script>
<-- external JavaScript 1.3 code in file somefileuri -->
<script type="text/javascript;version=1.3" src="somefileurl" ></script>
</head>
<body>
stuff
</body>
</html>
Note the use of HTML comments
<-- … -->
inside of the HTML document and the use of single line JavaScript comments
// …
inside of the script element. An HTML comment
prevents its contents from being displayed by a web browser
while a JavaScript comment prevents the JavaScript
interpreter from attempting to compile and execute the line.
Comments are typically used for communicating with other web
developers who read the source code of a web document or
JavaScript program.
The HTML 4.01 specification states
that if a web brower does not recognize an HTML element in a
document, it should attempt to render the contents of the
element. This can cause some browsers which do not support
the script element to display inline JavaScript
programs as text in a web page. A standard technique to hide
JavaScript code from such browsers is to enclose inline
JavaScript inside of HTML comments as in the following
example:
<script type="text/javascript"> <-- // inline javascript code //--> </script>
The <-- … --> hides the script
from non-scriptable browsers while the last line comment
// hides the end of the HTML comment
--> from the JavaScript interpreter. Other
techniques involving CDATA sections are used for
hiding inline scripts in XHTML or XML documents.
A common error by those who do not understand HTML and
JavaScript is to wrap JavaScript code in external JavaScript
files inside of HTML comments. The only reason to use HTML
comments inside of a script element is to hide
the inline script from non-scriptable browsers which do not
recognize the script element. If a script is
contained in an external file, a non-scriptable browser will
not even attempt to load the file. There is no reason to
include such HTML comments in an external file and it is
technically an error to do so although most browsers are
forgiving of this common beginner's mistake.
Ever since Kernighan and Ritchie's The
(K&R) was first
published in 1978, the standard first program in any
language is one that prints the message hello,
world. Since this article is inspired by
K&R our first JavaScript program will do
the same.C
Programming Language
"hello world" can be written in a web page as:
<html>
<head>
<script type="text/javascript">
document.write('hello, world');
</script>
</head>
<body>
<h1>The Hello World JavaScript Program</h1>
</body>
</html>
To run the program, create a document containing the above text, then open the web page with Mozilla or Internet Explorer which will display
The actual JavaScript program consists of the statement
document.write('hello, world');
Unlike C there is no preferred
main function where execution of the program
begins. In JavaScript, the source code is first compiled,
then each global statement is executed in turn. Web
browsers and HTML provide other means to begin execution of
JavaScript depending upon events in the browser but will
not be covered in this article.
Let's pick the "hello, world" program a part and look at its pieces
document.write('hello, world') is a
function call which calls a method (function)
write of the object document with
argument 'hello, world'.
document is a host object provided
by the web browser and document.write is a
function property (method) of the document
object which outputs text in a web page.
document and document.write are
not official parts of the JavaScript language, but are
supplied by the web browser as part of its Document
Object Model (DOM). We will not be
discussing the Document Object Model in this article
instead focusing our attention on the JavaScript language
itself. If you are already familiar with C, you
may consider the DOM to be an external
library provided by the web browser.
'hello, world' is an example of a
literal primitive string value. Literal primitive
string values are sequences of characters which are
surrounded by either single quotes (') or
double quotes (").
The function call document.write('hello,
world') is an example of an expression. The
terminating semi-colon (;) terminates the
statement composed of the expression.
The examples in this article are written in such a
fashion that they can be executed directly from the article
itself. The examples use a user-defined function
named msg which uses the
DOM to output strings to web pages.
msg is defined as:
function msg(s)
{
document.write(s + '<br>');
}
When a function call msg(arg) is performed,
the body of the function msg ( the statements
inside of the braces { and }) are
executed with the value of the argument variable
s set to the value of the variable
arg.
document.write writes the argument
s followed by the HTML tag <br> to the
output document where it is displayed by your web
browser.
Each example will consist of the JavaScript source code
followed by an HTML select element which
allows the choice of JavaScript version to be used when
executing the example and an HTML button
element which will open a new browser window and execute
the example code. The new window will display the source
code of the example, followed by the output. Note that the
source code for the function msg is not
displayed for brevity's sake.
If you have a popup blocker enabled, you must configure it to allow this site to open popup windows.
The "Hello World" program would appear as:
msg('hello, world');
Run this example by selecting the JavaScript version and then clicking the execute button.
Our next example program is a slightly more realistic program based upon K&R's Fahrenheit to Celsius conversion program and introduces a number of JavaScript features which are found in all JavaScript programs.
/*
* Display Fahrenheit to Celsius Conversion table
* for 0°F to 100°F in steps of 10°F.
*/
var lower = 0; // lower limit of Fahrenheit temperature
var upper = 100; // upper limit of Fahrenheit temperature
var step = 10; // increment of Fahrenheit temperature
var fahrenheit; // current Fahrenheit temperature
var celsius; // current Celsius temperature
fahrenheit = lower;
while (fahrenheit <= upper)
{
celsius = Fahrenheit2Celsius(fahrenheit);
msg(fahrenheit + '°F is ' + celsius + '°C');
fahrenheit = fahrenheit + step;
}
function Fahrenheit2Celsius(fahr)
{
var cels = (5/9)*(fahr - 32);
return cels;
}
The program begins with a multiple line comment which describes the purpose of the program.
/* * Display Fahrenheit to Celsius Conversion table * for 0°F to 100°F in steps of 10°F. */
Comments are text which are intended to be read by programmers trying to understand how a program work. They are ignored by the JavaScript interpreter.
Multiple line comments begin with /* and
end with */ and are useful in circumstances
when a single comment line is insufficient or when you
would like to comment out
a portion of the program
so that the JavaScript interpreter will ignore it.
Note that multiple line comments can not nest. For example
/* this /* is an invalid */ comment */
For this reason, many people use single line comments
inside of functions, so that if it is desired to comment
out
the entire function later, a single /*
… */ can be placed around the entire
function. If multiple line comments had been used inside of
the function, this would not have been possible.
The next 5 lines of the program contain statements which declare the variables lower, upper, step, fahrenheit, celsius as global variables.
var lower = 0; // lower limit of Fahrenheit temperature var upper = 100; // upper limit of Fahrenheit temperature var step = 10; // increment of Fahrenheit temperature var fahrenheit; // current Fahrenheit temperature var celsius; // current Celsius temperature
A variable is a name for a data item in a
program and is an example of an identifier. An identifier is a
sequence of characters beginning with either $
(dollar sign), _
(underscore) or a unicode letter followed
by unicode letters, unicode digits, underscores
(_) or dollar signs ($). Note
that the use of unicode allows the creation of variable and
function names in character sets other than latin.
These variables are global because they were declared outside of any function definition and thus are available everywhere in the JavaScript program. In JavaScript variables do not have to be declared before use but it is recommended. JavaScript variables, unlike C or Java, do not have declared data types. A variable in JavaScript can hold any type of data.
Each of the variable declarations is followed by a
C++ style single line comment of the form //
…. Single line comments begin with
// and end at the end of the line where they
appear and are ignored by the JavaScript interpreter. They
are extremely useful for attaching a descriptive note to a
single line of code.
The variable declarations for lower,
upper and step include
initializers which set the initial values of the
variables to primitive number values 0,
100 and 10 respectively. The
variable declarations for fahrenheit and
celsius do not include intializers which means
they have the value undefined as their initial
values.
The next statement
fahrenheit = lower;
assigns the value of the variable
lower to the variable fahrenheit
using the assignment operator
=.
The next statement is a while statement
which has the general form:
while (condition) statement
while first evaluates condition
as a primitive boolean value (true or
false) and will execute statement
so long as condition is true. If
condition is initially false,
statement will not be executed.
while is an example of an iterative or looping
statement. Other examples of iterative statements in
JavaScript are for and do
while.
Our while statement looks like:
while (fahrenheit <= upper)
{
celsius = Fahrenheit2Celsius(fahrenheit);
msg(fahrenheit + '°F is ' + celsius + '°C');
fahrenheit = fahrenheit + step;
}
The condition is fahrenheit <=
upper. <= is a comparison operator
which returns true when the left operand (
fahrenheit in our example ) is less than or
equal to the right operand ( upper in our
example ).
The statement is the block statement
{
celsius = Fahrenheit2Celsius(fahrenheit);
msg(fahrenheit + '°F is ' + celsius + '°C');
fahrenheit = fahrenheit + step;
}
consisting of all statements between the braces
{ and }.
This while statement generates the
conversion table by repeatedly executing (looping
over) the set of statements contained in the block
statement as long as the condition
fahrenheit <= upper is
true.
The three statements inside of the block statement of
the while:
call the function Fahrenheit2Celsius
with the value of the argument fahr set to
the value of the variable fahrenheit then
set the value of the variable celsius to the
return value of the function.
output a message by calling the function
msg with the value of the argument set to
a primitive string consisting of the
concatenation of the fahrenheit
value, the string value '°F is ', the
celsius value and the string value
'°C'. JavaScript creates this string
value by automatically converting the primitive number
values in fahrenheit and celsius
to string values and the applying the string
concatenation operator +. Note that this
automatic conversion behavior is dramatically different
from other languages.
increment the value of the fahrenheit variable by the value of the variable step.
Note that the while loop will
terminate as soon as fahrenheit is greater than
the variable upper.
The final part of the program is the definition of the
function Fahrenheit2Celsius with argument
fahr.
function Fahrenheit2Celsius(fahr)
{
var cels = (5/9)*(fahr - 32);
return cels;
}
The body of the function consists of the two statements
contained within the braces { and
}. Each time the function is called, the
argument is initialized to the value passed by the caller
and the body of the function is executed. The
return value of the function is used as the
value of the function in whatever expression the function
call occured.
The first statement of Fahrenheit2Celsius
declares the variable cels in the scope
of the function which is initialized by the value of the
expression (5/9)*(fahr - 32). The expression
(5/9)*(fahr - 32) is evaluated by first
calculating the contents of the first parentheses
(5/9) by dividing the number 5 by
the number 9, then calculating the value of
the second parentheses (fahr - 32) by
subtracting the number 32 from the value in
the variable fahr, then multiplying the two
results together. Note that JavaScript automatically
converts the result of dividing two integers into a
floating point number unlike C. The next statement
returns the value of the variable cels as the
value of the function.
The scope of the function determines how the
values of variables are determined inside the function.
When a function is called, its scope consists of
the variable (or variables) defined as arguments to the
function along with the variables declared inside of the
function. Whenever an expression in the function refers to
a variable, JavaScript first tries to find the variable in
the function scope and if it is found, the value of that
variable is used. However if the variable is not found in
the function's scope, JavaScript will attempt to find the
variable in the parent scope of the function. In our
example, the parent scope of the function
Fahrenheit2Celsius is the global scope of the
program. The parent scope of a function is determined at
compile time and does not change regardless of the scope
where the function is executed. Once the function has
completed executing, any variables defined in the scope of
the function will cease to exist.
If a variable is used inside of a function
without being declared using the
var statement, JavaScript considers the
variable to part of the program's global scope. Declaring
function variables helps isolate functions from side
effects in programs and helps increase maintainability in
programs. For example, the Venkman JavaScript debugger,
available from mozilla.org, will display all variables
declared in function scope in a separate "local variables"
panel from the "global variables/properties". Considering
the large number of global variables and properties which
are defined by the browser's DOM, it is much easier to
inspect the variables used in a function if they are
declared as local.
Note that JavaScript does not require that a function be
declared or defined before it is used in a program. The
function Fahrenheit2Celsius could have been
located anywhere in the program.
Properties
JavaScript objects are named collections of
properties. Properties have names and can contain
primitive values such as strings, numbers or references to
other Objects. C++ and Java programmers
can think of a JavaScript object as similar (in their use)
as an instance of a struct or
class. JavaScript objects can be created
either by using literal object initializers using braces
({}) or using the new operator on
constructors.
For example, the following creates an empty object
literalObject using an literal object
initializer and another empty object
constructedObject using the new operator on the
Object()
constructor.
var literalObject = {};
var contructedObject = new Object();
Properties are set and retrieved using the member of operators
(.) or ([]). If variable
myobject references an object, then the property
named propname can be accessed in one of two
ways:
myobject.propnamemyobject['propname']where propname is some sequence of characters.
The only difference in these two approaches is the first
(myobject.propname) requires that
propname be an identifier, that is the
characters in propname must begin with a unicode letter, an
underscore (_) or a dollar sign
($) and the remaining characters must be
unicode letters, unicode digits, underscores or dollar
signs.
If propname is not an identifier, the second
syntax (myobject['propname']) can
still be used. For example myobject['this is a
property name that is not an identifier'] is a valid
property reference. The second syntax can also be used with
a variable containing the property name
(object[prop]) which is useful in
circumstances where the property name is not known until
the program runs.
Local versus Shared Properties
Every JavaScript object has a special internal property called its prototype object which is used to implement shared properties and object inheritance. To understand the role of the prototype object a little better, lets look at how JavaScript determines the value of an object's property.
When you access the value of an object's property
myobject.propname, JavaScript first
looks in myobject for a local property named
propname. If a local property with the requested
name is found, JavaScript will return the value of that
property. If the property is not found, JavaScript will try
to find the property in the object's prototype
object. If again the property is not found, JavaScript will
attempt to find the property in the prototype of the
prototype and so on until either the property is found or
the prototype chain ends. If the property was not
found, then JavaScript will return undefined
as the result.
As a result, JavaScript objects which share prototype objects effectively share the properties of the prototype (and those of the entire prototype chain).
A local property is defined when you assign a value to
an object property as in myobject.propname =
value;. The value of this property is not shared
with any other instances of objects. If a property is
shared via the prototype chain, any assignment to the local
property shadows the shared property effectively
replacing it for that object.
Literal Object initializers can be used to define local
properties and their initial values by listing each
property name followed by a colon (:),
followed by the property value as in
var literalObject = { property1: 'value1',
'property 2': 'value2' };
Each property/value pair (except the last) is
terminated by a comma (,). Note that property
names which are not identifiers (property 2 in
the above example) can be written as string literal
values.
// create an instance of Object using a literal initializer
var literalObject = {
property1: 'value1',
'property 2': 'value2'
};
// display the values of the properties
msg('literalObject.property1 = ' + literalObject.property1);
msg('literalObject[\'property 2\'] = ' + literalObject['property 2']);
To create a similar object using the
Object() constructor, we would first create
the object using new Object(), then create the
local properties by assigning values to them as in:
// create an instance of Object using a Constructor
var constructedObject = new Object();
// add properties to object
constructedObject.property1 = 'value1';
constructedObject['property 2'] = 'value2';
// display the values of the properties
msg('constructedObject.property1 = ' + constructedObject.property1);
msg('constructedObject[\'property 2\'] = ' + constructedObject['property 2']);
As you can see, the only difference in these examples is the means by which the objects were created.
Simple Object methods
Object properties are not limited to simple values such as strings or numbers but can also be functions which are also known as methods. For example, we can create an object which represents a circle which can report its area.
// define the method getArea
function getArea()
{
return Math.PI * this.radius * this.radius;
}
// create a 'circle' with radius 1
var circle = {radius: 1};
// attach the method to the circle object
// as a local property
circle.getArea = getArea;
msg('circle with radius ' + circle.radius +
' has area ' + circle.getArea());
This program first defines a function getArea
which will be used as a method of the circle
object. getArea returns the value of area of the
circle by calculating the square of the circle's
radius multiplied by π. this is a special
name which is used inside of methods to refer to the object
which contains the method. Inside of the getArea
method, this.radius refers to
circle.radius. Math is a built-in
JavaScript object which contains a number of properties and
methods useful in mathematical calculations.
Math.PI is the value of the mathemetical
constant PI (π).
The next statement creates the circle object via an object initializer to contain a numeric property called radius representing a radius of the circle.
The next statement creates a local property of the
circle object getArea by
assigning a reference to the function
getArea.
Simple Constructors
The simple approach to creating and using JavaScript
objects is useful when you have only a few objects with a
small number of properties each but can be awkward when you
have more objects to create. Consider the situation where
you have many instances of the same kind
of object
to create:
function getArea()
{
return Math.PI * this.radius * this.radius;
}
var circle1 = {radius: 1};
circle1.getArea = getArea;
var circle2 = {radius: 2};
circle2.getArea = getArea;
var circle3 = {radius: 3};
circle3.getArea = getArea;
…
The repetitive creation and initialization of the properties and methods of the various circle objects is boring and error prone. Fortunately, JavaScript provides the ability to encapsulate the creation and initialization of similar instances of objects through the use of constructors.
function Circle(radius)
{
this.radius = radius;
this.getArea = function ()
{
return Math.PI * (this.radius * this.radius);
};
}
// create an array to hold the circle objects
var circles = [];
var i;
// loop from i == 0 to 2 creating Circle objects
for (i = 0; i < 3; i++)
{
circles[i] = new Circle(i + 1);
}
for (i = 0; i < 3; i++)
{
msg('The area of a circle of radius ' +
circles[i].radius + ' is ' + circles[i].getArea());
}
The function Circle defines a constructor
for Circle objects. It takes one argument,
radius, which is a number representing the
radius of the circle.
The next statement uses an array initializer to
create an empty Array object called
circles. An Array is a kind of
JavaScript object used to store lists of items which can be
accessed by using the [] member of operator
and the numeric position (index) of the item in
the list. Items in Arrays are indexed from
0. For example, if list is an
Array, then list[0] is the first
item in the list, list[1] is the second item,
and so on.
The for loop is very similar to the
while loop we saw in the Fahrenheit conversion
example and is used to create 3 instances of
the Circle object which are stored in the
circles array.
for loops have the form:
for (initializer; conditional; increment) statement
and are equivalent to the following while
loop:
initializer;
while (conditional)
{
statement
increment;
}
The initializer expression is evaluated
first. It is used to set up the initial values before the
loop is executed. Then the conditional
expression is evaluated. If it is false, the
for loop terminates and the statement
following it in the program is executed. If the
condition is true, the statement is
executed and the initializer expression is
evaluated and the whole process begins again by testing if
the conditional is still true.
The new Circle(radius) statement creates an
instance of the Circle object by creating an
empty native JavaScript object, then setting its
prototype object to the prototype of the
function Circle, then calls the
Circle constructor with the this
identifier set to reference the newly created object. The
constructor adds the local properties radius
and getArea to the instance.
getArea is initialized with a function
expression which can be called like any other
function.
Since the properties radius and
getArea were created as local properties of
the Circle instance, they are created
independently for each instance of Circle.
Even though each instance of Circle has the same method
getArea, the compiled code is duplicated for
each separate instance thus increasing the memory usage if
there are many such instances created. We could have saved
the extra code by defining the function separately and
simply assigning a reference to the function to
getArea however that would have still meant we
needed an extra local property for getArea in
each instance. We will see later how to save both the
compiled code and the extra reference when we discuss
object prototypes in more detail.
Finally, the program outputs a message by concatenating
the string value 'The area of a circle of radius
', the value of the radius property of
the Circle instance, the string ' is
' and the value returned by calling the method
getArea of the Circle instance.
Note that constructor functions such as
Circle() should not be called as ordinary
functions unless they are specifically designed to be
called that way. The reason has to do with the value of the
this object reference inside of normal
function calls does not reference the object you expect.
For more details see Execution Contexts.
Object Inheritance
The style of creating objects using Simple
Constructors is sufficient for most cases however it
fails to handle cases where different kinds
of
objects need to share properties and methods. What is
needed is a way for us to create objects which
share properties and methods. What we need to do
is combine the use of constructors and prototypes.
Remember, constructors are functions (and also objects)
which the new operator acts upon to create
instances of the object defined by the
constructor.
Since constructors are objects, constructors also have a
prototype property which is conveniently named
prototype. When the new operator
acts upon a constructor, it first creates an empty native
JavaScript object (called the instance), then sets
the internal prototype property of the
instance to the prototype property of the
constructor, then the identifier this is set
to point to the new instance and the constructor is called.
The constructor can create local properties of the newly
created object by creating the properties on the
this object.
It is possible to create chains of constructors where the prototype of one constructor is an instance of another constructor. JavaScript uses these prototype chains to implement object-oriented inheritance.
Lets generalize our Circle object to be a part of a graphics program where we use families of objects to represent geometric shapes.
/*
* Circles and Squares
*/
// Define a Shape Object which is intended to serve as the ancestor
// of all "shapes" in the program.
function Shape(size)
{
if (size > 0)
{
// only define the size property as a local property
// of each instance if it is non-zero. Otherwise, the
// shared size property of the Shape Object's prototype
// object will be used.
this.size = size;
}
}
// Add a shared sized property to Shape's prototype
// Every instance of Shape has a default size of 0
// unless it is overridden by the constructor.
Shape.prototype.size = 0;
// Every instance of Shape has the same name. Implement it
// as a shared property so that only one instance of the name
// can be shared amongst all instances.
Shape.prototype.name = 'Shape';
// Every instance of Shape has an area which scales as the
// square of the size of the shape. Define a default getArea
// method which can be shared or overridden by child classes.
Shape.prototype.getArea = function _getArea()
{
return this.size * this.size;
}
/*
* Squares
*/
// Define a Square as a kind of Shape
function Square(size)
{
// call the Shape constructor on this
// object to initialize it
Shape.call(this, size);
}
// set Square's prototype to Shape to
// share Shape's properties
Square.prototype = new Shape;
// override the shared name property
Square.prototype.name = 'Square';
// Note that Square does not need to
// override Shape's getArea method
/*
* Circles
*/
// Define Circle as a kind of Shape
function Circle(size)
{
// call the Shape constructor on this
// object to initialize it
Shape.call(this, size);
}
// set Circle's prototype to Shape to
// share Shape's properties
Circle.prototype = new Shape;
// override the shared name property
Circle.prototype.name = 'Circle';
// override the getArea method to return the
// proper area of a Circle
Circle.prototype.getArea = function()
{
return Math.PI * Shape.prototype.getArea.call(this);
};
function tellMeAbout(shape)
{
var list = '';
for (var propname in shape)
list += propname + ', ';
msg(shape.name + ' has the following properties: ' + list);
msg('shape ' +
('size' in shape ? ' has ' : ' does not have ') +
'property size');
msg('shape ' +
(shape.hasOwnProperty('size') ? ' has ' : ' does not have ') +
'local property size');
msg('shape ' +
('name' in shape ? ' has ' : ' does not have ') +
'property name');
msg('shape ' +
(shape.hasOwnProperty('name') ? ' has ' : ' does not have ') +
'local property name');
msg('shape ' +
('getArea' in shape ? ' has ' : ' does not have ') +
'property getArea');
msg('shape ' +
(shape.hasOwnProperty('getArea') ? ' has ' : ' does not have ') +
'local property getArea');
msg('shape ' + shape.name + ' ' +
(shape instanceof Object ? 'is' : 'is not') +
' an instance of Object');
msg('shape ' + shape.name + ' ' +
(shape instanceof Shape ? 'is' : 'is not') +
' an instance of Shape');
msg('shape ' + shape.name + ' ' +
(shape instanceof Circle ? 'is' : 'is not') +
' an instance of Circle');
msg('shape ' + shape.name + ' ' +
(shape instanceof Square ? 'is' : 'is not') +
' an instance of Square');
msg('The area of a ' + shape.name + ' of size ' +
shape.size + ' is ' + shape.getArea());
}
var shape;
msg('<br>create an instance of Shape with no size<br>');
shape = new Shape;
tellMeAbout(shape);
msg('<br>create an instance of Circle with size 1<br>');
shape = new Circle(1);
tellMeAbout(shape);
msg('<br>create an instance of Square with size 2<br>');
shape = new Square(2);
tellMeAbout(shape);
This example is quite a bit longer than we have seen so far however it is really not any more complicated. There are four sections to the program:
In the first section of the program, the
Shape constructor is defined as a function which
takes one argument: the size of the object in
some unit of measure. The body of the Shape
constructor consists of an if statement.
if statements are used to select which set of
statements are to be executed depending on the value of a
conditional test and have the form:
if (condition) statement
If condition evaluates to true,
the statement will be executed, otherwise it
will be skipped. Another form allows the choice of one of
two possible statements depending on the
condition.
if (condition) statement1 else statement2
In this case, if condition evaluates to
true, statement1 will be executed,
but if condition is false, then
statement2 will be executed.
In our example, the condition is the test
size > 0. If the size is greater
than 0, the set of statements in the block (
the statements surrounded by the braces {} )
is executed. If the size is less than or equal
to 0, the local property this.size
is not defined in order to allow it to be shared as the
0 value. Since in our example a
Shape of 0 size doesn't make much
sense the trick doesn't add much to our example, but does
illustrate a technique where you can save memory usage by
sharing the most common value of a property and only
defining the property as local if the value differs.
Immediately following the Shape constructor,
the shared size property is added to
Shape's prototype object initialized to
0.
In order to allow our Shapes to know what
kind of shape they are, the next statement adds a shared
property name with value 'Shape' to
each instance of the Shape object. Note that
even if we create 100,000 Shapes, no additional
memory will be required to store the Shapes
names since each will share the name
property.
As the final part of the definition of Shape objects, a shared method getArea is defined. Since areas scale as the square of the size of an object, I went ahead and defined the shared method as such.
The second section of the program defines the
Square Objects. The constructor
Square is defined to take the same argument as
the Shape constructor. In order to
re-use the initialization code in
Shape, the call method of
Function objects is used to call
Shapes constructor. call allows us
to set the this value to point to our newly
created Square while passing any required
arguments.
Following the definition of the Square
constructor, the prototype property of
Square is initialized to be an instance
of the Shape Object. This step is what makes the
sharing of properties and methods between
Squares and Shapes possible. If a
property is not found as a local property of a
Square, Square's prototype is search
for the property and so on.
Next the name property is overridden since we want our Squares to know they are Squares and not just Shapes.
Since the area of a square is exactly the square of the length of its sides, there is no need to override the getArea method inherited from Shape.
The third section creates the Circle Object in a very similar fashion to that used to define Squares. The only difference in the overriding of the getArea method to apply the correct forumula for the area of a circle.
The fourth section of the program creates instances of Shape, Circle and Square and generates various reports about them. To simplify the program, a function tellMeAbout is defined to take an argument shape and then report various items about the shape such as:
tellMeAbout uses the operator in
which tests if a property exists in a object either as a
local property or as a property found in the object's
protocol chain. For example:
'propname' in object
will return true if
object.propname exists.
The operator in can also be used in a
for loop to enumerate each of an object's
properties so long as the properties do not have the
internal DontEnum attribute. For example, to
iterate over the list of enumerable properties in an
object:
var prop;
for (prop in object)
{
// prop is a string value
// containing the name of a property
}
The member function hasOwnProperty of
Function objects, is used to test if a
property is local or shared.
object.hasOwnProperty(propname)
returns true if propname is a local
property of object.
tellMeAbout also uses the
instanceof operator to test if an instance of
an object is an instance of a particular constructor. For
example:
object instanceof SomeConstructor
A new type of expression called the ternary or conditional expression is used throughout the function tellMeAbout. The conditional expression looks like:
condition ? expression1 : expression2
The value of the conditional expression is the
value of expression1 if the condition
evaluates to true. If condition is
false, the value of the conditional
expression is the value of expression2. This
operator is convenient shorthand for:
var value; if (condition) value = expression1; else value = expression2;
Data types in JavaScript are divided into two categories:
Primitive types whose members are primitive values. The primitive types in JavaScript consist of Undefined, Null, Boolean, Number and String.
Object types whose members are all objects. A JavaScript object is really just an unordered collection of properties which may have attributes governing how the properties behave.
Even though there is a one-one correspondence between some of the primitive types and some Object types, they are not the same.
The Undefined primitive type has only one value:
undefined which is used to represent the value a
variable contains when it has not been initialized.
The Null primitive type has only value:
null. While null and
undefined are used in similar circumstances, the
primary difference is that if a variable which has value
null has been intentionally initialized
to contain a value whereas an undefined value
can mean the variable is uninitialized or even does not exist
at all.
The Boolean primitive type has two values:
true and false. Boolean primitive
values are not the same as instances of Boolean
Objects.
The String primitive type are sequences of characters. String primitive values are not the same as instances of String Objects.
Literal primitive string values are written as a sequence
of characters surrounded either by single quotes
(') or double quotes ("). For
example,
"this is primitive string" and
'this is primitive string' are
both primitive string values.
To include quotes in a string literal, you can alternate
the type of quote as in
"this contains a single ' quote"
or you can use character escapes to temporarily
change the quote to a normal character as in
'this contains a single \' quote'.
Character escapes are a means of temporarily changing the
meaning of a character and are used to describe control
characters as well as all quotes to be included in literal
string. A character is escaped by preceding it
with a \ character, for example to escape the
letter a, you would write it as \a.
Section ECMAScript 7.8.4 String
Literals describes the character escapes \'
(escaped single quote), \" (escaped double
quote), \\ (escaped escape character),
\b (backspace), \f (form feed),
\n (newline), \r (carriage return),
\t (tab), \v (vertical tab) which
allow special characters to be included in string
literals.
MSIE does not support the \v vertical tab
character in literal strings. Since \v is
defined to be a white space character in Section
7.2 of the ECMAScript standard, MSIE also fails white space
handling involving \v.
MSIE does not recognize vertical tab '\v' in strings.
msg('\' supported ' + (String.fromCharCode(39) == '\''));
msg('\" supported ' + (String.fromCharCode(34) == '\"'));
msg('\\ supported ' + (String.fromCharCode(92) == '\\'));
msg('\b supported ' + (String.fromCharCode(8) == '\b'));
msg('\f supported ' + (String.fromCharCode(12) == '\f'));
msg('\n supported ' + (String.fromCharCode(10) == '\n'));
msg('\r supported ' + (String.fromCharCode(13) == '\r'));
msg('\t supported ' + (String.fromCharCode(9) == '\t'));
msg('\v supported ' + (String.fromCharCode(11) == '\v'));
The Number primitive type are the lowest level representation of integers and floating point numbers. Number primitive values are not the same as instances of Number Objects.
Literal primitive number values can be written as integers
(123), as floating point with decimals
(123.456), as floating point with exponents
(123.456e10, the e may be either
case), as hexadecimal integers (0xFF, the
x or hex digits A-F may be either case)
As I already mentioned, objects in JavaScript are collections of unordered properties. Each property of an object has a set of internal (not directly accessible to JavaScript programs) attributes which control how the property can be accessed: DontDelete, ReadOnly, and DontEnum.
delete operator. Any
attempt to delete the property is ignored without
error.in
operator is used in a for loop to enumerate
properties of an object. For example,
for (var propname in object) { … }
will not list the property if it is DontEnum.
However the in operator can be used to test
if the property exists. For example,
('propname' in object)
will return true if
object.propname exists and
false otherwise.
When JavaScript1.1 or JavaScript1.2 are used in Mozilla, attempting to delete a {DontDelete} property or attempting to change a {ReadOnly} property will throw an error.
try
{
Math.PI = 3;
msg('Math.PI == ' + Math.PI);
}
catch(e)
{
msg('Error assigning to a [ReadOnly] property. ' +
e.name + ': ' + e.message);
}
try
{
delete Math.PI;
msg('Math.PI == ' + Math.PI);
}
catch(e)
{
msg('Error deleting a [DontDelete] property. ' +
e.name + ': ' + e.message);
}
JavaScript automatically converts primitive values and object
instances from one type to another depending upon the context.
For example, as we have seen, when a number is added to
a string using the + operator, JavaScript will
determine that the appropriate operator is the string
concatenation operator and will convert the number to a string
before performing the operation.
| input | To Boolean | To Number | To String | To Object |
|---|---|---|---|---|
| Undefined | false |
NaN |
'undefined' |
throw TypeError |
| Null | false |
+0 |
'null' |
throw TypeError |
| Boolean | no conversion | 1 if input is true,
+0 if input is false |
'true' if input is true,
'false' if input is false |
new Boolean(input) |
| Number | false if input +0,
-0, or NaN otherwise
true |
no conversion | 'NaN' if input is NaN'0' if input is +0,
-0'Infinity' if input is Infinityotherwise a string primitive value consisting of the literal representation of the input value |
new Number(input) |
| String | false if string is empty, otherwise
true |
NaN if the input does not match a literal
number value, otherwise the number represented by the
equivalent literal number value. |
no conversion | new String(input) |
| Object | true |
convert the object's default value to a number | convert the object's default value to a string | no conversion |
Type Conversions to primitive types can be performed by using the constructors of the Native Object types as functions rather than as constructors. For example:
// Convert undefined to primitive values
msg('Boolean(undefined) is ' + Boolean(undefined));
msg('Boolean(null) is ' + Boolean(null));
msg('Boolean(10) is ' + Boolean(10));
msg('Boolean("42x") is ' + Boolean("42x"));
msg('Number(undefined) is ' + Number(undefined));
msg('Number(null) is ' + Number(null));
msg('Number(10) is ' + Number(10));
msg('Number("42x") is ' + Number("42x"));
msg('String(undefined) is ' + String(undefined));
msg('String(null) is ' + String(null));
msg('String(10) is ' + String(10));
msg('String("42x") is ' + String("42x"));
One surprising result of the Object to Boolean
conversion is that an instance of the
Boolean Object with value false
evaluates to true in conditional
expressions but the Object to String conversion
of the instance is the string 'false'.
var bool = new Boolean(false);
if (bool)
{
msg('new Boolean(false) is true');
}
else
{
msg('new Boolean(false) is false');
}
msg('String(new Boolean(false)) is ' + String(bool));
In Mozilla (JavaScript1.1, JavaScript1.2), new
Boolean(false) is converted to false
instead of true
When called as a function, the Number
constructor attempts to convert its argument to a number
value.
Mozilla JavaScript1.2 converts an Array object to its
length. Mozilla JavaScript1.1, JavaScript1.3-JavaScript1.5
converts an empty Array to 0, an Array with one
element to 1 and a non-empty Array with length
greater than 1 to NaN …
Number(array) is like
Number(array.toString()).
var empty = [];
var one = [1];
var nonempty = [1,2,3];
msg('Number(empty) == ' + Number(empty));
msg('Number(one) == ' + Number(one));
msg('Number(nonempty) == ' + Number(nonempty));
MSIE converts all strings representing negative hexadecimal
numbers to NaN.
msg('Number("0x10") == ' + Number("0x10"));
msg('Number("-0x10") == ' + Number("-0x10"));
Before any JavaScript code is executed, a unique Global object is created which has {DontEnum} properties for the standard built-in objects (Math, String, Date, etc.) and any properties created by the host environment.
The environment in which a JavaScript program is executing is maintained in a stack of objects called execution contexts. A stack, also known as a Last in First Out (LIFO) list is a list of data items where items are added (pushed) or removed (popped) from one end called the top of the stack. The execution context at the top of the stack is used by the executing program as the current context.
Each execution context maintains an internal object called the variable object which is used to hold references to the variables and functions created in that context. Each variable (or function) is stored in the variable object as a property with property name equal to the variable (or function) name.
Each execution context also maintains a scope chain
which is a list of objects which are searched when a variable or
name needs to be resolved. Whenever JavaScript needs to resolve a
reference to a name, it first looks in the current object in the
scope chain for a property with the same name. If it is found,
the data referenced by that propery is returned as the value of
the name. If the name is not found, the next object in the scope
chain is searched and so on until either the name has been
resolved or all objects in the scope chain have been searched.
Once created for a context, the scope chain is only effected by
with statements and catch clauses.
var v = 'global scope';
var o = {v: 'object scope'};
var e;
msg('global this == ' + this);
msg('global v == ' + v);
msg('global e == ' + e);
function test()
{
msg('function this == ' + this);
msg('function v == ' + v);
var v = 'function scope';
with (o)
{
msg('with this == ' + this);
msg('with o v == ' + v);
}
try
{
throw 'string exception';
}
catch(e)
{
msg('catch this == ' + this);
msg('catch e == ' +
e.name + ': ' + e.message);
e = 'catch scope';
}
msg('function e == ' + e);
msg('function v == ' + v);
}
test();
msg('global v == ' + v);
msg('global e == ' + e);
MSIE creates the catch variable e
containing the thrown exception in function scope. This means
the variable e exists after the catch
clause has finished executing but ceases to exist after the
function where the catch clause was located exits.
This is not the same as Mozilla which does not define the catch
variable outside of the catch clause.
Both MSIE and Mozilla bind the initial scope to a
function when it is created as a function expression. In the
following example, function f1() is bound to a
scope consisting of the global object while function
f2() is defined inside a with clause which
adds the object o to the beginning of the scope
followed by the global object.
var v = 'value 1';
var o = {v: 'value 2'};
var f1 = function () { msg('v == ' + v); };
with (o)
{
var f2 = function () { msg('v == ' + v); };
}
// call with the initial values
f1();
f2();
// now modify the values
v = 'modified value 1';
o.v = 'modified value 2';
f1();
f2();
However when the function is defined using a function
declaration, MSIE differs from Mozilla and binds the
function to the global scope regardless of any scope changes
introduced by with clauses.
var v = 'value 1';
var o = {v: 'value 2'};
function f1() { msg('v == ' + v); };
with (o)
{
function f2() { msg('v == ' + v); };
}
// call with the initial values
f1();
f2();
// now modify the values
v = 'modified value 1';
o.v = 'modified value 2';
f1();
f2();
Each execution context maintains a special value called the
this value which is determined by how the context
was created and the type of code being executed. The
this value can not be changed to point to a
different object, although properties can be added or removed
from it.
Whenever a JavaScript program begins, a Global
execution context is created and pushed onto the top of
the stack. The scope chain is initialized to contain
just the global object and the this value is set
to point to the global object. Variables are created as
{DontDelete} properties of the global object.
var globalVar = 'this is a global variable';
msg('globalVar = ' + globalVar);
msg('this.globalVar = ' + globalVar);
try
{
// it should not be possible delete the variable
// but no error should be thrown.
delete this.globalVar;
msg('this.globalVar after delete = ' + this.globalVar);
}
catch(e)
{
msg('An unexpected error occured deleting a globalVar variable ' +
e.name + ': ' + e.message);
}
Mozilla (JavaScript1.1, JavaScript1.2) will throw an error when deleting a global variable since it is defined as {DontDelete}. Note that Mozilla JavaScript1.3 and later do not have the behavior and that Mozilla JavaScript1.1, JavaScript1.2 do not throw errors when deleting function local variables even though they are also {DontDelete}.
Whenever JavaScript code is compiled using the
eval function, an eval execution
context is created and pushed onto the stack until the
eval'd code completes when the eval execution
context is removed from the stack.
The calling execution context determines the scope chain,
the variable object and the this value for the
eval execution context. If there is no calling context, the
scope chain, variable object and the this value
are determined in the same way as for the global execution
context.
eval("var evalVar = 'this is an eval variable'");
msg('evalVar = ' + evalVar);
msg('this.evalVar = ' + evalVar);
try
{
// it should be possible delete the variable
// and no error should be thrown.
delete this.evalVar;
msg('this.evalVar after delete = ' + this.evalVar);
}
catch(e)
{
msg('An unexpected error occured deleting a global variable ' +
e.name + ': ' + e.message);
}
Each time a function call is performed, a function execution context is created and pushed onto the top of the stack and then removed from the stack when the function call ends.
An Activation Object is created in the execution context for each function call and is treated as the variable object for the execution context. The scope chain for the context is created by placing the activation object in front of the scope chain of the calling execution context.
An arguments object is added as a {DontEnum}
property to the variable. The arguments object
is initialized with the following properties:
calleea {DontEnum} property which points to the function object being executed.
caller (non-standard)In Mozilla and MSIE, the non-standard
caller property was used to point to the
function which called the currently executing function.
This property has been removed from recent releases of
both browsers due to security considerations.
function Caller()
{
msg('Caller: Caller.arguments ' + Caller.arguments);
Callee();
}
function Callee()
{
msg('Callee: arguments.caller ' + arguments.caller);
}
Caller();
lengtha {DontEnum} property which contains the number of actual arguments in the function call.
For each argument supplied, a {DontEnum} property of
the same name is created in the
arguments.
Mozilla JavaScript1.1, JavaScript1.2 will allow function arguments to be enumerated as zero based indexes into the arguments list. This is not possible in Mozilla JavaScript1.3 or later.
In normal function calls, the this value
remains unchanged from that of the calling context however in
the context of a constructor call (new
SomeFunction()), the this value is set to
the newly created JavaScript object.
An example of a normal function call…
var global = this;
var avar = 'this is a global variable';
func('this is arg0', 'this is arg1');
msg('After func this === global is ' + (this === global));
msg('After func avar should be unchanged ' + avar);
function func(arg0, arg1, arg2)
{
// the this value should point to the global object
msg('Inside func this === global is ' + (this === global));
// declare a varible local to the function
var avar = 'this is a local variable';
msg('Inside func avar == ' + avar);
// attempt to delete the local variable
try
{
msg('delete avar == ' + (delete avar));
}
catch(e)
{
msg('an error occured during delete avar ' +
e.name + ': ' + e.message);
}
msg('typeof avar == ' + typeof avar);
// the length of the function should be the number
// of defined arguments. In this case 3.
msg('func.length is ' + func.length);
msg('arguments ' + arguments);
// the length of the arguments object should be the
// number of actual arguments the function was called with.
// In this example it should be 2.
msg('arguments.length is ' + arguments.length);
// this function
msg('arguments.callee <pre>' + arguments.callee +
'</pre>');
// the caller function which will be undefined.
msg('arguments.caller <pre>' + arguments.caller + '</pre>');
// the properties of the arguments object should be {DontEnum}
// so this should output the empty string.
var arglist = '';
for (var arg in arguments)
{
arglist += arg + ', ';
}
msg('Enumerable properties of arguments ' + arglist);
}
An example of a constructor function call…
var global = this;
var avar = 'this is a global variable';
var instance = new func('this is arg0', 'this is arg1');
msg('After func this === global is ' + (this === global));
msg('this.propname should be undefined ' + this.propname);
msg('instance.propname should be defined ' + instance.propname);
function func(arg0, arg1, arg2)
{
// the this value should point to the newly created object
// and _not_ the global object.
msg('Inside func this === global is ' + (this === global));
// add a property to this instance
this.propname = 'a property in our new object';
// attempt to delete the local variable
try
{
msg('delete avar == ' + (delete avar));
}
catch(e)
{
msg('an error occured during delete avar ' +
e.name + ': ' + e.message);
}
msg('typeof avar == ' + typeof avar);
// the length of the function should be the number
// of defined arguments. In this case 3.
msg('func.length is ' + func.length);
msg('arguments ' + arguments);
// the length of the arguments object should be the
// number of actual arguments the function was called with.
// In this example it should be 2.
msg('arguments.length is ' + arguments.length);
// this function
msg('arguments.callee <pre>' + arguments.callee +
'</pre>');
// the caller function which will be undefined.
msg('arguments.caller <pre>' + arguments.caller + '</pre>');
// the properties of the arguments object should be {DontEnum}
// so this should output the empty string.
var arglist = '';
for (var arg in arguments)
{
arglist += arg + ', ';
}
msg('Enumerable properties of arguments ' + arglist);
}
An identifier is a sequence of characters beginning with
either $ (dollar sign), _
(underscore) or a unicode letter followed
by unicode letters, unicode digits, underscores
(_) or dollar signs ($). The
characters can also be written using unicode escape
sequences. Note that identifiers are not restricted to the
normal latin character set, but are instead allowed
to have letters and digits from the unicode character
set.
// Valid Identifiers
// latin type variable identifiers
var _foo123;
var $foo123;
var foo123;
var foo_123$
// hindi variable identifier using unicode escapes
var \u092F\u0942\u0928\u093F\u0915\u094B\u0921 = 'unicode';
msg('\u092F\u0942\u0928\u093F\u0915\u094B\u0921 == ' +
\u092F\u0942\u0928\u093F\u0915\u094B\u0921);
// hindi variable identifier using unicode characters
var यूनिकोड = 'unicode';
msg('यूनिकोड == ' + यूनिकोड);
msg('Test passed');
// Invalid Identifiers
// Mozilla will fire a window.onerror or exception handler
// for compile time errors, however MSIE will not.
try
{
eval('var 0foo;');
// the next statement is an error
// the next statement should not be executed
msg('Test failed: invalid identifier 0foo == ' + 0foo);
}
catch(e)
{
msg('Test passed: 0foo is an invalid identifier ' +
e.name + ': ' + e.message);
}
Keywords are identifiers which have special meanings in JavaScript and should not be used as variable or property names. Reserved words are identifiers which may be used as keywords in future versions of JavaScript and should not be used as variable or property names.
Example 7.5.1 - Reserved Words
// test if a word can be declared as var
function testvar(words)
{
var e;
for (var i = 0; i < words.length; i++)
{
var word = words[i];
try
{
eval('var ' + word + ';');
msg('test failed: ' + word + ' is declarable');
}
catch(e)
{
msg('test passed: ' + word + ' is not declarable ' +
e.name + ': ' + e.message);
}
}
}
// test if a word can be assigned to
function testwrite(words)
{
var e;
for (var i = 0; i < words.length; i++)
{
var word = words[i];
try
{
eval(word + ' = "foo";');
msg('test failed: ' + word + ' is writable');
}
catch(e)
{
msg('test passed: ' + word + ' is not writable ' +
e.name + ': ' + e.message);
}
}
}
// keywords
var keywords =
['break', 'else', 'new', 'var', 'case',
'finally', 'return', 'void', 'catch', 'for',
'switch', 'while', 'continue', 'function', 'this',
'with', 'default', 'if', 'throw', 'delete',
'in', 'try', 'do', 'instanceof', 'typeof'];
// future reserved words
var reserved =
['abstract', 'enum', 'int', 'short', 'boolean',
'export', 'interface', 'static', 'byte', 'extends',
'long', 'super', 'char', 'final', 'native',
'synchronized', 'class', 'float', 'package', 'throws',
'const', 'goto', 'private', 'transient', 'debugger',
'implements', 'protected', 'volatile', 'double', 'import',
'public'];
testvar(keywords);
testvar(reserved);
testwrite(keywords);
testwrite(reserved);
MSIE incorrectly allows the following to be declared as
variables and assigned values: abstract,
int, short, boolean,
interface, static,
byte, long, char,
final, native,
synchronized, float,
package, throws,
goto, private,
transient, implements,
protected, volatile,
double, public.
In Mozilla 1.7 (Firefox 1.0) JavaScript 1.1, JavaScript 1.2 instanceof, enum, export, debugger are declarable and writable however in Mozilla 1.7 (Firefox 1.0) for JavaScript 1.3 and later the other keywords and reserved words are not declarable or writable.
Mozilla 1.8 (Firefox 1.1) and later (Bugzilla
240317) relax the declaration and assignment of
reserved identifiers to improve MSIE compatibility. The
following words will no longer throw errors but will still
issue JavaScript warnings: abstract,
enum, int, short,
boolean, interface,
static, byte,
extends, long,
super, char, final,
native, synchronized,
class, float,
package, throws,
goto, private,
transient, implements,
protected, volatile,
double, public
Mozilla can define functions in conditionals. If the code branch is not executed due to the conditional the function is not defined. MSIE however uses the definition of the last occurence of the function. Both Mozilla and MSIE can conditionally define function expressions.
// works in Mozilla, not MSIE
if (true)
{
function f() { return 'true'; };
}
else
{
function f() { return false; };
}
msg('Conditional Function: ' + f());
// works in Mozilla and MSIE
var f;
if (true)
{
f = function () { return 'true'; };
}
else
{
f = function () { return false; };
}
msg('Conditional Function: ' + f());
MSIE violates the ECMSAcript standard since it can reference a function name outside of the function expression.
f = function foo(){alert('foo');};
foo();
| Description | Operator | Associativity |
|---|---|---|
| member of | []
. |
left to right |
| (grouping | function call), create instance | ()
new |
right to left |
| unary | !
~
++
--
+
-
typeof
void
delete |
right to left |
| multiplicative | *
/
% |
left to right |
| addition | + - |
left to right |
| bitwise shift | <<
>>
>>> |
left to right |
| relational | <
<=
>
>=
in instanceof |
left to right |
| equality | == ===
!=
!== |
left to right |
| bitwise and | & |
left to right |
| bitwise xor | ^ |
left to right |
| bitwise or | | |
left to right |
| logical and | && |
left to right |
| logical or | || |
left to right |
| conditional | ?: |
right to left |
| assignment | =
*=
/=
%=
-=
<<=
>>=
>>>=
&=
^=
|= |
right to left |
| comma | , |
left to right |
Mozilla has the ability to define getter and setter methods for user defined objects. Note that Mozilla introduced this feature in JavaScript 1.5, however it is available for all language versions in Mozilla.
deprecated - can only create getters and setters as local properties.
{_prop: stuff, get prop() {...}, set prop() {...}}
or
instance.propname getter = function(arglist) {...};
or
instance.propname setter = function(arglist) {...};
recommended - can create getters and setters as shared properties.
someObject.prototype.__defineGetter__('propname',
function(arglist) )
someObject.prototype.__lookupGetter__('propname');
and
someObject.prototype.__defineSetter__('propname',
function(arglist) )
someObject.prototype.__lookupSetter__('propname');
var deprecated_1 = {
_prop: 'default value',
get prop() { return this._prop; },
set prop(v) { return this._prop = v; }
}
msg('deprecated_1.prop == ' + deprecated_1.prop);
deprecated_1.prop = 'foo';
msg('deprecated_1.prop == ' + deprecated_1.prop);
var deprecated_2 = {
_prop: 'default value'
};
deprecated_2.prop getter =
function() { return this._prop; };
deprecated_2.prop setter =
function(v) { return this._prop = v; };
msg('deprecated_2.prop == ' + deprecated_2.prop);
deprecated_2.prop = 'foo';
msg('deprecated_2.prop == ' + deprecated_2.prop);
function NonDeprecated(v)
{
this.prop = v;
}
NonDeprecated.prototype.__defineGetter__('prop',
function() {
return this._prop;
}
);
NonDeprecated.prototype.__defineSetter__('prop',
function (v) {
return this._prop = v;
}
);
var nondeprecated = new NonDeprecated('initial value');
// call getter
msg('nondeprecated.prop == ' + nondeprecated.prop);
// call setter
nondeprecated.prop = 'foo';
msg('nondeprecated.prop == ' + nondeprecated.prop);
// lookup getter
msg('nondeprecated.__lookupGetter__(\'prop\') == ' +
nondeprecated.__lookupGetter__('prop'));
// lookup setter
msg('nondeprecated.__lookupSetter__(\'prop\') == ' +
nondeprecated.__lookupSetter__('prop'));
The __noSuchMethod__ handler was introduced in
Mozilla 1.6 in Bug
196097 '__noSuchMethod__' handler for trapping calls to
undefined object methods.
// define on an object instance
var obj = {};
obj.__noSuchMethod__ =
function(id, args) {
msg('Undefined method ' + id +
' called with arguments (' + args.join(', ') + ')');
};
obj.foo('bar');
// define on an object prototype
Object.prototype.__noSuchMethod__ =
function(id, args) {
msg('Undefined method ' + id +
' called with arguments (' + args.join(', ') + ')');
};
var date = new Date();
date.baz('foobar');
[]) OperatorSyntax
expression[propertyexpression]
Returns the value of a property from an instance of an object.
The result of evaluating expression must be
or be convertable to an instance of an object, otherwise
a TypeError is thrown. For example, it is a
TypeError to attempt to access or set a
property on undefined or null
values.
try
{
null['prop'] = 'foo';
}
catch(e)
{
msg(e.name + ': ' + e.message)
}
The name of the property is obtained by evaluating propertyexpression and converting the result to a string primitive value. There is no restriction on the characters which can be used as part of the property name.
var object = {};
object['0' + Math.PI] = 'bar';