Purchase Advertising On AWT
 Upload & Sell Your Software
 Upload & Sell Your eBook
 Enter The AWT Control Panel  Contact Applied Web Tools
 Sell Your eBook @ The PDF Store  Password Troubles? Click Here!
Knowledge Center
Want to write for Applied Web Tools? Send your Writer's Query to
Editor@AppliedWebTools.net for details.
HTML
Reference
&
Tutorial
CSS
Reference
&
Tutorial
DOM
Reference
&
Tutorial
JavaScript
Reference
&
Tutorial
50+ Programming Examples
Use HTML, CSS, DOM & JavaScript
Misc. Essays & Documents
You'll Find Just About Anything In Here
JavaScript Constructions
JavaScript
Fundamentals

26 pages, 9372 words
JavaScript
Constructions

34 pages, 12501 words
JavaScript
On The Web

50 pages, 18564 words
This document takes 20 to 35 minutes to read
In Chapter 1 we covered some of the basics of JavaScript. We delved quite deep into a few of the concepts that people struggle with when learning the language. We didn’t really address the language as a whole, though, which is what we’ll do now. In this chapter we’ll dive into the details we glossed over in Chapter 1 and get to the nuts and bolts of the language. We’ll also discuss some of the things we touched on in Chapter 1 in more detail.

This chapter will provide you with a solid grounding in the JavaScript language, and will do so in a way that’s both accessible to novices to the language and still a valuable reference for the experienced JavaScript developer. Our hope is that, as you progress in your JavaScript development skills, you’ll refer to this chapter both to remind yourself of the basics and to dive into specific topics more deeply.

We will begin by reviewing some basic matters of formatting JavaScript code, especially as related to the examples in this book. Then we will cover expressions and statements, the two most basic building blocks of JavaScript from which all JavaScript programs are built. With that groundwork laid, we can then discuss creating more complex statements with operators. We will talk about variables and how to manage them in your JavaScript programs. Then we will discuss objects and arrays, which will give you the building blocks for everything else. Then we will have an in-depth discussion of functions: what they are, and how to make them, and we will gain some important insights into the dynamic nature of JavaScript. Finally, we will cover how to control our programs with conditionals and loops.

By the end of this chapter, you should have a solid understanding of JavaScript’s lexical structure and syntax, and should feel comfortable using its basic constructs for flow control and functionality.

Note : Throughout this chapter, we will be referring to, and even quoting directly, the ECMA-262 standard, the current version of which is ECMAScript Language Specification, 5.1 Edition. You are encouraged to explore the standard itself at www.ecma-international.org/ecma-262/5.1/ (which also provides a link for a downloadable PDF version), as this is an excellent way of expanding your understanding of JavaScript.

Formatting JavaScript Code

Formatting code is one of the many subjects that will invariably result in a roomful of angry developers shouting at each other. (I once saw someone nearly throw a chair in the middle of an argument about indenting with spaces vs. tabs.) Even though it’s a touchy subject, this reference would be remiss without at least laying the groundwork for future arguments, as well as defining the conventions we will use throughout this book.

Broadly, JavaScript uses C-like formatting. Most notably, JavaScript uses curly brackets ( { }) to denote blocks of code, like loops or logical flow control.

JavaScript also uses two styles of comment delimiters. The double-slash (//) is the single-line delimiter, which indicates that everything from that point to the end of the line is a comment. JavaScript also uses /* to denote the beginning of a multiline comment and */ to indicate the end. Anything contained within those delimiters, regardless of new lines, is considered a comment.

Whitespace, including indentation, for the most part is unimportant. To quote Section 7.2 of the ECMA-262 standard: “White space characters are used to improve source text readability and to separate tokens (indivisible lexical units) from each other, but are otherwise insignificant.” JavaScript doesn’t care if you indent with tabs or spaces, or even if you indent at all. Similarly, JavaScript imposes no requirements for new lines. In fact, it’s common to “compress” JavaScript by removing all whitespace and running everything together on one line for the sake of reducing file size (see Chapter 4 for more information on compressing JavaScript).

JavaScript uses semicolons (;) to terminate statements. However, semicolons can be considered optional because JavaScript interpreters practice automatic semicolon insertion (ASI), which means they attempt to correct code that would be nonfunctional without semicolons by automatically inserting them as needed. As a result you can choose to write your JavaScript without using many (or even any) semicolons, and instead rely on ASI. Traditionally, it has been considered a best practice to explicitly use semicolons to terminate statements. However, with the advent of newer meta-scripting languages like CoffeeScript, many people now prefer to write terse code that employs a minimum of semicolons and instead relies on ASI as much as possible.

From a practical standpoint, either method is acceptable in that either method will help produce consistent, functional code. However, as with anything involving programming style, there have been many heated arguments recently about explicit semicolon use versus relying on ASI.

Relying on ASI

ASI follows a well-defined set of rules laid out in the ECMA-262 standard (Section 7.9 of Edition 5.1). If you would like to write JavaScript without semicolons, you are encouraged to review the standard so you know exactly what you are doing. We won’t cover the rules in detail here, but if you would like to rely on ASI, there are some important things to bear in mind.

Broadly, if the JavaScript engine encounters a new line (or a curly brace, though ASI is invoked mostly for new lines) that is used to break up tokens that otherwise don’t belong together, JavaScript will insert a semicolon—but only if it needs to do so in order to create syntactically valid code: code that the interpreter can successfully parse and execute. But the interpreter does not care if the code results in an error when it is executed. It only cares that the code can be executed.

To illustrate this, consider the two lines of JavaScript shown in Listing 2-1.

Listing 2-1. JavaScript with No Semicolons

myResult = argX - argY

myFunction()

If the interpreter were to encounter this code, it would determine that indeed a semicolon is needed to make this code functional, and it would insert one (Listing 2-2):

Listing 2-2. Result of ASI on Listing 2-1

myResult = argX - argY;

myFunction()

On the other hand, consider the two lines of code in Listing 2-3.

Listing 2-3. More JavaScript with No Semicolons

myResult = argX - argY

[myResult].myProperty = "foo"

In this case, the interpreter would not insert a semicolon because, even though there is a new line, a semicolon isn’t needed to make the code functional. Instead, the interpreter would assume we meant what you see in Listing 2-4.

Listing 2-4. What the Interpreter Thinks Listing 2-3 Means

myResult = argX - argY[myResult].myProperty = "foo";

If you actually run the example, your browser will throw a Reference Error complaining about an invalid assignment. The = operator is JavaScript’s assignment operator, and JavaScript expects assignments to be formed such that the left operand takes the value of the right operand. In this example, JavaScript is unable to determine what you even mean in the left operand, let alone be able to use the result to assign a value.

This is a contrived example, but it does expose the main consideration when relying on ASI: you have to understand the rules in order to use it effectively, whereas explicitly using semicolons leaves no doubt. And not only do you have to understand the rules, anyone who will be working with you on your code will have to understand them as well.

Be Consistent

Every programmer has their own personal opinions about programming style, which is fine; what’s important is to pick one way of doing things and be consistent. Consistently written code is much easier to read and understand than code written with multiple bracketing styles, inconsistent indentation rules, and variable naming conventions. To that end, in this book we employ the following styles for the sake of consistency:


Semicolons: We use semicolons explicitly (instead of relying on ASI).


Brackets: We use the so-called “one true bracketing style,” where opening brackets are placed on the same line as their associated statements, and closing brackets are on their own line at the same indention as their associated statements.


Variable naming: By and large, properties are nouns and methods are verbs. In some examples, we rely on a variation of “Hungarian notation,” wherein variable names are prefixed with an indication of their type or functionality (e.g., intCounter or strMessage), just for the sake of being even more explicit about the variable’s use or role within the example.

These particular choices aren’t meant to be singled out as better than others. When deciding which styles to use in your projects, you should pick what works best for you, your team, and your situation. Consistency is what is important.

Expressions and Statements

Expressions and statements are the first things to understand really well about JavaScript because they are the basic building blocks for JavaScript programs. The distinction between expressions and statements is simple, but subtle.

Expressions

Conceptually, expressions are like words or phrases in a spoken language. They are the simplest building blocks of a program. In JavaScript, an expression is any section of code that resolves to a value. Since literal expressions evaluate to actual values, JavaScript supports the same broad types of expressions as it does of variables: boolean, number, string, and object. Expressions can be as simple as just stating a value, or they can be mathematical or logical operations, as shown in Listing 2-5:

Listing 2-5. JavaScript Literal Expressions

10 // Literal expression, resolves to 10

"Hello World" // Literal expression, resolves to the string "Hello World"

3+7 // Mathematical expression, resolves to 10

You can also write compound expressions. A compound expression is an expression in which one (or more) of the items in the expression is another expression. Compound expressions can be as complex and as nested as needed, as in Listing 2-6:

Listing 2-6. Compound Expressions

(3+7)/(5+5) // evaluates to 1

Math.sqrt(100) // evaluates to 10

One of the most common places you’ll encounter expressions is in a conditional, as demonstrated in Listing 2-7.

Listing 2-7. Compound Expression in a Conditional

if ((myString === "Hello World") && (myNumber > 10)) {

// conditional code here

}

In this example, we have a compound expression consisting of two expressions, one testing the value of myString and the other testing the value of myNumber, which will evaluate to either true or false. Those expressions are included in a single logical AND expression, so if both evaluate to true, the conditional code will execute. (We’ll talk more about nested multiple expressions in a bit; for now, just concentrate on each individual expression as the boolean that it represents.)

Finally, though an expression can stand on its own, as shown in Listing 2-8, such an expression typically is not very useful.

Listing 2-8. A Not-So-Useful Literal Expression

var myNumber = 10,

myOtherNumber = 20;

"hello world"; // um, okay?

if (myOtherNumber > myNumber) {

alert("Condition was true!"); // will alert because conditional is true

}

This code will execute without throwing an error, and will alert “Condition was true!” as expected. The literal expression on the third line of Listing 2-8 is perfectly valid, though it is not doing anything useful. To actually do something, literal expressions are usually combined with operators: an assignment (using the = operator), a conditional (using logic operators), and so forth.

The bottom line about expressions (even compound expressions) is that they only represent values. If you want to actually do anything with those values, you need to use a statement.

Statements

In JavaScript, a statement is a collection of one or more expressions that performs a specific action. To return to the spoken language analogy, if expressions are words and phrases, then statements are full sentences. Conceptually, the simplest type of statement is an expression that has a side effect, such as variable assignment or a simple mathematical operation. See Listing 2-9 for some examples.

Listing 2-9. Simple Statements

var x = 5, // variable assignment, a statement

y = 3,

z = x + y; // mathematical operation, also a statement

Sometimes these simple statements are referred to as expression statements to underline the fact that they are essentially expressions with side effects. However, that term can confuse the subtle distinction between expressions and statements, so in this book we will not use it.

Just as JavaScript has compound expressions, it also has compound statements. A compound statement is a collection of statements in a block of code, often enclosed in curly brackets. Excellent examples of compound statements are if statements and loops, shown in Listing 2-10.

Listing 2-10. if Statements and Loops Are Compound Statements

if (expression) {

// conditional statement--often a compound statement because it contains multiple statements.

}

for (expression) {

// repeated statement--often a compound statement because it contains multiple statements.

}

Note, however, that not every section of code enclosed in curly brackets is necessarily a statement. Object literals, for example, are expressions, not statements, despite being multiple expressions enclosed in brackets, as you can see in Listing 2-11.

Listing 2-11. An Object Literal Is Not a Statement

{

prop1: "value",

prop2: "value2"

}

However, as long as you remember that expressions (even compound expressions) represent values and nothing else, the fact that an object literal is not a statement should be clear because an object literal is simply the specification of an actual object value. For more information on object literals, see the “Objects” section later in this chapter.

Operators

Operators, perhaps unsurprisingly, perform operations on expressions. Operators perform their function (“operate”) on operands. Most JavaScript operators are binary, meaning they take two operands, typically in this format:

operand1 operator operand2

Probably the most commonly used binary operator in JavaScript is the assignment operator, =. Other examples include mathematical operators and most logical operators.

A few JavaScript operators are unary, meaning they take only one operand; for example:

operand operator

or

operator operand

The order of operand and operator depends on both the operator in question and, sometimes, what you’re trying to do with the operator. Examples include the logical NOT operator or the mathematical negation operator.

In addition, JavaScript has one ternary operator, known as the conditional operator. It takes three operands and performs a conditional test:

conditionalExpression ? valueIfTrue : valueIfFalse

The conditional operator allows you to write more terse code than if you explicitly used an if-then-else statement and can be used anywhere you would use a standard operator.

JavaScript operators fall into the following broad categories:


Arithmetic operators: Perform arithmetical operations on their operands, such as addition, multiplication, etc.


Assignment operators: Modify variables, either by assigning their values or altering their values according to specific rules.


Bitwise operators: Treat their operands as a set of 32 bits, and perform their operations in that context.


Comparison operators: Compare their operands and return a logical value (true or false) based on whether or not the comparison is true.


Logical operators: Perform logical operations on their operands and are often used to link together multiple comparisons.


String operators: Perform operations on two strings, such as concatenation.


Miscellaneous operators: Operators that don’t fall into any of the above categories. This category includes the conditional operator and operators such as the void operator and the comma operator.

We’re not going to cover every single operator in detail in this chapter; that reference is available in Chapter 7. However, there is one important operator concept we want to cover here: precedence.

Precedence

If you have multiple operators in one statement, how do you determine the order in which to execute them? Do you evaluate them strictly left to right? Are there other rules? Different orders of execution can produce different results depending on the operators and their operands, so it’s important to have a standard way of approaching this problem.

Consider the example in Listing 2-12 involving mathematical operators.

Listing 2-12. Multiple Mathematical Operators in a Single Statement

var myVar = 5 + 7 * 3 + 4 - 2 * 8;

alert(myVar); // what will this alert?

If you evaluate the statement in Listing 2-12 from left to right, performing each operation as you come to it, you end up with 304. However, the example actually alerts 14, because some operators are evaluated before others according to a set of rules known as precedence. In this example, multiplication has a higher precedence than addition or subtraction, so that statement is actually evaluated as shown in Listing 2-13, which uses parentheses to indicate precedence explicitly by grouping together the operations as they are actually evaluated.

Listing 2-13. Using Parentheses to Demonstrate Precedence Explicitly

var myVar = ((5 + (7 * 3)) + 4) - (2 * 8);

alert(myVar); // will alert 14

As it happens, mathematical operator precedence in JavaScript follows the precedence rules of mathematics itself: items in parentheses or brackets are evaluated first, followed by exponents and roots, followed by multiplication and division, followed by addition and subtraction.

Listing 2-14 provides another example, involving just addition and subtraction, two operators that are of the same precedence.

Listing 2-14. Multiple Operators of the Same Precedence

var myVar = 5 + 6 - 7 + 10;

alert(myVar); // what will this alert?

What’s the value of myVar? It depends on the order in which you execute the operations. It would be 14 if you evaluated it from left to right, or it would be –6 if you evaluated it as (5 + 6) – (7 + 10).

When you have multiple operators of the same precedence together, they will evaluate according to their associativity: either left to right, or right to left. In the case of mathematical operators, they are all evaluated left to right, so the value of myVar is 14.

Because JavaScript has more than just mathematical operators, it has slightly more complex precedence rules than those that come with mathematics, as you can see in Table 2-1.

Table 2-1. Operator Precedence in JavaScript

Precedence Operator Type Associativity Individual Operator(s)

1 Member Left to right ., []

New Right to left new

2 Function call Left to right ()

3 Increment Not applicable (unary) ++

Decrement Not applicable (unary) --

4 Logical NOT Right to left !

Bitwise NOT Right to left ~

Unary + Right to left +

Unary negation Right to left -

Typeof Right to left typeof

Void Right to left void

Delete Right to left delete

5 Multiplication Left to right *

Division Left to right /

Modulus Left to right %

6 Addition Left to right +

Subtraction Left to right -

7 Bitwise shift Left to right <<, >>, >>>

8 Relational In Instanceof Left to right Left to right Left to right <, <=, >, >= in instanceof

9 Equality Left to right ==, !=, ===, !==

10 Bitwise AND Left to right &

11 Bitwise XOR Left to right ^

12 Bitwise OR Left to right |

13 Logical AND Left to right &&

14 Logical OR Left to right ||

15 Conditional Right to left ? :

16 Yield Right to left yield

17 Assignment Right to left =, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, !=

18 Comma Left to right ,

Understanding operator precedence is important; otherwise, your statements might produce unexpected results. Even so, many JavaScript best practices and style guides recommend that, for complex statements with multiple operators, you explicitly state with parentheses the precedence you are intending. Generally, that makes for more readable code and easier maintenance, though if you have an extremely complex statement, you can end up with lots of parentheses. In that case, it might be worthwhile to break up the single statement into one or more statements, to be fully explicit and reduce the overall number of parentheses.

Variables

Broadly speaking, a variable is a named storage location with an associated value. You access the value associated with the storage location by using the name. Each language has its own implementation of variables: how to declare them, what their scope is, and how they are managed.

Declaring Variables in JavaScript

In JavaScript, variables are declared using the var keyword, as shown in Listing 2-15.

Listing 2-15. Declaring a Variable in JavaScript

var myVar = 1;

You can also simply access variables as needed without formally declaring them using the var keyword (Listing 2-16).

Listing 2-16. Creating a New Variable by Accessing It

var myVar = 1;

myOtherVar = 2;

Either way of declaring variables is syntactically valid, but they have different meanings for the variable’s scope (described in the following section).

It’s common practice to declare many variables at once. You can use the var keyword for each variable, or you can use the var keyword once and separate the variable declarations with commas. It’s also common practice to place each variable declaration on its own line, as shown in Listing 2-17, for improved readability.

Listing 2-17. Declaring Multiple Variables at Once

var myObject = {},

intCounter = 0,

strMessage = "",

isVisible = true;

JavaScript style guides typically recommend declaring all variables in a given scope at the beginning of that scope, mostly because it helps prevent problems of variable mis-scoping. It also helps JavaScript code compressors, which will take the list of variables and run search and replace on each item to change variable names to single- or double-letter names, thus further reducing the size of the file.

Understanding Variable Scope in JavaScript

Just as each language has rules for creating variables, each language has rules that govern where variables can be accessed. This is known as variable scope. Basically, scoping rules determine the answer to the question, “If I create this variable here, where else will I be able to access it?” Variable scope is an important concept of any language because it influences just about every aspect of working with the language, from debugging to optimization.

As mentioned in Chapter 1, JavaScript has functional scope: when you formally declare a variable using the var keyword, it is limited in scope to the current functional scope and all of the functional scopes contained within the current functional scope. In other words, if you declare a variable within a given scope, you will be able to access it within a subscope but not within any containing scope. Listing 2-18 provides an example to illustrate this concept.

Listing 2-18. Functional Scope in JavaScript

function myFunction() {

var myVariable = "Here"; // myVariable is now limited in scope to myFunction and any scopes we

create within myFunction

// Create a new function within myFunction to demonstrate scope nesting

function myInternalFunction() {

alert(myVariable);

}

myInternalFunction(); // call myInternalFunction when myFunction is called

}

myFunction(); // will alert "Here"

alert(myVariable); // will throw an error; myVariable is not defined outside of myFunction().

When you declare a variable in a particular scope, that scope is often referred to as the local scope for that variable. As you nest functions within one another, you create nested functional scopes that are often referred to as scope chains.

Whenever you access a variable in your program, the JavaScript engine will look throughout the current scope to see if it is defined there. If it doesn’t find a definition there, it goes up to the containing scope and looks there, and so on, up the chain to the topmost scope of the program. This is often referred to as a scope chain lookup, or sometimes just scope lookup.

The topmost scope of any JavaScript program is called the global scope. Any variable declared in the global scope will be available to all scopes in the program, as demonstrated in Listing 2-19.

Listing 2-19. Global Scope in JavaScript

var myVariable = "This is a global variable";

function myFunction() {

myVariable = "Global variable has been changed inside a function";

alert(myVariable);

}

alert(myVariable); // will alert "This is a global variable"

myFunction(); // will alert "Global variable has been changed inside a function"

alert(myVariable); // will alert "Global variable has been changed inside a function"

You can always override higher scope declarations by re-declaring varibles in a particular functional scope. This essentially creates a new variable limited in scope to that functional scope; this is often referred to as local scope precedence. To demonstrate local scope precedence, see Listing 2-20.

Listing 2-20. Local Scope Precedence

var myVariable = "This is a global variable";

function myFunction() {

var myVariable = "Global variable has been overridden inside a function";

alert(myVariable);

}

alert(myVariable); // will alert "This is a global variable"

myFunction(); // will alert "Global variable has been overridden inside a function"

alert(myVariable); // will alert "This is a global variable"

Because of the precedence of local scope, JavaScript variables (and function declarations, described later in the chapter) are available immediately at the beginning of their scope block, whether or not they have been defined yet. If you attempt to access JavaScript variables before their initialization, you will get an undefined value, but they will be there, and the script will not throw an error. This can be quite unexpected behavior, especially in the case of overriding variables that have been declared in a higher scope as illustrated in Listing 2-21.

Listing 2-21. Local Scope Overriding Higher Scope

function testScope() {

var myTest = true; // myTest is now present in this top level scope.

function testNestedScope() { // Create a sub-scope within the main scope

alert(myTest); // Access myTest...but from which scope?

var myTest = false; // Redefine myTest in this sub-scope.

}

testNestedScope();

alert(myTest);

}

testScope(); // will alert "undefined", and then true.

When we execute this example, it first alerts “undefined” and then alerts “true.” The first alert occurs because, within the testNestedScope() function, we redefined the variable myTest so that it is now within that scope. This makes its new value available everywhere within that scope, effectively erasing the value of the variable from the higher scope everywhere within that function. This is called hoisting: A variable declaration (not its assignment, just its declaration) is automatically “hoisted” to the beginning of its containing scope. In other words, when a new scope is created, JavaScript immediately declares all of the local variables before doing anything else, including assignments and function calls. As a result, Listing 2-21 is parsed as if it were written as show in Listing 2-22.

Listing 2-22. Explicitly Hoisting Variables

function testScope() {

var myTest = true;

function testNestedScope() {

var myTest;

alert(myTest);

myTest = false;

}

testNestedScope();

alert(myTest);

}

testScope(); // will alert "undefined", and then true.

Because of variable hoisting, many JavaScript best practices and style guides recommend defining all variables at the beginning of their scope before they are accessed, thus explicitly stating what hoisting does invisibly.

If you access a variable without declaring it using the var keyword, JavaScript will still perform a scope chain lookup. If it reaches the global scope and still has not found the variable declaration, it will assume the variable is meant to be global in scope and will add it there. This is known as implied global scope, an example of which is shown in Listing 2-23.

Listing 2-23. Implied Global Scope

function myFunction() {

myVariable = "Declared in function, default global scope";

alert(myVariable);

}

alert(typeof myVariable); // will alert "undefined" because it wasn't created yet

myFunction(); // will alert " Declared in function, default global scope "

alert(myVariable); // will alert "Declared in function, default global scope "

For details on variable scope, including related topics like closures, see the “Scoping in JavaScript” section in Chapter 1.

Managing Variables in JavaScript

JavaScript tries to make variable management as easy as possible for the programmer. Once you declare a variable, you don’t need to explicitly undeclare it to free memory—in fact, JavaScript provides no mechanism for doing so. The interpreter will manage the variables itself, deallocating their memory when all references and any closures are completely finished.

As mentioned in Chapter 1, JavaScript is a weakly typed language, which means it will manage variable type mismatches in expressions according to a specific set of rules. Because JavaScript is constantly managing variable types behind the scenes, one of the most important aspects of understanding JavaScript is understanding how it manages types, so be sure to review Chapter 1 closely. To recap, JavaScript has four broad data types:


Boolean: True or False values.


Number: All numbers in JavaScript are 64-bit floating-point numbers.


String: Strings of any characters.


Object: Collections of properties and methods.

In addition, JavaScript employs the concept of primitives: non-object, simple variables, which themselves can be booleans, numbers, or strings. Anything that is not a primitive is an object—though JavaScript will transparently change primitives to their associated object types and back again as needed.

When it comes to copying variables, JavaScript handles primitives and objects differently. Primitives are passed from one variable instance to another directly. Objects, on the other hand, are passed by reference: setting a new variable equal to an existing object does not copy that object wholesale into the new variable; rather, it only makes the new variable a pointer to the original object. See Listing 2-24 for an example.

Listing 2-24. Direct Assignment of Primitives and References to Objects

var myObject = {};

var myOtherObject = myObject; // myOtherObject is now a reference to myObject

myObject.bar = "bar"; // This changes myObject directly

myOtherObject.foo = "foo"; // This changes myObject via reference

alert(myObject.foo); // will alert "foo"

alert(myOtherObject.bar); // will alert "bar"

var myInt = 5; // Primitive

var myOtherInt = myInt; // myOtherInt is now its own primitive, there is no reference

myOtherInt++;

myInt--;

alert(myOtherInt); // will alert 6

alert(myInt); // will alert 4

var myPrimitiveString = "My Primitive String";

var myOtherPrimitiveString = myPrimitiveString;

myOtherPrimitiveString += " is now longer."

alert(myOtherPrimitiveString); // Will alert "My Primitive String is now longer."

alert(myPrimitiveString); // Will alert "My Primitive String"

Because JavaScript manages type mismatches transparently, sometimes, as you can see in Listing 2-25, it’s easy to confuse what’s a primitive and what’s an object:

Listing 2-25. Type Conversion Between Objects and Primitives of the Same Data Type

var myStringObject = new String("This is an object");

var myOtherStringObject = myStringObject;

myOtherStringObject += " which I just changed into a primitive"; // Type change, so no longer a

reference!

alert(myStringObject); // will alert "This is an object"

alert(myOtherStringObject); // will alert "This is an object which I just changed into a primitive"

Two objects will return equal in an equality check if they reference the same object in memory, even if the two objects are otherwise identical, as shown in Listing 2-26.

Listing 2-26. Objects Are Only Equal If They Reference the Same Object in Memory

var myObject = {};

var myOtherObject = {};

var myThirdObject = myObject;

alert(myObject == myThirdObject); // will alert "true"

alert(myOtherObject == myThirdObject); // will alert "false"

alert(myObject == myOtherObject); // will alert "false"

JavaScript only provides methods for making references to objects; there is no method for copying an object. However, if you should need to, it’s not difficult to iterate through an object and copy all of its methods and properties to a new object.

Objects

In just about every object-oriented programming language, an object is a collection of properties, and JavaScript is no different. Properties can be either primitives or other objects, including functions. JavaScript objects can be arbitrarily deep, meaning you can have objects that have properties that are objects, which in turn have properties that are objects, and so on, as deeply as you wish.

Inheritance

As covered in detail in Chapter 1, JavaScript uses prototypal inheritance rather than classes. Each object has a special prototype property that serves as a pointer to the object from which it was created. When you attempt to access a property on the object, the interpreter checks to see if the desired property exists within the current object. If the property does not exist, the interpreter checks the prototype. If the property is not there, the interpreter checks the prototype’s prototype, and so on, until it either finds the property or reaches the end of the prototype chain and returns an error. (See Chapter 1 for details and examples of prototypal inheritance.)

Accessing Properties and Enumeration

JavaScript provides two ways of accessing properties on objects, as demonstrated in Listing 2-27.

Listing 2-27. Accessing Object Properties in JavaScript

var myObject = {};

myObject.property1 = "This is property1"; // access via dot notation

myObject["property2"] = 5; // access via square brackets

alert(myObject["property1"]); // will alert "This is property1"

alert(myObject.property2); // will alert 5

The ECMA-262 standard specifies that these two methods are exactly the same:

Properties are accessed by name, using either the dot notation:

MemberExpression.IdentifierName

CallExpression.IdentifierName

or the bracket notation:

MemberExpression[ Expression ]

CallExpression[ Expression ]

The dot notation is explained by the following syntactic conversion:

MemberExpression.IdentifierName

is identical in its behaviour to

MemberExpression[ ]

and similarly

CallExpression.IdentifierName

is identical in its behaviour to

CallExpression[ ]

where is a string literal containing the same sequence of characters after processing of Unicode escape sequences as the IdentifierName.

ECMA-262 Edition 5.1, Section 11.2.1, “Property Accessors”

The benefit of this dual notation is that you can easily programmatically access object properties using the square bracket notation without necessarily knowing the names of all the properties. As an example, consider the need to enumerate all the properties of an object. You don’t know what they are, so you can’t access them using dot notation. Instead, you just query the object for each of its properties and access their values using brackets, as shown in Listing 2-28.

Listing 2-28. Traditional Method for Enumerating an Object in JavaScript

// Assuming the existence of targetObject, which has many unknown properties:

var thing,

strMessage = "";

for (thing in targetObject) {

strMessage += "targetObject." + thing + " = " + targetObject[thing] + "\n";

} alert(strMessage); // will alert all of the properties in targetObject

In Listing 2-28, we are iterating over all the properties in targetObject using a for loop (see the section “for Loops,” later in the chapter, for details about for loops). We build a string containing each property and its associated value, one per line, and then alert the string. This will only include the noninherited properties of an object. That’s the traditional method for enumerating properties in JavaScript. With newer versions of JavaScript, it’s possible to enumerate objects using different methods. In Version 5 of ECMA-262, the global Object object has two new methods: Object.keys() and Object.getOwnPropertyNames(). (See Chapter 5 for details on these two methods and how they differ.) Now we can enumerate an object as shown in Listing 2-29.

Listing 2-29. New Method for Enumerating an Object in JavaScript

// Assuming the existence of myObject, which has many unknown properties:

var arrKeys = Object.keys(myObject),

strMessage = "",

i = 0,

arrKeysLength = arrKeys.length;

for (i = 0; I , arrKeysLength; i++) {

strMessage += "myObject." + arrKeys[i] + " = " + myObject[arrKeys[i]] + "/n";

}

alert(strMessage);

Creating Objects

JavaScript has three main ways of creating objects: using a constructor function, using literal notation, or using Object.create().

Using Constructor Functions

The traditional method for creating new JavaScript objects is to create a constructor function and use it to make new objects as desired. To make a constructor function, you simply create a function as you ordinarily would, and add properties to it as needed, as shown in Listing 2-30.

Listing 2-30. Basic Constructor Function

function myConstructor() {

this.property1 = "foo";

this.property2 = "bar";

this.method1 = function() {

alert("Hello World!");

}

}

You’ll notice in this constructor that we are using the this keyword to add new properties to the object. Details on the subtleties of the this keyword within functions are provided later in the chapter, in the section “Functions.” In the context of constructor functions, the keyword this refers to the object that is being created by the constructor.

To create a new instance from the constructor, use the new operator, as shown in Listing 2-31.

Listing 2-31. Creating a New Instance from a Constructor

var myObject = new myConstructor();

myObject.method1(); // will alert "Hello World!"

The new operator performs the following steps:

1. It creates a new empty object that inherits from the operand’s prototype,

2. It sets that new object as the execution scope of the operand (so within the operand, the this keyword refers to the new empty object),

3. It invokes the operand, so the operand can then modify the new object as needed,

4. It returns the value that the operand returns, or if the operand does not return anything, it automatically returns the new object it created in step 1 and that the operand modified in step 3.

If you’re coming to JavaScript from a background in class-based languages like Java or C++, you may be thinking, “Hey, that looks kind of like a class!” You’re correct, this method does superficially resemble classes. You can continue down this road and fully emulate classes in JavaScript using this method in combination with others. However, you are encouraged to try and leave behind the idea of classes when working with JavaScript so that you can better take advantage of the language’s dynamic nature.

Using Literals

Another way of creating objects in JavaScript is to use literal notation. Literal notation is a way for you to provide values literally for an object during creation. In JavaScript, literal notation is very common, and we’ll be covering it several times in this chapter.

To create an object literally, begin by defining it as you ordinarily would with the var keyword, as shown in Listing 2-32, and then use curly brackets to enclose properties, which should be key/value pairs separated by commas.

Listing 2-32. Creating an Object Using Literal Notation

var myObjectLiteral = {

property1: "one",

property2: "two",

method1: function() {

alert("Hello World!");

}

}

myObjectLiteral.method1(); // will alert "Hello World!"

Because we have created the object literally, we can use it immediately. Literal notation is thus the best way to create singletons in JavaScript. Objects created this way can still be extended later by adding properties and methods as desired, as shown in Listing 2-33.

Listing 2-33. Extending an Object

myObjectLiteral.property3 = "New property"; // adds a new property to the previously created object

Using Object.create( )

Finally, the latest versions of JavaScript provide a third method for creating new objects: the create() method on the global Object object. As shown in Listing 2-34, the method takes an object as its parameter and returns a new object with the parameter object as its prototype.

Listing 2-34. Using Object.create() to Create New Objects

var myObjectLiteral = {

property1: "one",

property2: "two",

method1: function() {

alert("Hello world!");

}

}

var myChild = Object.create(myObjectLiteral);

myChild.method1(); // will alert "Hello world!"

This method can create a new object from any object, even a constructor function.

Which Method Should I Use?

Which method to use is largely a matter of taste, though sometimes the choice will be dictated by convention or situation. Some JavaScript libraries, for example, make heavy use of Object.create(). Or, if you are doing extensive work with JSON, you might find it makes more sense to use literals to manage your singletons. And if you really feel you need classlike behavior in your JavaScript programs, then constructor functions are the easiest way to get there.

Arrays

In JavaScript, as in most languages, arrays are essentially indexed data structures, with a value associated with each index. JavaScript array indices all start from 0, so the second item in an array actually has an index of 1, and the length of an array is equal to the last index + 1.

Dynamic Length

In keeping with JavaScript’s dynamic nature, its arrays have dynamic lengths. This means that you can add and remove items from arrays, and the length of the arrays will change as needed. Thus, you cannot generate boundary errors when you are adding or removing items from arrays. And if you attempt to access an element that does not exist, the interpreter will return undefined instead of throwing an error. A common mistake in JavaScript is to attempt to retrieve something from an array that does not exist, resulting in undefined, and then attempting to do something with that value without first checking to see if it is undefined. Depending on what you attempt to do with the value, the interpreter might throw an error at that point, but it will not throw an error at the point when the array was accessed out of bounds.

JavaScript arrays have a length property, which contains a number that indicates the length of the array. As elements are added to and removed from the array, this number increases or decreases as needed. The length property can also be set directly, as shown in Listing 2-35; doing so will either remove existing items from or add undefined items to the end of the array, as appropriate.

Listing 2-35. Dynamic Lengths of JavaScript Arrays

var arrColors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

alert(arrColors.length); // will alert 7

arrColors.length = 10; // adds three new elements to the array, each set to "undefined"

alert(arrColors[8]); // will alert "undefined"

arrColors.length = 6; // arrColors is now ["red", "orange", "yellow", "green", "blue", "indigo"]

Accessing and Assigning Values

In JavaScript, arrays can contain any valid data type: objects, functions, booleans, and so forth. Data types can be mixed within arrays, too, meaning you can have an array made up of an object, a boolean, a number, and a string.

Array values are accessed using the square bracket notation described for objects: the name of the array, followed by a set of square brackets that contain the index of the desired value; for example, in Listing 2-36, myArray[2] will access the third item in the array myArray.

Listing 2-36. Accessing Arrays

var myArray = new Array();

myArray[0] = "foo";

myArray[1] = "bar";

myArray[3] = 4;

alert(myArray.length); // will alert 4

alert(myArray[2]); // will alert "undefined"

var testVar = myArray[498]; // testVar is now "undefined" and no error will be thrown

alert(testVar); // will alert "undefined"

Arrays are actually special cases of JavaScript objects. You can add properties to arrays just like you can any other object, as demonstrated in Listing 2-37.

Listing 2-37. Assigning Values

var myArray = new Array();

myArray[0] = "foo"; // assign "foo" to the first element of the array

myArray[1] = "bar"; // assign "bar" to the second element of the array

myArray["foo"] = "bar"; // create the property "foo" on myArray and give it the value of "bar"

alert(myArray.length); // will alert 2

myArray["2"] = 7; // assign 7 to the third element of the array

alert(myArray.length); // will alert 3

var strMyIndex = "3";

myArray[strMyIndex] = 8; // will assign 8 to the fourth element of the array

alert(myArray.length); // will alert 4

Listing 2-37 demonstrates how JavaScript will coerce the type of a non-numeric index to a numeric value if it can, and then use that as an index. Otherwise, it will use the supplied value as the key for a new property on the array object itself.

Because of this behavior, it’s often said that JavaScript has “associative arrays.” This isn’t strictly true, because JavaScript arrays cannot have non-numeric indexes. If you add something to an array with a non-numeric index, as shown in Listing 2-38, you are simply adding it as a property on the array object itself, not adding elements to the array.

Listing 2-38. Array Elements vs. Properties

var myArray = new Array();

myArray["foo"] = "bar"; // adds a property, not a new element

myArray["new"] = "old"; // adds a property, not a new element

alert(myArray.length); // will alert 0, because no elements have actually been added to the array.

myArray[0] = 0; // Adds a new element to the array

alert(myArray.length); // will alert 1

Creating Arrays

There are two ways to create arrays in JavaScript: using the global Array object as a constructor, as shown in Listing 2-39, or using literal notation, as shown in Listing 2-40.

Listing 2-39. Creating Arrays with the Constructor

var myArrayObject = new Array(4); // creates an array with 4 undefined elements

var myOtherArray = new Array(4, 2, 5, 2, 7); // creates an array with those values

alert(myArrayObject.length); // will alert 4

alert(myOtherArray.length); // will alert 5

Listing 2-40. Creating Arrays with Literal Notation

var myLiteralArray = []; // Creates an array of length 0 with no elements

var myOtherArray = [1, "foo", {}, true]; // Creates an array of length 4 with a number, a string, an object, and a boolean

When using the Array object as a constructor, you can supply an optional single numeric value, which will cause the constructor to return an array initialized with the specified number of slots. Each slot will be set to “undefined.” If you provide more than one comma-delimited argument, the constructor will return an array with each of the arguments as an indexed value, in order, starting from 0. Creating arrays using literal notation is similar to creating objects with literal notation.

Listing 2-40 demonstrates that you can have multiple data types within a single array. You can even have objects as your array values, just as you can have arrays as properties of objects. JavaScript also supports multidimensional arrays, as arrays of arrays, as shown in Listing 2-41.

Listing 2-41. Multidimensional Arrays

var row1 = [0, 1, 2];

var row2 = [3, 4, 5];

var row3 = [6, 7, 8];

var array3by3 = [row1, row2, row3];

alert(array3by3[2][1]); // will alert 7

Iterating over Arrays

Because arrays are numerically indexed, one of the most common things to do with them is to run through their members in order, often doing something with each one. The most common way to iterate over an array is in a for loop, as shown in Listing 2-42. (See “for Loops,” later in the chapter, for details on for loops and how to optimize them.)

Listing 2-42. Iterating over an Array Using a for Loop

var myColors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

for (var i = 0; i < myColors.length; i++) {

alert(myColors[i]); // will alert each color one at a time

}

In this example, the for loop will continue until the iterator i reaches myColors.length -1. Each time through the loop, JavaScript will alert the value stored at that index. This is by far the most common pattern for iterating over arrays, and it is very fast, even for very large arrays.

You might be tempted to use a for-in loop, as we did when enumerating objects, but remember that arrays can have properties as well as values, and a for-in loop would iterate over all of those items. Also, there’s no guarantee that the loop would go through all of the indexed values in order, or do them all at once.

Commonly, you will want to do something with each element in the array. If you know for certain that none of the elements in your array will be undefined, you can use a slightly different version of a for loop, as shown in Listing 2-43.

Listing 2-43. Another Way to Iterate over an Array

var myColors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

for ( var i = 0, color; color = myColors[i]; i++) {

// Inside of the loop, the variable color will contain the value at the current index

alert(color); // will alert each color one at a time

}

The advantage of this method is that, within the loop, the variable color is already set to a value at the current index, saving you the trouble of getting it yourself. Note that if one of the array elements is undefined, then your variable will likewise be undefined within the loop.

With newer versions of JavaScript, arrays have a forEach() method that you can use to iterate over them, as shown in Listing 2-44. The method takes a function expression as an argument, and it executes that function once per array element. The function expression can take an optional parameter, which will be set to the array value at the current index.

Listing 2-44. Third Way to Iterate over an Array

var myColors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

myColors.forEach(function(color) {

alert(color); // will alert each of the colors, one at a time

});

If you have an array that you wish to modify as you are iterating over it, you will need to be careful that you don’t skip elements. Consider the example provided in Listing 2-45.

Listing 2-45. Modifying an Array During Iteration

var myColors = ["red", "orange", "green", "green", "blue", "indigo", "violet"];

for (var i = 0; i < myColors.length; i++) {

if (myColors[i] === "green") {

myColors.splice(i, 1); // the splice() method removes the item at index i (see Chapter 5 for

details on the splice() method)

}

}

In this example, we start by examining each member of the myColors array one at a time, starting with “red”. When the loop reaches i = 2, myColors[i] will be “green” and the conditional will cause that element to be removed from the array. As a result, the array will go from being 7 elements to being 6, and the second “green” element will go from being at index 3 to being at index 2. Then, in accordance with for loop functionality (see “for Loops,” later in the chapter), the index will be incremented, going from 2 to 3, and the loop will continue. This will cause the second “green” element to be missed.

Whenever you modify an array while iterating over it, you have to consider this possibility. There are two ways to deal with it. One is to decrement the counter i inside the if statement, so that if a match occurs and an element is popped out of the array, i will decrement by 1, then increment by 1, thus avoiding skipping the element. Another, more elegant solution is to iterate over the array in reverse, as demonstrated in Listing 2-46.

Listing 2-46. Iterating over an Array in Reverse

var myColors = ["red", "orange", "green", "green", "blue", "indigo", "violet"];

for (var i = myColors.length - 1; i >= 0; i--) {

if (myColors[i] === "green") {

myColors.slice(i, 1); // the slice method removes the specified element from the array.

}

}

In Listing 2-46 we are starting at the end of the array and working backward. First we test index i = 6, then i = 5, and so on. At i = 3, we encounter a “green” element, which will be removed from the array. This will reduce the array length by 1, and the “blue” element (and all elements that follow) will have their indices reduced by 1. Then, the counter will decrement by 1, going to i = 2, and we will hit the other “green” element in the array. By going through the array in reverse, you avoid having to manage the counter manually.

Array Methods and Properties

Arrays have several methods and properties for managing their elements. For example, throughout this chapter we’ve used the length property of arrays. There are several other properties and methods as well; for a detailed description of all array methods and properties, along with examples, see Chapter 5.

Functions

Functions are reusable blocks of code that can be called from other areas of the program. In JavaScript, functions are also first-class objects, meaning they can be manipulated like any other object in the language: they can have properties and methods, can be returned from functions, can be passed as arguments, and so on. The object nature of functions in JavaScript is one of the most important keys to understanding the language’s dynamic nature. JavaScript provides two ways to create new functions: via declarations and via expressions.

Function Declarations

JavaScript provides a function keyword that can be used to declare functions. It works similarly to the var keyword for declaring variables, and it’s useful to consider it in the same context. According to the ECMA-262 standard, a function declaration is of the form:

function Identifier (FormalParameterList optional) { FunctionBody}

The FormalParameterList is optional (JavaScript functions are not required to have parameters).

This will create a function with the name Identifier(), which will be visible both in its parent’s scope and in its own scope. Listing 2-47 shows a simple function declaration.

Listing 2-47. Simple Function Declaration

function saySomething(strMessage, strTarget) {

alert(strMessage + " " + strTarget);

}

saySomething("Hello", "world"); // will alert "Hello world"

Note that because the function name is available in its own scope, a function can call itself, allowing for recursion. Listing 2-48 provides an example that shows we can easily implement the mathematical concept of factorials, where a number N! = N(N–1)(N–2) . . . (N–(N–1)).

Listing 2-48. Recursive Functions

function factorial(number) {

if (number <=1) {

return 1

} else {

return number * factorial(number - 1);

}

}

alert(factorial(5)); // will alert 120

Like variable declarations, function declarations are hoisted to the beginning of their scope. In fact, they are parsed and evaluated before all other statements, meaning they will be available immediately within their defined scope, even before they are defined in the code. (See “Understanding Variable Scope in JavaScript,” earlier in the chapter, for a full explanation of hoisting.) As a demonstration, consider the common JavaScript interview question presented in Listing 2-49.

Listing 2-49. Common Interview Question Demonstrating Function Declaration Hoisting

function myFunction() {

function myInternalFunction() {

return 10;

}

return myInternalFunction();

function myInternalFunction() {

return 20;

}

}

alert(myFunction()); // What will this alert?

If you answered “It will alert 20,” then congratulations, you’re hired! The function myInternalFunction() is defined twice, with the second one replacing the first. It doesn’t matter that you accessed the function in the middle of the two definitions, because the definition is hoisted to the top of its scope. It’s the equivalent of Listing 2-50.

Listing 2-50. Equivalent to Listing 2-49

function myFunction() {

function myInternalFunction() {

return 10;

}

function myInternalFunction() {

return 20;

}

return myInternalFunction();

}

alert(myFunction()); // What will this alert?

Because function declarations are hoisted to the top of their scope, you can access them before you can declare them. Just because you can, however, doesn’t mean you should; many JavaScript style guides recommend against this practice because it can lead to obfuscated or confusing code. Whether or not you make use of it, though, function declaration hoisting exists and you should bear it in mind when determining the scope of your functions.

Function Expressions

The other way you can create functions in JavaScript is with function expressions. As with any expression, a function expression represents a value; in the case of a function expression, the value is a function object. Commonly, function expressions are then assigned to variables so that they can be accessed.

According to the ECMA-262 standard, a function expression is of the form:

function Identifier optional (FormalParameterList optional) { FunctionBody }

which in turn results in code like

var myFunction = function foo() {

// function body here

}

You’ll notice that the Identifier is optional. JavaScript allows the creation of unnamed function expressions, known as anonymous functions. Anonymous functions are quite common in JavaScript. It’s commonplace to not provide identifiers when creating function expressions, because the variable serves as a way to invoke the function. Most of the time, unless the function will need to call itself, an anonymous function is assigned to a variable as part of a function expression:

var myFunction = function() {

// function body

}

You’ll also notice that this definition looks almost exactly the same as the definition for a function declaration (introduced in the previous section). This means it’s possible to have exactly the same code serve as either a function declaration or a function expression, depending on context:

function myFunction() {

// function body

}

In general, in the context of an assignment (like a variable assignment) or an expression (as in an anonymous function provided as the parameter of another function), or if there is no Identifier, then the interpreter will assume the code is a function expression. Otherwise, the code will be part of a function body (or the global context) and will be interpreted as a function declaration. Listing 2-51 shows examples of each.

Listing 2-51. Function Declarations vs. Function Expressions

// This is not an assignment, there is an Identifier, and it's in the

// global scope, so it's a function declarationfunction myFunction() {

// function body

}

// This is part of an assignment, so it is a function expression

var myOtherFunction = function foo() {

// function body

}

// Part of a new expression, so it is a function expression

new function myThirdFunction() {

// function body

// This is part of a function body, so it is a function declaration

function myInternalFunction() {

// internal function body

}

}

Assignment function expressions are hoisted just like variable declarations, but only their declaration expression is hoisted, not their assignment expression. As an example, consider another common JavaScript interview question, shown in Listing 2-52.

Listing 2-52. Hoisting for a Function Expression

function myTestFunction() {

var myInternalFunction = function() {

return "Hello World.";

}

return myInternalFunction();

var myInternalFunction = function() {

return "Second Definition.";

}

}

alert(myTestFunction()); // What will this alert?

In this example, the code will alert “Hello Word.” The second assignment expression does not get hoisted, so the assignment of myInternalFunction() is to return the string “Hello World.”

Invoking Functions

So far in the book we’ve been invoking functions, but we’ve never really defined the specific syntax. The syntax is important because there are actually a few ways to invoke functions in JavaScript, and how you invoke a function will determine its execution context.

In JavaScript, when you invoke a function, the function receives a pointer to its execution context, which will be set to the this keyword, which can be accessed within the function. In JavaScript, there are three ways to invoke a function:


Using the function invoker, (), which works with both functions and methods (functions attached to objects)


Using the new keyword, as when constructing a new object


Using the apply() and call() methods

Invoking Functions Using the Invoker

In JavaScript the function invoker is a pair of parentheses, (). Any expression that evaluates to a function can be invoked using the invoker. To pass parameters to the invoked function, you include them in the parentheses as a comma-delimited list.

When you invoke functions using the invoker, the execution context of the function is set to the window object. When you invoke a method, the execution context is set to the parent object. Listing 2-53 provides some examples.

Listing 2-53. Testing the Execution Context of Functions and Methods

var myObject = {

myMethod : function() {

alert(this === myObject); // Test to see if this does indeed refer to the parent object of a method.

}

}

function myGlobalFunction() {

alert(this === window); // Test to see if this refers to the window for functions

function mySubFunction() {

alert(this === window); // Test to see if this refers to window as well.

};

mySubFunction();

}

// Invoke our tests

myObject.myMethod(); // will alert "true"

myGlobalFunction(); // will alert "true" and then alert "true" again.

In listing 2-53, we set up an object with a method, and within that method we test to see if the this keyword does in fact refer to the parent object. Then we create a global function that both tests to see if its this keyword is set to window, and defines its own subfunction. The subfunction tests to see if its keyword is set to the window object as well.

In the case of methods, having this refer to the parent object is one of the main features of JavaScript’s objectoriented paradigm. It enables you to easily access and modify the parent object with its methods.

Invoking Functions As Constructors

A function can also be invoked by using the new operator, as shown in Listing 2-54. Any expression that evaluates to a function can be invoked in this way.

Listing 2-54. Invoking a Function Using the new Keyword

function myFunction() {

alert('Hello world!');

}

new myFunction; // will alert "Hello world!"

Even though you can invoke any function using the new operator, it’s meant to be used for constructing new objects. When you invoke a function in this way, JavaScript creates a new empty object that inherits its prototype from the operand, and sets it as the execution context of the function. As a result, when you are building a constructor function, the this keyword will refer to the new object you’re creating. Then your constructor function can either explicitly return the new object, or the new operator will automatically return it for you.

This invocation method gives you the power to create any arbitrary object constructor you need, as demonstrated in Listing 2-55.

Listing 2-55. Constructor Function Used to Construct an Object

// A common convention for constructors is to capitalize their first letter. function Kitty() {

this.soft = true;

this.temperature = "warm";

this.vocalize = function() {

alert('Purr, purr, purr');

}

}

var myKitty = new Kitty; // create a new kitty.

alert(myKitty.soft); // will alert true

alert(myKitty.temperature); // will alert "warm"

myKitty.vocalize(); // will alert "Purr, purr, purr"

In this example, when we invoke the Kitty() function using the new keyword, JavaScript first creates a new empty object and passes it into the function as its execution context. Then, the function executes, adding the properties soft and temperature and the method vocalize() to the object. Finally, the object is returned so that it can be assigned to the myKitty variable.

This syntax looks remarkably like the syntax you would see for instantiating an object from a class. But don’t forget: JavaScript has no classes, only objects. If you come from a background in class-based object-oriented languages, don’t let this familiar syntax lull you into thinking you’re dealing with classes.

Invoking Functions Using apply() and call()

Finally, all JavaScript functions have two methods that can be used to invoke them: apply() and call(). These methods allow us to specify any context we want for a function.

The apply() and call() methods have similar syntax:

myFunction.apply(thisContext, arrArgs);

myFunction.call(thisContext, arg1, arg2, arg3, ..., argN);

Both methods take a thisContext parameter, which is an object or reference that specifies the execution context of the function. If you don’t specify the parameter, JavaScript will execute the function in the global context, the same as if you had passed a reference to the window object.

The difference between the two methods is how you specify arguments for the function. With the apply() method, you specify the arguments in an array, and with the call() method you supply them as a comma-delimited list. Otherwise the two methods work exactly the same way.

Listing 2-56 provides an example of using these methods to invoke a function.

Listing 2-56. Using call() and apply() to Invoke a Function

var contextObject = {

testContext: 10

}

var otherContextObject = {

testContext: "Hello World!"

}

var testContext = 15; // Global variable

function testFunction() {

alert(this.testContext);

}

testFunction(); // This will alert 15

testFunction.call(contextObject); // Will alert 10

testFunction.apply(otherContextObject); // Will alert "Hello World!"

In Listing 2-56, we create two context objects with the same property set to different values: one is set to the number 10, the other to the string “Hello World!”. Then we create a global variable with the same name as the property and set it to 15. Finally, we create a function that alerts the value of this.testContext.

When we invoke the function using the invoker, the execution context is the window object, so the function alerts the value of the global variable. When we use the call() method and provide the first context object as the new execution context, the function alerts the value of the property for that object. Similarly, when we use the apply() method and provide the other context object, the function alerts that property.

Conditionals

JavaScript provides a fairly standard set of features for conditional execution of code: testing expressions and, based on the results, executing specified statement blocks.

if Statements

The basic form of an if statement in JavaScript is:

if (conditionExpression) { statementBlock }

Explicitly, if the conditionExpression evaluates to true, the code in the statementBlock will be executed. If you have just one line of code in your statement block, the brackets are optional.

JavaScript if statements can be expanded using the else keyword, allowing for logic branches:

if (conditionExpression) {

statementBlock1

} else {

statementBlock2 }

Here, if the conditionExpression evaluates to true, statementBlock1 will execute; otherwise, statementBlock2 will execute.

You can chain if statements together in this way:

if (conditionExpression1) {

statementBlock1

} else if (conditionExpression2) {

statementBlock2

} else if (conditionExpression3) {

statementBlock3

} else {

statementBlock4

}

In this example, if conditionExpression1 is true, then statementBlock1 will execute and the control of the program will move to the end of the chain. If conditionExpression1 is false, then conditionExpression2 will be evaluated. If it is true, statementBlock2 will execute and then the control of the program will once again move to the end of the chain. As a result, statementBlock4 will only execute if conditionExpression1, conditionExpression2, and conditionExpression3 all evaluate to false; otherwise, it will never execute. See Listing 2-7 for an example of a chain of if...else statements:

Listing 2-57. An if-then-else Block

function alertGenres(authorName) {

if (authorName === "Neil Gaiman") {

alert("Fantasy");

} else if (authorName === "Octavia Butler") {

alert("Science Fiction");

} else if (authorName === "Roger Zelazny") {

alert("Science Fiction and Fantasy");

} else {

alert("Unknown author.");

}

}

alertGenres("Roger Zelazny"); // will alert "Science Fiction and Fantasy"

alertGenres("Arthur C. Clarke"); // will alert "Unknown author."

In Listing 2-57, we built a simple function that tests an author’s name and, if it recognizes it, alerts the name of the genre in which that author wrote. If the author is not recognized, the function alerts “Unknown author.”

switch Statements

switch statements provide an alternative to extensive if-else-if-else-if-else chains and are particularly useful when a single conditionExpression is being tested and can have multiple results. The following is the format for a switch statement:

switch (expression) {

case result1:

statementBlock1

[break;]

case result2:

statementBlock2

[break;]

case result3:

statementBlock3

[break;]

default:

statementBlockDefault

}

In a case statement, the expression is expected to resolve to one of the case labels. This in turn will cause execution to move to that label and begin executing the statementBlocks from that point. An optional break statement will stop execution of the blocks. If the expression does not resolve to a matching label, then the interpreter will look for a default label and, if present, will execute its associated statementBlock.

To demonstrate, Listing 2-58 provides a trivial example.

Listing 2-58. Trivial Example of a switch Statement

var myColor = "yellow";

switch (myColor) {

case "red":

alert("myColor was set to red");

break;

case "yellow":

alert("myColor was set to yellow");

case "green":

alert("myColor was set to green");

default:

alert("myColor was an unknown color");

}

In Listing 2-58, myColor resolves to “red” so the switch statement will alert “myColor was set to red”. Because the “red” code block includes a break statement, execution of the switch statement ends, and the program continues after the closing bracket. If myColor were instead set to “yellow”, then the switch statement would move to the “yellow” case and alert “myColor was set to yellow”. Then, because the “yellow” code block does not have a break statement, the program will execute the “green” case, and then the default case.

Loops

The most common tasks you will need to perform with any programming language typically are repetitive or iterative tasks. Imagine, for example, that you want to check all of the links on a given HTML page to see if any of them contain a particular text value. JavaScript has a set of looping statements to handle those situations.

Generally, a loop is a statement block that executes repeatedly until a specified condition is met. JavaScript has four basic kinds of loops: for loops, for-in loops, while loops, and do loops.

for Loops

Probably the most commonly used loop statement is the for loop. Generally, a for loop executes its statement block over and over again until the specified condition is false. At that point the program moves execution to the next statement after the close of the loop. A for loop will execute its condition check before executing its statement block for the first time, so it is possible (if the condition check is false that first time) for the statement block to never execute.

The syntax for a for loop is:

for (initialExpression; conditionExpression; incrementExpression)

statementBlock

A for loop executes as follows:

1. The initialExpression is executed. Typically this is used to define and initialize counting variables, but it can be any valid expression. The initialExpression is also optional.

2. The conditionExpression is evaluated. If the expression evaluates to true, then statementBlock will execute. If the expression evaluates to false, the loop will terminate without executing the statementBlock. The conditionExpression is technically optional; if you omit it, the interpreter assumes the condition is always true, and thus will always execute the loop, meaning you would have to manually break out of the loop.

3. After statementBlock executes, incrementExpression is executed. This expression is optional as well. Once it has executed, step 2 is repeated.

Listing 2-59 provides a trivial example to demonstrate for loop execution.

Listing 2-59. Simple for Loop

for (var i =0; i < 10; i++) {

alert(i); // will alert 0 through 9 one at a time

}

In this example, the loop executes as follows:

1. A variable i is declared and set to 0.

2. The loop checks to see if i < 10 and, if it is, executes its statement block (in this case, alerting the value of i).

3. i is incremented by 1, and step 2 is repeated.

Note that because the conditionExpression is evaluated each time the loop executes, if it is an expensive statement, it could cause a significant performance problem in your program, especially if the loop is executed hundreds or thousands of times. It’s a good idea to keep the conditionExpression as simple as possible. For example, consider the loop in Listing 2-60.

Listing 2-60. Potentially Expensive Loop

for (var i = 0; i < someArray.length; i++) {

// do things

}

In Listing 2-60, at the beginning of every time through the loop, we check the length property of the someArray array. This isn’t terribly expensive, but if someArray is several thousand items, it could add up. Listing 2-61 provides a simple optimization.

Listing 2-61. Optimized for Loop

var someArrayLength = someArray.length,

i = 0;

for (i = 0; i < someArrayLength; i++) {

// do things

}

In Listing 2-61 we have stored the length of the array in a variable, thus simplifying the conditionExpression. We have also moved the variable declaration of i outside of the loop to explicitly define its scope. These are minor improvements, but if the array is long, the benefits could add up quickly.

for-in Loops

JavaScript also has a for-in loop statement. This construct is specifically intended to enumerate object properties:

for (var variable in objectExpression) { statementBlock }

Any expression that evaluates to an object can be used in objectExpression; most often it’s just an object of some sort. On each iteration, one of the properties of the object will be assigned to the variable and then the statementBlock will be executed. An example is shown in Listing 2-62. See the “Objects” section earlier in the chapter for more information and examples on using for-in loops to enumerate object properties.

Listing 2-62. Enumerating an Object

var myObject = {

prop1: "value1",

prop2: "value2",

prop3: true,

prop4: 100

}

var strAlert = "";

for (var prop in myObject) {

strAlert += prop + " : " + myObject[prop] + "\n";

}

alert(strAlert);

This example will alert

prop1 : value1

prop2 : value2

prop3 : true

prop4 : 100

while Loops

JavaScript also has while loops, which execute their statement blocks as long as their conditional evaluates to true:

while (conditionExpression)

statementBlock

As with for loops, the condition is tested each time before the statementBlock is executed. If on the first time through the loop the condition evaluates to false, then the statementBlock will never execute. Listing 2-63 provides an example.

Listing 2-63. Trivial while Loop

var counter = 0;

while (counter < 10) {

alert(counter);

counter++;

}

In this example, the counter variable is incremented within the loop, so when it finally reaches 10, the loop will terminate. This example will result in alerting the numbers 0 through 9, one at a time.

do Loops

Similar to while loops, do loops execute their statement blocks while their condition evaluates to true:

do

statementBlock

while (conditionExpression);

Unlike while loops and for loops, in a do loop the statementBlock is executed the first time before the conditionExpression is evaluated. As a result, a do loop’s statementBlock will always execute at least once. Listing 2-64 provides an example.

Listing 2-64. Trivial do Loop

var counter = 0;

do {

alert(counter);

counter++;

} while (counter < 10)

Like the previous examples, this example will alert the numbers 0 through 9, one at a time.

Summary

In this chapter we have discussed the basics of working with JavaScript:


JavaScript expressions are sections of code that evaluate to a value, and statements are blocks of expressions that achieve a particular goal.


JavaScript has several different kinds of operators: arithmetic, assignment, bitwise, comparison, logical, and string, as well as a few that don’t fit into those categories.


JavaScript has specific precedence for determining which operators should go first in statements with multiple operators.


Variables declared with the var keyword are limited in scope to the current scope; variables that are declared simply by accessing them are assumed to be global.


You can access properties on an object with either dot notation or square brackets.


Objects can be created with either literal notation, constructor functions, or the Object.create() method.


JavaScript arrays are dynamic, and do not throw out-of-bounds errors.


You can create functions either as expressions or by declaration.


How you invoke a function determines its execution scope.


You can specify execution scope for a function using either the call() method or the apply() method.


JavaScript supports both if-then-else and switch conditionals.


JavaScript supports several loop types: for loops, for-in loops, while loops, and do loops.

In the next chapter we will dive deep into the Document Object Model: the representation of an HTML page in JavaScript. We will apply the knowledge from this chapter to creating dynamic web pages, animations, and other effects.

I do not employ cookies or tracking devices of any kind