Chapter 3

Language Basics

WHAT’S IN THIS CHAPTER?

  • Reviewing syntax
  • Working with data types
  • Working with flow-control statements
  • Understanding functions

At the core of any language is a description of how it should work at the most basic level. This description typically defines syntax, operators, data types, and built-in functionality upon which complex solutions can be built. As previously mentioned, ECMA-262 defines all of this information for JavaScript in the form of a pseudolanguage called ECMAScript.

ECMAScript as defined in ECMA-262, third edition, is the most-implemented version among web browsers. The fifth edition is the next to be implemented in browsers, though, as of the end of 2011, no browser has fully implemented it. For this reason the following information is based primarily on ECMAScript as defined in the third edition with changes in the fifth edition called out.

SYNTAX

ECMAScript’s syntax borrows heavily from C and other C-like languages such as Java and Perl. Developers familiar with such languages should have an easy time picking up the somewhat looser syntax of ECMAScript.

Case-sensitivity

The first concept to understand is that everything is case-sensitive; variables, function names, and operators are all case-sensitive, meaning that a variable named test is different from a variable named Test. Similarly, typeof can’t be the name of a function, because it’s a keyword (described in the next section); however, typeOf is a perfectly valid function name.

Identifiers

An identifier is the name of a variable, function, property, or function argument. Identifiers may be one or more characters in the following format:

  • The first character must be a letter, an underscore (_), or a dollar sign ($).
  • All other characters may be letters, underscores, dollar signs, or numbers.

Letters in an identifier may include extended ASCII or Unicode letter characters such as À and Æ, though this is not recommended.

By convention, ECMAScript identifiers use camel case, meaning that the first letter is lowercase and each additional word is offset by a capital letter, like this:

firstSecond
myCar
doSomethingImportant

Although this is not strictly enforced, it is considered a best practice to adhere to the built-in ECMAScript functions and objects that follow this format.

image

Keywords, reserved words, true, false, and null cannot be used as identifiers. See the section “Keywords and Reserved Words” coming up shortly for more detail.

Comments

ECMAScript uses C-style comments for both single-line and block comments. A single-line comment begins with two forward-slash characters, such as this:

//single line comment

A block comment begins with a forward slash and asterisk (/*) and ends with the opposite (*/), as in this example:

/*
 * This is a multi-line
 * Comment
 */

Note that even though the second and third lines contain an asterisk, these are not necessary and are added purely for readability. (This is the format preferred in enterprise applications.)

Strict Mode

ECMAScript 5 introduced the concept of strict mode. Strict mode is a different parsing and execution model for JavaScript, where some of the erratic behavior of ECMAScript 3 is addressed and errors are thrown for unsafe activities. To enable strict mode for an entire script, include the following at the top:

"use strict";

Although this may look like a string that isn’t assigned to a variable, this is a pragma that tells supporting JavaScript engines to change into strict mode. The syntax was chosen specifically so as not to break ECMAScript 3 syntax.

You may also specify just a function to execute in strict mode by including the pragma at the top of the function body:

function doSomething(){
    "use strict";
    //function body
}

Strict mode changes many parts of how JavaScript is executed, and as such, strict mode distinctions are pointed out throughout the book. Internet Explorer 10+, Firefox 4+, Safari 5.1+, Opera 12+, and Chrome support strict mode.

Statements

Statements in ECMAScript are terminated by a semicolon, though omitting the semicolon makes the parser determine where the end of a statement occurs, as in the following examples:

var sum = a + b        //valid even without a semicolon - not recommended
var diff = a - b;      //valid - preferred

Even though a semicolon is not required at the end of statements, it is recommended to always include one. Including semicolons helps prevent errors of omission, such as not finishing what you were typing, and allows developers to compress ECMAScript code by removing extra white space (such compression causes syntax errors when lines do not end in a semicolon). Including semicolons also improves performance in certain situations, because parsers try to correct syntax errors by inserting semicolons where they appear to belong.

Multiple statements can be combined into a code block by using C-style syntax, beginning with a left curly brace ({) and ending with a right curly brace (}):

if (test){
    test = false;
    alert(test);
}

Control statements, such as if, require code blocks only when executing multiple statements. However, it is considered a best practice to always use code blocks with control statements, even if there’s only one statement to be executed, as in the following examples:

if (test)
    alert(test);     //valid, but error-prone and should be avoided
                   
if (test){           //preferred
    alert(test);
}

Using code blocks for control statements makes the intent clearer, and there’s less chance for errors when changes need to be made.

KEYWORDS AND RESERVED WORDS

ECMA-262 describes a set of keywords that have specific uses, such as indicating the beginning or end of control statements or performing specific operations. By rule, keywords are reserved and cannot be used as identifiers or property names. The complete list of keywords is as follows (those denoted with an asterisk were added in the fifth edition):

break              do              instanceof         typeof
case               else            new                var
catch              finally         return             void
continue           for             switch             while
debugger*          function        this               with
default            if              throw
delete             in              try

The specification also describes a set of reserved words that cannot be used as identifiers or property names. Though reserved words don’t have any specific usage in the language, they are reserved for future use as keywords. The following is the complete list of reserved words defined in ECMA-262, third edition:

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

The fifth edition shrinks down the list of reserved words when running in nonstrict mode to the following:

class              enum              extends          super
const              export            import

When running in strict mode, the fifth edition also places reserved word restrictions on the following:

implements         package               public
interface          private               static
let                protected             yield

Note that let and yield are introduced as reserved words in the fifth edition; all other reserved words come from the third edition. For best compatibility, it’s recommended to use the third edition list as a guideline and add let and yield.

Attempting to use a keyword as an identifier name will cause an “Identifier Expected” error in ECMAScript 3 JavaScript engines. Attempting to use a reserved word may or may not cause the same error, depending on the particular engine being used.

The fifth edition slightly changes the rules regarding keywords and reserved words. These may still not be used as identifiers but now can be used as property names in objects. Generally speaking, it’s best to avoid using both keywords and reserved words as both identifiers and property names to ensure compatibility with past and future ECMAScript editions.

In addition to the list of keywords and reserved words, ECMA-262, fifth edition, places restrictions on the names eval and arguments. When running in strict mode, these two names may not be used as identifiers or property names and will throw errors when an attempt is made to do so.

VARIABLES

ECMAScript variables are loosely typed, meaning that a variable can hold any type of data. Every variable is simply a named placeholder for a value. To define a variable, use the var operator (note that var is a keyword) followed by the variable name (an identifier, as described earlier), like this:

var message;

This code defines a variable named message that can be used to hold any value. (Without initialization, it holds the special value undefined, which is discussed in the next section.) ECMAScript implements variable initialization, so it’s possible to define the variable and set its value at the same time, as in this example:

var message = "hi";

Here, message is defined to hold a string value of "hi". Doing this initialization doesn’t mark the variable as being a string type; it is simply the assignment of a value to the variable. It is still possible to not only change the value stored in the variable but also change the type of value, such as this:

var message = "hi";
message = 100;      //legal, but not recommended

In this example, the variable message is first defined as having the string value "hi" and then overwritten with the numeric value 100. Though it’s not recommended to switch the data type that a variable contains, it is completely valid in ECMAScript.

It’s important to note that using the var operator to define a variable makes it local to the scope in which it was defined. For example, defining a variable inside of a function using var means that the variable is destroyed as soon as the function exits, as shown here:

function test(){
    var message = "hi";  //local variable
}
test();
alert(message); //error!

Here, the message variable is defined within a function using var. The function is called test(), which creates the variable and assigns its value. Immediately after that, the variable is destroyed so the last line in this example causes an error. It is, however, possible to define a variable globally by simply omitting the var operator as follows:

function test(){
    message = "hi";  //global variable
}
test();
alert(message); //"hi"

By removing the var operator from the example, the message variable becomes global. As soon as the function test() is called, the variable is defined and becomes accessible outside of the function once it has been executed.

image

Although it’s possible to define global variables by omitting the var operator, this approach is not recommended. Global variables defined locally are hard to maintain and cause confusion, because it’s not immediately apparent if the omission of var was intentional. Strict mode throws a ReferenceError when an undeclared variable is assigned a value.

If you need to define more than one variable, you can do it using a single statement, separating each variable (and optional initialization) with a comma like this:

var message = "hi", 
    found = false,
    age = 29;

Here, three variables are defined and initialized. Because ECMAScript is loosely typed, variable initializations using different data types may be combined into a single statement. Though inserting line breaks and indenting the variables isn’t necessary, it helps to improve readability.

When you are running in strict mode, you cannot define variables named eval or arguments. Doing so results in a syntax error.

DATA TYPES

There are five simple data types (also called primitive types) in ECMAScript: Undefined, Null, Boolean, Number, and String. There is also one complex data type called Object, which is an unordered list of name-value pairs. Because there is no way to define your own data types in ECMAScript, all values can be represented as one of these six. Having only six data types may seem like too few to fully represent data; however, ECMAScript’s data types have dynamic aspects that make each single data type behave like several.

The typeof Operator

Because ECMAScript is loosely typed, there needs to be a way to determine the data type of a given variable. The typeof operator provides that information. Using the typeof operator on a value returns one of the following strings:

  • "undefined" if the value is undefined
  • "boolean" if the value is a Boolean
  • "string" if the value is a string
  • "number" if the value is a number
  • "object" if the value is an object (other than a function) or null
  • "function" if the value is a function

The typeof operator is called like this:

image
var message = "some string";
alert(typeof message);    //"string"
alert(typeof(message));   //"string"
alert(typeof 95);         //"number"

TypeofExample01.htm

In this example, both a variable (message) and a numeric literal are passed into the typeof operator. Note that because typeof is an operator and not a function, no parentheses are required (although they can be used).

Be aware there are a few cases where typeof seemingly returns a confusing but technically correct value. Calling typeof null returns a value of "object", as the special value null is considered to be an empty object reference. Safari through version 5 and Chrome through version 7 have a quirk where calling typeof on a regular expression returns "function" while all other browsers return "object".

image

Technically, functions are considered objects in ECMAScript and don’t represent another data type. However, they do have some special properties, which necessitates differentiating between functions and other objects via the typeof operator.

The Undefined Type

The Undefined type has only one value, which is the special value undefined. When a variable is declared using var but not initialized, it is assigned the value of undefined as follows:

image
var message;
alert(message == undefined);    //true

UndefinedExample01.htm

In this example, the variable message is declared without initializing it. When compared with the literal value of undefined, the two are equal. This example is identical to the following:

var message = undefined;
alert(message == undefined);    //true

UndefinedExample02.htm

Here the variable message is explicitly initialized to be undefined. This is unnecessary because, by default, any uninitialized variable gets the value of undefined.

image

Generally speaking, you should never explicitly set a variable to be undefined. The literal undefined value is provided mainly for comparison and wasn’t added until ECMA-262, third edition to help formalize the difference between an empty object pointer (null) and an uninitialized variable.

Note that a variable containing the value of undefined is different from a variable that hasn’t been defined at all. Consider the following:

var message;     //this variable is declared but has a value of undefined
                   
//make sure this variable isn't declared
//var age
                   
alert(message);  //"undefined"
alert(age);      //causes an error

UndefinedExample03.htm

In this example, the first alert displays the variable message, which is "undefined". In the second alert, an undeclared variable called age is passed into the alert() function, which causes an error because the variable hasn’t been declared. Only one useful operation can be performed on an undeclared variable: you can call typeof on it (calling delete on an undeclared variable won’t cause an error, but this isn’t very useful and in fact throws an error in strict mode).

The typeof operator returns "undefined" when called on an uninitialized variable, but it also returns "undefined" when called on an undeclared variable, which can be a bit confusing. Consider this example:

image
var message;     //this variable is declared but has a value of undefined
                   
//make sure this variable isn't declared
//var age
                   
alert(typeof message);  //"undefined"
alert(typeof age);      //"undefined"

UndefinedExample04.htm

In both cases, calling typeof on the variable returns the string "undefined". Logically, this makes sense because no real operations can be performed with either variable even though they are technically very different.

image

Even though uninitialized variables are automatically assigned a value of undefined, it is advisable to always initialize variables. That way, when typeof returns "undefined", you’ll know that it’s because a given variable hasn’t been declared rather than was simply not initialized.

The Null Type

The Null type is the second data type that has only one value: the special value null. Logically, a null value is an empty object pointer, which is why typeof returns "object" when it’s passed a null value in the following example:

var car = null;
alert(typeof car);   //"object"

NullExample01.htm

When defining a variable that is meant to later hold an object, it is advisable to initialize the variable to null as opposed to anything else. That way, you can explicitly check for the value null to determine if the variable has been filled with an object reference at a later time, such as in this example:

if (car != null){
    //do something with car
}

The value undefined is a derivative of null, so ECMA-262 defines them to be superficially equal as follows:

alert(null == undefined);   //true

NullExample02.htm

Using the equality operator (==) between null and undefined always returns true, though keep in mind that this operator converts its operands for comparison purposes (covered in detail later in this chapter).

Even though null and undefined are related, they have very different uses. As mentioned previously, you should never explicitly set the value of a variable to undefined, but the same does not hold true for null. Any time an object is expected but is not available, null should be used in its place. This helps to keep the paradigm of null as an empty object pointer and further differentiates it from undefined.

The Boolean Type

The Boolean type is one of the most frequently used types in ECMAScript and has only two literal values: true and false. These values are distinct from numeric values, so true is not equal to 1, and false is not equal to 0. Assignment of Boolean values to variables is as follows:

var found = true;
var lost = false;

Note that the Boolean literals true and false are case-sensitive, so True and False (and other mixings of uppercase and lowercase) are valid as identifiers but not as Boolean values.

Though there are just two literal Boolean values, all types of values have Boolean equivalents in ECMAScript. To convert a value into its Boolean equivalent, the special Boolean() casting function is called, like this:

image
var message = "Hello world!";
var messageAsBoolean = Boolean(message);

BooleanExample01.htm

In this example, the string message is converted into a Boolean value and stored in messageAsBoolean. The Boolean() casting function can be called on any type of data and will always return a Boolean value. The rules for when a value is converted to true or false depend on the data type as much as the actual value. The following table outlines the various data types and their specific conversions.

DATA TYPE VALUES CONVERTED TO TRUE VALUES CONVERTED TO FALSE
Boolean true false
String Any nonempty string "" (empty string)
Number Any nonzero number (including infinity) 0, NaN (See the “NaN” section later in this chapter.)
Object Any object null
Undefined n/a undefined

These conversions are important to understand because flow-control statements, such as the if statement, automatically perform this Boolean conversion, as shown here:

image
var message = "Hello world!";
if (message){
    alert("Value is true");
}

BooleanExample02.htm

In this example, the alert will be displayed because the string message is automatically converted into its Boolean equivalent (true). It’s important to understand what variable you’re using in a flow-control statement because of this automatic conversion. Mistakenly using an object instead of a Boolean can drastically alter the flow of your application.

The Number Type

Perhaps the most interesting data type in ECMAScript is Number, which uses the IEEE-754 format to represent both integers and floating-point values (also called double-precision values in some languages). To support the various types of numbers, there are several different number literal formats.

The most basic number literal format is that of a decimal integer, which can be entered directly as shown here:

var intNum = 55;         //integer

Integers can also be represented as either octal (base 8) or hexadecimal (base 16) literals. For an octal literal, the first digit must be a zero (0) followed by a sequence of octal digits (numbers 0 through 7). If a number out of this range is detected in the literal, then the leading zero is ignored and the number is treated as a decimal, as in the following examples:

var octalNum1 = 070;     //octal for 56
var octalNum2 = 079;     //invalid octal - interpreted as 79
var octalNum3 = 08;      //invalid octal - interpreted as 8

Octal literals are invalid when running in strict mode and will cause the JavaScript engine to throw a syntax error.

To create a hexadecimal literal, you must make the first two characters 0x (case insensitive), followed by any number of hexadecimal digits (0 through 9, and A through F). Letters may be in uppercase or lowercase. Here’s an example:

var hexNum1 = 0xA;       //hexadecimal for 10
var hexNum2 = 0x1f;      //hexedecimal for 31

Numbers created using octal or hexadecimal format are treated as decimal numbers in all arithmetic operations.

image

Because of the way that numbers are stored in JavaScript, it is actually possible to have a value of positive zero (+0) and negative zero (–0). Positive zero and negative zero are considered equivalent in all cases but are noted in this text for clarity.

Floating-Point Values

To define a floating-point value, you must include a decimal point and at least one number after the decimal point. Although an integer is not necessary before a decimal point, it is recommended. Here are some examples:

var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1;     //valid, but not recommended

Because storing floating-point values uses twice as much memory as storing integer values, ECMAScript always looks for ways to convert values into integers. When there is no digit after the decimal point, the number becomes an integer. Likewise, if the number being represented is a whole number (such as 1.0), it will be converted into an integer, as in this example:

var floatNum1 = 1.;     //missing digit after decimal - interpreted as integer 1
var floatNum2 = 10.0;   //whole number - interpreted as integer 10

For very large or very small numbers, floating-point values can be represented using e-notation. E-notation is used to indicate a number that should be multiplied by 10 raised to a given power. The format of e-notation in ECMAScript is to have a number (integer or floating-point) followed by an uppercase or lowercase letter E, followed by the power of 10 to multiply by. Consider the following:

var floatNum = 3.125e7;    //equal to 31250000

In this example, floatNum is equal to 31,250,000 even though it is represented in a more compact form using e-notation. The notation essentially says, “Take 3.125 and multiply it by 107.”

E-notation can also be used to represent very small numbers, such as 0.00000000000000003, which can be written more succinctly as 3e-17. By default, ECMAScript converts any floating-point value with at least six zeros after the decimal point into e-notation (for example, 0.0000003 becomes 3e-7).

Floating-point values are accurate up to 17 decimal places but are far less accurate in arithmetic computations than whole numbers. For instance, adding 0.1 and 0.2 yields 0.30000000000000004 instead of 0.3. These small rounding errors make it difficult to test for specific floating-point values. Consider this example:

if (a + b == 0.3){          //avoid!
    alert("You got 0.3.");
}

Here the sum of two numbers is tested to see if it’s equal to 0.3. This will work for 0.05 and 0.25 and for 0.15 and 0.15. But if applied to 0.1 and 0.2, as discussed previously, this test would fail. Therefore you should never test for specific floating-point values.

image

It’s important to understand that rounding errors are a side effect of the way floating-point arithmetic is done in IEEE-754–based numbers and is not unique to ECMAScript. Other languages that use the same format have the same issues.

Range of Values

Not all numbers in the world can be represented in ECMAScript, because of memory constraints. The smallest number that can be represented in ECMAScript is stored in Number.MIN_VALUE and is 5e-324 on most browsers; the largest number is stored in Number.MAX_VALUE and is 1.7976931348623157e+308 on most browsers. If a calculation results in a number that cannot be represented by JavaScript’s numeric range, the number automatically gets the special value of Infinity. Any negative number that can’t be represented is –Infinity (negative infinity), and any positive number that can’t be represented is simply Infinity (positive infinity).

If a calculation returns either positive or negative Infinity, that value cannot be used in any further calculations, because Infinity has no numeric representation with which to calculate. To determine if a value is finite (that is, it occurs between the minimum and the maximum), there is the isFinite() function. This function returns true only if the argument is between the minimum and the maximum values, as in this example:

var result = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result));    //false

Though it is rare to do calculations that take values outside of the range of finite numbers, it is possible and should be monitored when doing very large or very small calculations.

image

You can also get the values of positive and negative Infinity by accessing Number.NEGATIVE_INFINITY and Number.POSITIVE_INFINITY. As you may expect, these properties contain the values –Infinity and Infinity, respectively.

NaN

There is a special numeric value called NaN, short for Not a Number, which is used to indicate when an operation intended to return a number has failed (as opposed to throwing an error). For example, dividing any number by 0 typically causes an error in other programming languages, halting code execution. In ECMAScript, dividing a number by 0 returns NaN, which allows other processing to continue.

The value NaN has a couple of unique properties. First, any operation involving NaN always returns NaN (for instance, NaN /10), which can be problematic in the case of multistep computations. Second, NaN is not equal to any value, including NaN. For example, the following returns false:

alert(NaN == NaN);    //false

For this reason, ECMAScript provides the isNaN() function. This function accepts a single argument, which can be of any data type, to determine if the value is “not a number.” When a value is passed into isNaN(), an attempt is made to convert it into a number. Some nonnumber values convert into numbers directly, such as the string "10" or a Boolean value. Any value that cannot be converted into a number causes the function to return true. Consider the following:

image
alert(isNaN(NaN));       //true
alert(isNaN(10));        //false - 10 is a number
alert(isNaN("10"));      //false - can be converted to number 10
alert(isNaN("blue"));    //true - cannot be converted to a number
alert(isNaN(true));      //false - can be converted to number 1

NumberExample03.htm

This example tests five different values. The first test is on the value NaN itself, which, obviously, returns true. The next two tests use numeric 10 and the string "10", which both return false, because the numeric value for each is 10. The string "blue", however, cannot be converted into a number, so the function returns false. The Boolean value of true can be converted into the number 1, so the function returns false.

image

Although typically not done, isNaN() can be applied to objects. In that case, the object’s valueOf() method is first called to determine if the returned value can be converted into a number. If not, the toString() method is called and its returned value is tested as well. This is the general way that built-in functions and operators work in ECMAScript and is discussed more in the “Operators” section later in this chapter.

Number Conversions

There are three functions to convert nonnumeric values into numbers: the Number() casting function, the parseInt() function, and the parseFloat() function. The first function, Number(), can be used on any data type; the other two functions are used specifically for converting strings to numbers. Each of these functions reacts differently to the same input.

The Number() function performs conversions based on these rules:

  • When applied to Boolean values, true and false get converted into 1 and 0, respectively.
  • When applied to numbers, the value is simply passed through and returned.
  • When applied to null, Number() returns 0.
  • When applied to undefined, Number() returns NaN.
  • When applied to strings, the following rules are applied:
    • If the string contains only numbers, optionally preceded by a plus or minus sign, it is always converted to a decimal number, so "1" becomes 1, "123" becomes 123, and "011" becomes 11 (note: leading zeros are ignored).
    • If the string contains a valid floating-point format, such as "1.1", it is converted into the appropriate floating-point numeric value (once again, leading zeros are ignored).
    • If the string contains a valid hexadecimal format, such as "0xf", it is converted into an integer that matches the hexadecimal value.
    • If the string is empty (contains no characters), it is converted to 0.
    • If the string contains anything other than these previous formats, it is converted into NaN.
  • When applied to objects, the valueOf() method is called and the returned value is converted based on the previously described rules. If that conversion results in NaN, the toString() method is called and the rules for converting strings are applied.

Converting to numbers from various data types can get complicated, as indicated by the number of rules there are for Number(). Here are some concrete examples:

image
var num1 = Number("Hello world!");  //NaN
var num2 = Number("");              //0
var num3 = Number("000011");        //11
var num4 = Number(true);            //1

NumberExample04.htm

In these examples, the string "Hello world" is converted into NaN because it has no corresponding numeric value, and the empty string is converted into 0. The string "000011" is converted to the number 11 because the initial zeros are ignored. Last, the value true is converted to 1.

image

The unary plus operator, discussed in the “Operators” section later in this chapter, works the same as the Number() function.

Because of the complexities and oddities of the Number() function when converting strings, the parseInt() function is usually a better option when you are dealing with integers. The parseInt() function examines the string much more closely to see if it matches a number pattern. Leading white space in the string is ignored until the first non–white space character is found. If this first character isn’t a number, the minus sign, or the plus sign, parseInt() always returns NaN, which means the empty string returns NaN (unlike with Number(), which returns 0). If the first character is a number, plus, or minus, then the conversion goes on to the second character and continues on until either the end of the string is reached or a nonnumeric character is found. For instance, "1234blue" is converted to 1234 because "blue" is completely ignored. Similarly, "22.5" will be converted to 22 because the decimal is not a valid integer character.

Assuming that the first character in the string is a number, the parseInt() function also recognizes the various integer formats (decimal, octal, and hexadecimal, as discussed previously). This means when the string begins with "0x", it is interpreted as a hexadecimal integer; if it begins with "0" followed by a number, it is interpreted as an octal value.

Here are some conversion examples to better illustrate what happens:

image
var num1 = parseInt("1234blue");    //1234
var num2 = parseInt("");            //NaN
var num3 = parseInt("0xA");         //10 - hexadecimal
var num4 = parseInt(22.5);          //22
var num5 = parseInt("70");          //70 - decimal
var num6 = parseInt("0xf");         //15 - hexadecimal

NumberExample05.htm

There is a discrepancy between ECMAScript 3 and 5 in regard to using parseInt() with a string that looks like an octal literal. For example:

//56 (octal) in ECMAScript 3, 0 (decimal) in ECMAScript 5
var num = parseInt("070");

In ECMAScript 3 JavaScript engines, the value "070" is treated as an octal literal and becomes the decimal value 56. In ECMAScript 5 JavaScript engines, the ability to parse octal values has been removed from parseInt(), so the leading zero is considered invalid and the value is treated the same as “0”, resulting in the decimal value 0. This is true even when running ECMAScript 5 in non-strict mode.

All of the different numeric formats can be confusing to keep track of, so parseInt() provides a second argument: the radix (number of digits) to use. If you know that the value you’re parsing is in hexadecimal format, you can pass in the radix 16 as a second argument and ensure that the correct parsing will occur, as shown here:

var num = parseInt("0xAF", 16);       //175

In fact, by providing the hexadecimal radix, you can leave off the leading "0x" and the conversion will work as follows:

var num1 = parseInt("AF", 16);        //175
var num2 = parseInt("AF");            //NaN

NumberExample06.htm

In this example, the first conversion occurs correctly, but the second conversion fails. The difference is that the radix is passed in on the first line, telling parseInt() that it will be passed a hexadecimal string; the second line sees that the first character is not a number and stops automatically.

Passing in a radix can greatly change the outcome of the conversion. Consider the following:

image
var num1 = parseInt("10", 2);         //2 - parsed as binary
var num2 = parseInt("10", 8);         //8 - parsed as octal
var num3 = parseInt("10", 10);        //10 - parsed as decimal
var num4 = parseInt("10", 16);        //16 - parsed as hexadecimal

NumberExample07.htm

Because leaving off the radix allows parseInt() to choose how to interpret the input, it’s advisable to always include a radix to avoid errors.

image

Most of the time you’ll be parsing decimal numbers, so it’s good to always include 10 as the second argument.

The parseFloat() function works in a similar way to parseInt(), looking at each character starting in position 0. It also continues to parse the string until it reaches either the end of the string or a character that is invalid in a floating-point number. This means that a decimal point is valid the first time it appears, but a second decimal point is invalid and the rest of the string is ignored, resulting in "22.34.5" being converted to 22.34.

Another difference in parseFloat() is that initial zeros are always ignored. This function will recognize any of the floating-point formats discussed earlier, as well as the decimal format (leading zeros are always ignored). Hexadecimal numbers always become 0. Because parseFloat() parses only decimal values, there is no radix mode. A final note: if the string represents a whole number (no decimal point or only a zero after the decimal point), parseFloat() returns an integer. Here are some examples:

var num1 = parseFloat("1234blue");    //1234 - integer
var num2 = parseFloat("0xA");         //0
var num3 = parseFloat("22.5");        //22.5
var num4 = parseFloat("22.34.5");     //22.34
var num5 = parseFloat("0908.5");      //908.5
var num6 = parseFloat("3.125e7");     //31250000

NumberExample08.htm

The String Type

The String data type represents a sequence of zero or more 16-bit Unicode characters. Strings can be delineated by either double quotes (") or single quotes ('), so both of the following are legal:

var firstName = "Nicholas";
var lastName = 'Zakas';

Unlike PHP, for which using double or single quotes changes how the string is interpreted, there is no difference in the two syntaxes in ECMAScript. A string using double quotes is exactly the same as a string using single quotes. Note, however, that a string beginning with a double quote must end with a double quote, and a string beginning with a single quote must end with a single quote. For example, the following will cause a syntax error:

var firstName = 'Nicholas";    //syntax error - quotes must match

Character Literals

The String data type includes several character literals to represent nonprintable or otherwise useful characters, as listed in the following table:

LITERAL MEANING
New line
Tab
 Backspace
Carriage return
f Form feed
\ Backslash ()
' Single quote (') — used when the string is delineated by single quotes. Example: 'He said, 'hey.''.
" Double quote (") — used when the string is delineated by double quotes. Example: "He said, "hey."".
xnn A character represented by hexadecimal code nn (where n is a hexadecimal digit 0-F). Example: x41 is equivalent to "A".
unnnn A Unicode character represented by the hexadecimal code nnnn (where n is a hexadecimal digit 0-F). Example: u03a3 is equivalent to the Greek character <Symbol>Σ</Symbol>.

These character literals can be included anywhere with a string and will be interpreted as if they were a single character, as shown here:

var text = "This is the letter sigma: u03a3.";

In this example, the variable text is 28 characters long even though the escape sequence is 6 characters long. The entire escape sequence represents a single character, so it is counted as such.

The length of any string can be returned by using the length property as follows:

alert(text.length);  //outputs 28

This property returns the number of 16-bit characters in the string. If a string contains double-byte characters, the length property may not accurately return the number of characters in the string.

The Nature of Strings

Strings are immutable in ECMAScript, meaning that once they are created, their values cannot change. To change the string held by a variable, the original string must be destroyed and the variable filled with another string containing a new value, like this:

var lang = "Java";
lang = lang + "Script";

Here, the variable lang is defined to contain the string "Java". On the next line, lang is redefined to combined "Java" with "Script", making its value "JavaScript". This happens by creating a new string with enough space for 10 characters and then filling that string with "Java" and "Script". The last step in the process is to destroy the original string "Java" and the string "Script", because neither is necessary anymore. All of this happens behind the scenes, which is why older browsers (such as pre-1.0 versions of Firefox and Internet Explorer 6.0) had very slow string concatenation. These inefficiencies were addressed in later versions of these browsers.

Converting to a String

There are two ways to convert a value into a string. The first is to use the toString() method that almost every value has. (The nature of this method is discussed in Chapter 5.) This method’s only job is to return the string equivalent of the value. Consider this example:

image
var age = 11;
var ageAsString = age.toString();    //the string "11"
var found = true;
var foundAsString = found.toString(); //the string "true"

StringExample01.htm

The toString() method is available on values that are numbers, Booleans, objects, and strings. (Yes, each string has a toString() method that simply returns a copy of itself.) If a value is null or undefined, this method is not available.

In most cases, toString() doesn’t have any arguments. However, when used on a number value, toString() actually accepts a single argument: the radix in which to output the number. By default, toString() always returns a string that represents the number as a decimal, but by passing in a radix, toString() can output the value in binary, octal, hexadecimal, or any other valid base, as in this example:

var num = 10;
alert(num.toString());       //"10"
alert(num.toString(2));      //"1010"
alert(num.toString(8));      //"12"
alert(num.toString(10));     //"10"
alert(num.toString(16));     //"a"

StringExample02.htm

This example shows how the output of toString() can change for numbers when providing a radix. The value 10 can be output into any number of numeric formats. Note that the default (with no argument) is the same as providing a radix of 10.

If you’re not sure that a value isn’t null or undefined, you can use the String() casting function, which always returns a string regardless of the value type. The String() function follows these rules:

  • If the value has a toString() method, it is called (with no arguments) and the result is returned.
  • If the value is null, "null" is returned.
  • If the value is undefined, "undefined" is returned.

Consider the following:

image
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
                   
alert(String(value1));     //"10"
alert(String(value2));     //"true"
alert(String(value3));     //"null"
alert(String(value4));     //"undefined"

StringExample03.htm

Here, four values are converted into strings: a number, a Boolean, null, and undefined. The result for the number and the Boolean are the same as if toString() were called. Because toString() isn’t available on "null" and "undefined", the String() method simply returns literal text for those values.

image

You can also convert a value to a string by adding an empty string (“”) to that value using the plus operator (discussed in the “Operators” section later in this chapter).

The Object Type

Objects in ECMAScript start out as nonspecific groups of data and functionality. Objects are created by using the new operator followed by the name of the object type to create. Developers create their own objects by creating instances of the Object type and adding properties and/or methods to it, as shown here:

var o = new Object();

This syntax is similar to Java, although ECMAScript requires parentheses to be used only when providing arguments to the constructor. If there are no arguments, as in the following example, then the parentheses can be omitted safely (though that’s not recommended):

var o = new Object;  //legal, but not recommended

Instances of Object aren’t very useful on their own, but the concepts are important to understand, because, similar to java.lang.Object in Java, the Object type in ECMAScript is the base from which all other objects are derived. All of the properties and methods of the Object type are also present on other, more specific objects.

Each Object instance has the following properties and methods:

  • constructor — The function that was used to create the object. In the previous example, the constructor is the Object() function.
  • hasOwnProperty(propertyName) — Indicates if the given property exists on the object instance (not on the prototype). The property name must be specified as a string (for example, o.hasOwnProperty("name")).
  • isPrototypeOf(object) — Determines if the object is a prototype of another object. (Prototypes are discussed in Chapter 5.)
  • propertyIsEnumerable(propertyName) — Indicates if the given property can be enumerated using the for-in statement (discussed later in this chapter). As with hasOwnProperty(), the property name must be a string.
  • toLocaleString() — Returns a string representation of the object that is appropriate for the locale of execution environment.
  • toString() — Returns a string representation of the object.
  • valueOf() — Returns a string, number, or Boolean equivalent of the object. It often returns the same value as toString().

Since Object is the base for all objects in ECMAScript, every object has these base properties and methods. Chapters 5 and 6 cover the specifics of how this occurs.

image

Technically speaking, the behavior of objects in ECMA-262 need not necessarily apply to other objects in JavaScript. Objects that exist in the browser environment, such as those in the Browser Object Model (BOM) and Document Object Model (DOM), are considered host objects since they are provided and defined by the host implementation. Host objects aren’t governed by ECMA-262 and, as such, may or may not directly inherit from Object.

OPERATORS

ECMA-262 describes a set of operators that can be used to manipulate data values. The operators range from mathematical operations (such as addition and subtraction) and bitwise operators to relational operators and equality operators. Operators are unique in ECMAScript in that they can be used on a wide range of values, including strings, numbers, Booleans, and even objects. When used on objects, operators typically call the valueOf() and/or toString() method to retrieve a value they can work with.

Unary Operators

Operators that work on only one value are called unary operators. They are the simplest operators in ECMAScript.

Increment/Decrement

The increment and decrement operators are taken directly from C and come in two versions: prefix and postfix. The prefix versions of the operators are placed before the variable they work on; the postfix ones are placed after the variable. To use a prefix increment, which adds 1 to a numeric value, you place two plus signs (++) in front of a variable like this:

var age = 29;
++age;

In this example, the prefix increment changes the value of age to 30 (adding 1 to its previous value of 29). This is effectively equal to the following:

var age = 29;
age = age + 1;

The prefix decrement acts in a similar manner, subtracting 1 from a numeric value. To use a prefix decrement, place two minus signs (--) before a variable, as shown here:

var age = 29;
--age;

Here the age variable is decremented to 28 (subtracting 1 from 29).

When using either a prefix increment or a prefix decrement, the variable’s value is changed before the statement is evaluated. (In computer science, this is usually referred to as having a side effect.) Consider the following:

image
var age = 29;
var anotherAge = --age + 2;
                   
alert(age);         //outputs 28
alert(anotherAge);  //outputs 30

IncrementDecrementExample01.htm

In this example, the variable anotherAge is initialized with the decremented value of age plus 2. Because the decrement happens first, age is set to 28, and then 2 is added, resulting in 30.

The prefix increment and decrement are equal in terms of order of precedence in a statement and are therefore evaluated left to right. Consider this example:

image
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2;    //equals 21
var num4 = num1 + num2;      //equals 21

IncrementDecrementExample02.htm

Here, num3 is equal to 21 because num1 is decremented to 1 before the addition occurs. The variable num4 also contains 21, because the addition is also done using the changed values.

The postfix versions of increment and decrement use the same syntax (++ and --, respectively) but are placed after the variable instead of before it. Postfix increment and decrement differ from the prefix versions in one important way: the increment or decrement doesn’t occur until after the containing statement has been evaluated. In certain circumstances, this difference doesn’t matter, as in this example:

var age = 29;
age++;

Moving the increment operator after the variable doesn’t change what these statements do, because the increment is the only operation occurring. However, when mixed together with other operations, the difference becomes apparent, as in the following example:

var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2;    //equals 22
var num4 = num1 + num2;      //equals 21

IncrementDecrementExample03.htm

With just one simple change in this example, using postfix decrement instead of prefix, you can see the difference. In the prefix example, num3 and num4 both ended up equal to 21, whereas this example ends with num3 equal to 22 and num4 equal to 21. The difference is that the calculation for num3 uses the original value of num1 (2) to complete the addition, whereas num4 is using the decremented value (1).

All four of these operators work on any values, meaning not just integers but strings, Booleans, floating-point values, and objects. The increment and decrement operators follow these rules regarding values:

  • When used on a string that is a valid representation of a number, convert to a number and apply the change. The variable is changed from a string to a number.
  • When used on a string that is not a valid number, the variable’s value is set to NaN (discussed in Chapter 4). The variable is changed from a string to a number.
  • When used on a Boolean value that is false, convert to 0 and apply the change. The variable is changed from a Boolean to a number.
  • When used on a Boolean value that is true, convert to 1 and apply the change. The variable is changed from a Boolean to a number.
  • When used on a floating-point value, apply the change by adding or subtracting 1.
  • When used on an object, call its valueOf() method (discussed more in Chapter 5) to get a value to work with. Apply the other rules. If the result is NaN, then call toString() and apply the other rules again. The variable is changed from an object to a number.

The following example demonstrates some of these rules:

image
var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = { 
    valueOf: function() {
        return -1;
    }
};
                   
s1++;   //value becomes numeric 3
s2++;   //value becomes NaN
b++;    //value becomes numeric 1
f--;    //value becomes 0.10000000000000009 (due to floating-point inaccuracies)
o--;    //value becomes numeric -2

IncrementDecrementExample04.htm

Unary Plus and Minus

The unary plus and minus operators are familiar symbols to most developers and operate the same way in ECMAScript as they do in high-school math. The unary plus is represented by a single plus sign (+) placed before a variable and does nothing to a numeric value, as shown in this example:

var num = 25;
num = +num;    //still 25

When the unary plus is applied to a nonnumeric value, it performs the same conversion as the Number() casting function: the Boolean values of false and true are converted to 0 and 1, string values are parsed according to a set of specific rules, and objects have their valueOf() and/or toString() method called to get a value to convert.

The following example demonstrates the behavior of the unary plus when acting on different data types:

var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = { 
    valueOf: function() {
        return -1;
    }
};
                   
s1 = +s1;   //value becomes numeric 1
s2 = +s2;   //value becomes numeric 1.1
s3 = +s3;   //value becomes NaN
b = +b;     //value becomes numeric 0
f = +f;     //no change, still 1.1
o = +o;     //value becomes numeric -1

UnaryPlusMinusExample01.htm

The unary minus operator’s primary use is to negate a numeric value, such as converting 1 into –1. The simple case is illustrated here:

var num = 25;
num = -num;    //becomes -25

When used on a numeric value, the unary minus simply negates the value (as in this example). When used on nonnumeric values, unary minus applies all of the same rules as unary plus and then negates the result, as shown here:

image
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = { 
    valueOf: function() {
        return -1;
    }
};
                   
s1 = -s1;   //value becomes numeric -1
s2 = -s2;   //value becomes numeric -1.1
s3 = -s3;   //value becomes NaN
b = -b;     //value becomes numeric 0
f = -f;     //change to -1.1
o = -o;     //value becomes numeric 1

UnaryPlusMinusExample02.htm

The unary plus and minus operators are used primarily for basic arithmetic but can also be useful for conversion purposes, as illustrated in the previous example.

Bitwise Operators

The next set of operators works with numbers at their very base level, with the bits that represent them in memory. All numbers in ECMAScript are stored in IEEE-754 64-bit format, but the bitwise operations do not work directly on the 64-bit representation. Instead, the value is converted into a 32-bit integer, the operation takes place, and the result is converted back into 64 bits. To the developer, it appears that only the 32-bit integer exists, because the 64-bit storage format is transparent. With that in mind, consider how 32-bit integers work.

Signed integers use the first 31 of the 32 bits to represent the numeric value of the integer. The 32nd bit represents the sign of the number: 0 for positive or 1 for negative. Depending on the value of that bit, called the sign bit, the format of the rest of the number is determined. Positive numbers are stored in true binary format, with each of the 31 bits representing a power of 2, starting with the first bit (called bit 0), representing 20, the second bit represents 21, and so on. If any bits are unused, they are filled with 0 and essentially ignored. For example, the number 18 is represented as 00000000000000000000000000010010, or more succinctly as 10010. These are the five most significant bits and can be used, by themselves, to determine the actual value (see Figure 3-1).

Negative numbers are also stored in binary code but in a format called two’s complement. The two’s complement of a number is calculated in three steps:

1. Determine the binary representation of the absolute value (for example, to find –18, first determine the binary representation of 18).

2. Find the one’s complement of the number, which essentially means that every 0 must be replaced with a 1 and vice versa.

3. Add 1 to the result.

Using this process to determine the binary representation –18, start with the binary representation of 18, which is the following:

0000 0000 0000 0000 0000 0000 0001 0010

Next, take the one’s complement, which is the inverse of this number:

1111 1111 1111 1111 1111 1111 1110 1101

Finally, add 1 to the one’s complement as follows:

1111 1111 1111 1111 1111 1111 1110 1101
                                      1
---------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110

So the binary equivalent of –18 is 11111111111111111111111111101110. Keep in mind that you have no access to bit 31 when dealing with signed integers.

ECMAScript does its best to keep all of this information from you. When outputting a negative number as a binary string, you get the binary code of the absolute value preceded by a minus sign, as in this example:

var num = -18;
alert(num.toString(2));    //"-10010"

When you convert the number –18 to a binary string, the result is –10010. The conversion process interprets the two’s complement and represents it in an arguably more logical form.

image

By default, all integers are represented as signed in ECMAScript. There is, however, such a thing as an unsigned integer. In an unsigned integer, the 32nd bit doesn’t represent the sign, because there are only positive numbers. Unsigned integers also can be larger, because the extra bit becomes part of the number instead of an indicator of the sign.

When you apply bitwise operators to numbers in ECMAScript, a conversion takes place behind the scenes: the 64-bit number is converted into a 32-bit number, the operation is performed, and then the 32-bit result is stored back into a 64-bit number. This gives the illusion that you’re dealing with true 32-bit numbers, which makes the binary operations work in a way similar to the operations of other languages. A curious side effect of this conversion is that the special values NaN and Infinity both are treated as equivalent to 0 when used in bitwise operations.

If a bitwise operator is applied to a nonnumeric value, the value is first converted into a number using the Number() function (this is done automatically) and then the bitwise operation is applied. The resulting value is a number.

Bitwise NOT

The bitwise NOT is represented by a tilde (~) and simply returns the one’s complement of the number. Bitwise NOT is one of just a few ECMAScript operators related to binary mathematics. Consider this example:

image
var num1 = 25;             //binary 00000000000000000000000000011001
var num2 = ~num1;          //binary 11111111111111111111111111100110
alert(num2);               //-26

BitwiseNotExample01.htm

Here, the bitwise NOT operator is used on 25, producing –26 as the result. This is the end effect of the bitwise NOT: it negates the number and subtracts 1. The same outcome is produced with the following code:

var num1 = 25; 
var num2 = -num1 - 1;
alert(num2);               //"-26"

Realistically, though this returns the same result, the bitwise operation is much faster, because it works at the very lowest level of numeric representation.

Bitwise AND

The bitwise AND operator is indicated by the ampersand character (&) and works on two values. Essentially, bitwise AND lines up the bits in each number and then, using the rules in the following truth table, performs an AND operation between the two bits in the same position.

BIT FROM FIRST NUMBER BIT FROM SECOND NUMBER RESULT
1 1 1
1 0 0
0 1 0
0 0 0

The short description of a bitwise AND is that the result will be 1 only if both bits are 1. If either bit is 0, then the result is 0.

As an example, to AND the numbers 25 and 3 together, use the following code:

image
var result = 25 & 3;
alert(result);       //1

BitwiseAndExample01.htm

The result of a bitwise AND between 25 and 3 is 1. Why is that? Take a look:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001

As you can see, only one bit (bit 0) contains a 1 in both 25 and 3. Because of this, every other bit of the resulting number is set to 0, making the result equal to 1.

Bitwise OR

The bitwise OR operator is represented by a single pipe character ( | ) and also works on two numbers. Bitwise OR follows the rules in this truth table:

BIT FROM FIRST NUMBER BIT FROM SECOND NUMBER RESULT
1 1 1
1 0 1
0 1 1
0 0 0

A bitwise OR operation returns 1 if at least one bit is 1. It returns 0 only if both bits are 0.

Using the same example as for bitwise AND, if you want to OR the numbers 25 and 3 together, the code looks like this:

image
var result = 25 | 3;
alert(result);       //27

BitwiseOrExample01.htm

The result of a bitwise OR between 25 and 3 is 27:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
 OR = 0000 0000 0000 0000 0000 0000 0001 1011

In each number, four bits are set to 1, so these are passed through to the result. The binary code 11011 is equal to 27.

Bitwise XOR

The bitwise XOR operator is represented by a caret (^) and also works on two values. Here is the truth table for bitwise XOR:

BIT FROM FIRST NUMBER BIT FROM SECOND NUMBER RESULT
1 1 0
1 0 1
0 1 1
0 0 0

Bitwise XOR is different from bitwise OR in that it returns 1 only when exactly one bit has a value of 1 (if both bits contain 1, it returns 0).

To XOR the numbers 25 and 3 together, use the following code:

var result = 25 ^ 3;
alert(result);    //26

BitwiseXorExample01.htm

The result of a bitwise XOR between 25 and 3 is 26, as shown here:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010

Four bits in each number are set to 1; however, the first bit in both numbers is 1, so that becomes 0 in the result. All of the other 1s have no corresponding 1 in the other number, so they are passed directly through to the result. The binary code 11010 is equal to 26. (Note that this is one less than when performing bitwise OR on these numbers.)

Left Shift

The left shift is represented by two less-than signs (<<) and shifts all bits in a number to the left by the number of positions given. For example, if the number 2 (which is equal to 10 in binary) is shifted 5 bits to the left, the result is 64 (which is equal to 1000000 in binary), as shown here:

image
var oldValue = 2;             //equal to binary 10
var newValue = oldValue << 5; //equal to binary 1000000 which is decimal 64

LeftShiftExample01.htm

Note that when the bits are shifted, five empty bits remain to the right of the number. The left shift fills these bits with 0s to make the result a complete 32-bit number (see Figure 3-2).

Note that left shift preserves the sign of the number it’s operating on. For instance, if –2 is shifted to the left by five spaces, it becomes –64, not positive 64.

Signed Right Shift

The signed right shift is represented by two greater-than signs (>>) and shifts all bits in a 32-bit number to the right while preserving the sign (positive or negative). A signed right shift is the exact opposite of a left shift. For example, if 64 is shifted to the right five bits, it becomes 2:

var oldValue = 64;               //equal to binary 1000000
var newValue = oldValue >> 5;    //equal to binary 10 which is decimal 2

SignedRightShiftExample01.htm

Once again, when bits are shifted, the shift creates empty bits. This time, the empty bits occur at the left of the number but after the sign bit (see Figure 3-3). Once again, ECMAScript fills these empty bits with the value in the sign bit to create a complete number.

Unsigned Right Shift

The unsigned right shift is represented by three greater-than signs (>>>) and shifts all bits in a 32-bit number to the right. For numbers that are positive, the effect is the same as a signed right shift. Using the same example as for the signed-right-shift example, if 64 is shifted to the right five bits, it becomes 2:

image
var oldValue = 64;               //equal to binary 1000000
var newValue = oldValue >>> 5;   //equal to binary 10 which is decimal 2

UnsignedRightShiftExample01.htm

For numbers that are negative, however, something quite different happens. Unlike signed right shift, the empty bits get filled with zeros regardless of the sign of the number. For positive numbers, it has the same effect as a signed right shift; for negative numbers, the result is quite different. The unsigned-right-shift operator considers the binary representation of the negative number to be representative of a positive number instead. Because the negative number is the two’s complement of its absolute value, the number becomes very large, as you can see in the following example:

var oldValue = -64;              //equal to binary 11111111111111111111111111000000
var newValue = oldValue >>> 5;   //equal to decimal 134217726

UnsignedRightShiftExample02.htm

When an unsigned right shift is used to shift –64 to the right by five bits, the result is 134217726. This happens because the binary representation of –64 is 11111111111111111111111111000000, but because the unsigned right shift treats this as a positive number, it considers the value to be 4294967232. When this value is shifted to the right by five bits, it becomes 00000111111111111111111111111110, which is 134217726.

Boolean Operators

Almost as important as equality operators, Boolean operators are what make a programming language function. Without the capability to test relationships between two values, statements such as if...else and loops wouldn’t be useful. There are three Boolean operators: NOT, AND, and OR.

Logical NOT

The logical NOT operator is represented by an exclamation point (!) and may be applied to any value in ECMAScript. This operator always returns a Boolean value, regardless of the data type it’s used on. The logical NOT operator first converts the operand to a Boolean value and then negates it, meaning that the logical NOT behaves in the following ways:

  • If the operand is an object, false is returned.
  • If the operand is an empty string, true is returned.
  • If the operand is a nonempty string, false is returned.
  • If the operand is the number 0, true is returned.
  • If the operand is any number other than 0 (including Infinity), false is returned.
  • If the operand is null, true is returned.
  • If the operand is NaN, true is returned.
  • If the operand is undefined, true is returned.

The following example illustrates this behavior:

image
alert(!false);      //true
alert(!"blue");     //false
alert(!0);          //true
alert(!NaN);        //true
alert(!"");         //true
alert(!12345);      //false

LogicalNotExample01.htm

The logical NOT operator can also be used to convert a value into its Boolean equivalent. By using two NOT operators in a row, you can effectively simulate the behavior of the Boolean() casting function. The first NOT returns a Boolean value no matter what operand it is given. The second NOT negates that Boolean value and so gives the true Boolean value of a variable. The end result is the same as using the Boolean() function on a value, as shown here:

image
alert(!!"blue");     //true
alert(!!0);          //false
alert(!!NaN);        //false
alert(!!"");         //false
alert(!!12345);      //true

LogicalNotExample02.htm

Logical AND

The logical AND operator is represented by the double ampersand (&&) and is applied to two values, such as in this example:

var result = true && false;

Logical AND behaves as described in the following truth table:

OPERAND 1 OPERAND 2 RESULT
true true true
true false false
false true false
false false false

Logical AND can be used with any type of operand, not just Boolean values. When either operand is not a primitive Boolean, logical AND does not always return a Boolean value; instead, it does one of the following:

  • If the first operand is an object, then the second operand is always returned.
  • If the second operand is an object, then the object is returned only if the first operand evaluates to true.
  • If both operands are objects, then the second operand is returned.
  • If either operand is null, then null is returned.
  • If either operand is NaN, then NaN is returned.
  • If either operand is undefined, then undefined is returned.

The logical AND operator is a short-circuited operation, meaning that if the first operand determines the result, the second operand is never evaluated. In the case of logical AND, if the first operand is false, no matter what the value of the second operand, the result can’t be equal to true. Consider the following example:

image
var found = true;
var result = (found && someUndeclaredVariable);    //error occurs here
alert(result);    //this line never executes

LogicalAndExample01.htm

This code causes an error when the logical AND is evaluated, because the variable someUndeclaredVariable isn’t declared. The value of the variable found is true, so the logical AND operator continued to evaluate the variable someUndeclaredVariable. When it did, an error occurred because someUndeclaredVariable is not declared and therefore cannot be used in a logical AND operation. If found is instead set to false, as in the following example, the error won’t occur:

var found = false;
var result = (found && someUndeclaredVariable);    //no error
alert(result);    //works

LogicalAndExample02.htm

In this code, the alert is displayed successfully. Even though the variable someUndeclaredVariable is undefined, it is never evaluated, because the first operand is false. This means that the result of the operation must be false, so there is no reason to evaluate what’s to the right of the &&. Always keep in mind short-circuiting when using logical AND.

Logical OR

The logical OR operator is represented by the double pipe (||) in ECMAScript, like this:

var result = true || false;

Logical OR behaves as described in the following truth table:

OPERAND 1 OPERAND 2 RESULT
true true true
true false true
false true true
false false false

Just like logical AND, if either operand is not a Boolean, logical OR will not always return a Boolean value; instead, it does one of the following:

  • If the first operand is an object, then the first operand is returned.
  • If the first operand evaluates to false, then the second operand is returned.
  • If both operands are objects, then the first operand is returned.
  • If both operands are null, then null is returned.
  • If both operands are NaN, then NaN is returned.
  • If both operands are undefined, then undefined is returned.

Also like the logical AND operator, the logical OR operator is short-circuited. In this case, if the first operand evaluates to true, the second operand is not evaluated. Consider this example:

image
var found = true;
var result = (found || someUndeclaredVariable);    //no error
alert(result);    //works

LogicalOrExample01.htm

As with the previous example, the variable someUndefinedVariable is undefined. However, because the variable found is set to true, the variable someUndefinedVariable is never evaluated and thus the output is "true". If the value of found is changed to false, an error occurs, as in the following example:

var found = false;
var result = (found || someUndeclaredVariable);    //error occurs here
alert(result);    //this line never executes

LogicalOrExample02.htm

You can also use this behavior to avoid assigning a null or undefined value to a variable. Consider the following:

var myObject = preferredObject || backupObject;

In this example, the variable myObject will be assigned one of two values. The preferredObject variable contains the value that is preferred if it’s available, whereas the backupObject variable contains the backup value if the preferred one isn’t available. If preferredObject isn’t null, then it’s assigned to myObject; if it is null, then backupObject is assigned to myObject. This pattern is used very frequently in ECMAScript for variable assignment and is used throughout this book.

Multiplicative Operators

There are three multiplicative operators in ECMAScript: multiply, divide, and modulus. These operators work in a manner similar to their counterparts in languages such as Java, C, and Perl, but they also include some automatic type conversions when dealing with nonnumeric values. If either of the operands for a multiplication operation isn’t a number, it is converted to a number behind the scenes using the Number() casting function. This means that an empty string is treated as 0, and the Boolean value of true is treated as 1.

Multiply

The multiply operator is represented by an asterisk (*) and is used, as one might suspect, to multiply two numbers. The syntax is the same as in C, as shown here:

var result = 34 * 56;

However, the multiply operator also has the following unique behaviors when dealing with special values:

  • If the operands are numbers, regular arithmetic multiplication is performed, meaning that two positives or two negatives equal a positive, whereas operands with different signs yield a negative. If the result cannot be represented by ECMAScript, either Infinity or –Infinity is returned.
  • If either operand is NaN, the result is NaN.
  • If Infinity is multiplied by 0, the result is NaN.
  • If Infinity is multiplied by any finite number other than 0, the result is either Infinity or –Infinity, depending on the sign of the second operand.
  • If Infinity is multiplied by Infinity, the result is Infinity.
  • If either operand isn’t a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Divide

The divide operator is represented by a slash (/) and divides the first operand by the second operand, as shown here:

var result = 66 / 11;

The divide operator, like the multiply operator, has special behaviors for special values. They are as follows:

  • If the operands are numbers, regular arithmetic division is performed, meaning that two positives or two negatives equal a positive, whereas operands with different signs yield a negative. If the result can’t be represented in ECMAScript, it returns either Infinity or –Infinity.
  • If either operand is NaN, the result is NaN.
  • If Infinity is divided by Infinity, the result is NaN.
  • If zero is divided by zero, the result is NaN.
  • If a nonzero finite number is divided by zero, the result is either Infinity or –Infinity, depending on the sign of the first operand.
  • If Infinity is divided by any number, the result is either Infinity or –Infinity, depending on the sign of the second operand.
  • If either operand isn’t a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Modulus

The modulus (remainder) operator is represented by a percent sign (%) and is used in the following way:

var result = 26 % 5;    //equal to 1

Just like the other multiplicative operators, the modulus operator behaves differently for special values, as follows:

  • If the operands are numbers, regular arithmetic division is performed, and the remainder of that division is returned.
  • If the dividend is an infinite number and the divisor is a finite number, the result is NaN.
  • If the dividend is a finite number and the divisor is 0, the result is NaN.
  • If Infinity is divided by Infinity, the result is NaN.
  • If the dividend is a finite number and the divisor is an infinite number, then the result is the dividend.
  • If the dividend is zero and the divisor is nonzero, the result is zero.
  • If either operand isn’t a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Additive Operators

The additive operators, add and subtract, are typically the simplest mathematical operators in programming languages. In ECMAScript, however, a number of special behaviors are associated with each operator. As with the multiplicative operators, conversions occur behind the scenes for different data types. For these operators, however, the rules aren’t as straightforward.

Add

The add operator (+) is used just as one would expect, as shown in the following example:

var result = 1 + 2;

If the two operands are numbers, they perform an arithmetic add and return the result according to the following rules:

  • If either operand is NaN, the result is NaN.
  • If Infinity is added to Infinity, the result is Infinity.
  • If –Infinity is added to –Infinity, the result is –Infinity.
  • If Infinity is added to –Infinity, the result is NaN.
  • If +0 is added to +0, the result is +0.
  • If –0 is added to +0, the result is +0.
  • If –0 is added to –0, the result is –0.

If, however, one of the operands is a string, then the following rules apply:

  • If both operands are strings, the second string is concatenated to the first.
  • If only one operand is a string, the other operand is converted to a string and the result is the concatenation of the two strings.

If either operand is an object, number, or Boolean, its toString() method is called to get a string value and then the previous rules regarding strings are applied. For undefined and null, the String() function is called to retrieve the values "undefined" and "null", respectively.

Consider the following:

image
var result1 = 5 + 5;     //two numbers
alert(result1);           //10
var result2 = 5 + "5";   //a number and a string
alert(result2);           //"55"

AddExample01.htm

This code illustrates the difference between the two modes for the add operator. Normally, 5 + 5 equals 10 (a number value), as illustrated by the first two lines of code. However, if one of the operands is changed to a string, "5", the result becomes "55" (which is a primitive string value), because the first operand gets converted to "5" as well.

One of the most common mistakes in ECMAScript is being unaware of the data types involved with an addition operation. Consider the following:

var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + num1 + num2;
alert(message);    //"The sum of 5 and 10 is 510"

AddExample02.htm

In this example, the message variable is filled with a string that is the result of two addition operations. One might expect the final string to be "The sum of 5 and 10 is 15"; however, it actually ends up as "The sum of 5 and 10 is 510". This happens because each addition is done separately. The first combines a string with a number (5), which results in a string. The second takes that result (a string) and adds a number (10), which also results in a string. To perform the arithmetic calculation and then append that to the string, just add some parentheses like this:

var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + (num1 + num2);
alert(message);    //"The sum of 5 and 10 is 15"

AddExample03.htm

Here, the two number variables are surrounded by parentheses, which instruct the interpreter to calculate its result before adding it to the string. The resulting string is "The sum of 5 and 10 is 15".

Subtract

The subtract operator (-) is another that is used quite frequently. Here’s an example:

var result = 2 - 1;

Just like the add operator, the subtract operator has special rules to deal with the variety of type conversions present in ECMAScript. They are as follows:

  • If the two operands are numbers, perform arithmetic subtract and return the result.
  • If either operand is NaN, the result is NaN.
  • If Infinity is subtracted from Infinity, the result is NaN.
  • If –Infinity is subtracted from –Infinity, the result is NaN.
  • If –Infinity is subtracted from Infinity, the result is Infinity.
  • If Infinity is subtracted from –Infinity, the result is –Infinity.
  • If +0 is subtracted from +0, the result is +0.
  • If –0 is subtracted from +0, the result is –0.
  • If –0 is subtracted from –0, the result is +0.
  • If either operand is a string, a Boolean, null, or undefined, it is converted to a number (using Number() behind the scenes) and the arithmetic is calculated using the previous rules. If that conversion results in NaN, then the result of the subtraction is NaN.
  • If either operand is an object, its valueOf() method is called to retrieve a numeric value to represent it. If that value is NaN, then the result of the subtraction is NaN. If the object doesn’t have valueOf() defined, then toString() is called and the resulting string is converted into a number.

The following are some examples of these behaviors:

image
var result1 = 5 - true;    //4 because true is converted to 1
var result2 = NaN - 1;     //NaN
var result3 = 5 - 3;       //2
var result4 = 5 - "";      //5 because "" is converted to 0
var result5 = 5 - "2";     //3 because "2" is converted to 2
var result6 = 5 - null;    //5 because null is converted to 0

SubtractExample01.htm

Relational Operators

The less-than (<), greater-than (>), less-than-or-equal-to (<=), and greater-than-or-equal-to (>=) relational operators perform comparisons between values in the same way that you learned in math class. Each of these operators returns a Boolean value, as in this example:

var result1 = 5 > 3;    //true
var result2 = 5 < 3;    //false

All snippets in this section can be found in RelationalOperatorsExample01.htm

As with other operators in ECMAScript, there are some conversions and other oddities that happen when using different data types. They are as follows:

  • If the operands are numbers, perform a numeric comparison.
  • If the operands are strings, compare the character codes of each corresponding character in the string.
  • If one operand is a number, convert the other operand to a number and perform a numeric comparison.
  • If an operand is an object, call valueOf() and use its result to perform the comparison according to the previous rules. If valueOf() is not available, call toString() and use that value according to the previous rules.
  • If an operand is a Boolean, convert it to a number and perform the comparison.

When a relational operator is used on two strings, an interesting behavior occurs. Many expect that less-than means “alphabetically before” and greater-than means “alphabetically after,” but this is not the case. For strings, each of the first string’s character codes is numerically compared against the character codes in a corresponding location in the second string. After this comparison is complete, a Boolean value is returned. The problem here is that the character codes of uppercase letters are all lower than the character codes of lowercase letters, meaning that you can run into situations like this:

var result = "Brick" < "alphabet";  //true

In this example, the string "Brick" is considered to be less than the string "alphabet", because the letter B has a character code of 66 and the letter a has a character code of 97. To force a true alphabetic result, you must convert both operands into a common case (upper or lower) and then compare like this:

var result = "Brick".toLowerCase() < "alphabet".toLowerCase();  //false

Converting both operands to lowercase ensures that "alphabet" is correctly identified as alphabetically before "Brick".

Another sticky situation occurs when comparing numbers that are strings, such as in this example:

var result = "23" < "3";  //true

This code returns true when comparing the string "23" to "3". Because both operands are strings, they are compared by their character codes (the character code for "2" is 50; the character code for "3" is 51). If, however, one of the operands is changed to a number as in the following example, the result makes more sense:

var result = "23" < 3;    //false

Here, the string "23" is converted into the number 23 and then compared to 3, giving the expected result. Whenever a number is compared to a string, the string is converted into a number and then numerically compared to the other number. This works well for cases like the previous example, but what if the string can’t be converted into a number? Consider this example:

var result = "a" < 3;    //false because "a" becomes NaN

The letter "a" can’t be meaningfully converted into a number, so it becomes NaN. As a rule, the result of any relational operation with NaN is false, which is interesting when considering the following:

var result1 = NaN < 3;    //false
var result2 = NaN >= 3;   //false

In most comparisons, if a value is not less than another, it is always greater than or equal to it. When using NaN, however, both comparisons return false.

Equality Operators

Determining whether two variables are equivalent is one of the most important operations in programming. This is fairly straightforward when dealing with strings, numbers, and Boolean values, but the task gets a little complicated when you take objects into account. Originally ECMAScript’s equal and not-equal operators performed conversions into like types before doing a comparison. The question of whether these conversions should, in fact, take place was then raised. The end result was for ECMAScript to provide two sets of operators: equal and not equal to perform conversion before comparison, and identically equal and not identically equal to perform comparison without conversion.

Equal and Not Equal

The equal operator in ECMAScript is the double equal sign (==), and it returns true if the operands are equal. The not-equal operator is the exclamation point followed by an equal sign (!=), and it returns true if two operands are not equal. Both operators do conversions to determine if two operands are equal (often called type coercion).

When performing conversions, the equal and not-equal operators follow these basic rules:

  • If an operand is a Boolean value, convert it into a numeric value before checking for equality. A value of false converts to 0, whereas a value of true converts to 1.
  • If one operand is a string and the other is a number, attempt to convert the string into a number before checking for equality.
  • If one of the operands is an object and the other is not, the valueOf() method is called on the object to retrieve a primitive value to compare according to the previous rules.

The operators also follow these rules when making comparisons:

  • Values of null and undefined are equal.
  • Values of null and undefined cannot be converted into any other values for equality checking.
  • If either operand is NaN, the equal operator returns false and the not-equal operator returns true. Important note: even if both operands are NaN, the equal operator returns false because, by rule, NaN is not equal to NaN.
  • If both operands are objects, then they are compared to see if they are the same object. If both operands point to the same object, then the equal operator returns true. Otherwise, the two are not equal.

The following table lists some special cases and their results:

EXPRESSION VALUE
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

EqualityOperatorsExample01.htm

Identically Equal and Not Identically Equal

The identically equal and not identically equal operators do the same thing as equal and not equal, except that they do not convert operands before testing for equality. The identically equal operator is represented by three equal signs (===) and returns true only if the operands are equal without conversion, as in this example:

image
var result1 = ("55" == 55);    //true - equal because of conversion
var result2 = ("55" === 55);   //false - not equal because different data types

EqualityOperatorsExample02.htm

In this code, the first comparison uses the equal operator to compare the string "55" and the number 55, which returns true. As mentioned previously, this happens because the string "55" is converted to the number 55 and then compared with the other number 55. The second comparison uses the identically equal operator to compare the string and the number without conversion, and of course, a string isn’t equal to a number, so this outputs false.

The not identically equal operator is represented by an exclamation point followed by two equal signs (!==) and returns true only if the operands are not equal without conversion. For example:

image
var result1 = ("55" != 55);    //false - equal because of conversion
var result2 = ("55" !== 55);   //true - not equal because different data types

EqualityOperatorsExample03.htm

Here, the first comparison uses the not-equal operator, which converts the string "55" to the number 55, making it equal to the second operand, also the number 55. Therefore, this evaluates to false because the two are considered equal. The second comparison uses the not identically equal operator. It helps to think of this operation as saying, “Is the string 55 different from the number 55?” The answer to this is yes (true).

Keep in mind that while null == undefined is true because they are similar values, null === undefined is false because they are not the same type.

image

Because of the type conversion issues with the equal and not-equal operators, it is recommended to use identically equal and not identically equal instead. This helps to maintain data type integrity throughout your code.

Conditional Operator

The conditional operator is one of the most versatile in ECMAScript, and it takes on the same form as in Java, which is as follows:

variable = boolean_expression ? true_value : false_value;

This basically allows a conditional assignment to a variable depending on the evaluation of the boolean_expression. If it’s true, then true_value is assigned to the variable; if it’s false, then false_value is assigned to the variable, as in this instance:

var max = (num1 > num2) ? num1 : num2;

In this example, max is to be assigned the number with the highest value. The expression states that if num1 is greater than num2, then num1 is assigned to max. If, however, the expression is false (meaning that num1 is less than or equal to num2), then num2 is assigned to max.

Assignment Operators

Simple assignment is done with the equal sign (=) and simply assigns the value on the right to the variable on the left, as shown in the following example:

var num = 10;

Compound assignment is done with one of the multiplicative, additive, or bitwise-shift operators followed by an equal sign (=). These assignments are designed as shorthand for such common situations as this:

var num = 10;
num = num + 10;

The second line of code can be replaced with a compound assignment:

var num = 10;
num += 10;

Compound-assignment operators exist for each of the major mathematical operations and a few others as well. They are as follows:

  • Multiply/assign (*=)
  • Divide/assign (/=)
  • Modulus/assign (%=)
  • Add/assign (+=)
  • Subtract/assign (-=)
  • Left shift/assign (<<=)
  • Signed right shift/assign (>>=)
  • Unsigned right shift/assign (>>>=)

These operators are designed specifically as shorthand ways of achieving operations. They do not represent any performance improvement.

Comma Operator

The comma operator allows execution of more than one operation in a single statement, as illustrated here:

var num1=1, num2=2, num3=3;

Most often, the comma operator is used in the declaration of variables; however, it can also be used to assign values. When used in this way, the comma operator always returns the last item in the expression, as in the following example:

var num = (5, 1, 4, 8, 0);  //num becomes 0

In this example, num is assigned the value of 0 because it is the last item in the expression. There aren’t many times when commas are used in this way; however, it is helpful to understand that this behavior exists.

STATEMENTS

ECMA-262 describes several statements (also called flow-control statements). Essentially, statements define most of the syntax of ECMAScript and typically use one or more keywords to accomplish a given task. Statements can be simple, such as telling a function to exit, or complicated, such as specifying a number of commands to be executed repeatedly.

The if Statement

One of the most frequently used statements in most programming languages is the if statement. The if statement has the following syntax:

if (condition) statement1 else statement2

The condition can be any expression; it doesn’t even have to evaluate to an actual Boolean value. ECMAScript automatically converts the result of the expression into a Boolean by calling the Boolean() casting function on it. If the condition evaluates to true, statement1 is executed; if the condition evaluates to false, statement2 is executed. Each of the statements can be either a single line or a code block (a group of code lines enclosed within braces). Consider this example:

image
if (i > 25)
    alert("Greater than 25.");    //one-line statement
else {
    alert("Less than or equal to 25.");  //block statement
}

IfStatementExample01.htm

It’s considered best coding practice to always use block statements, even if only one line of code is to be executed. Doing so can avoid confusion about what should be executed for each condition.

You can also chain if statements together like so:

if (condition1) statement1 else if (condition2) statement2 else statement3

Here’s an example:

if (i > 25) {
    alert("Greater than 25.");
} else if (i < 0) {
    alert("Less than 0.");
} else {
    alert("Between 0 and 25, inclusive.");
}

IfStatementExample02.htm

The do-while Statement

The do-while statement is a post-test loop, meaning that the escape condition is evaluated only after the code inside the loop has been executed. The body of the loop is always executed at least once before the expression is evaluated. Here’s the syntax:

do {
    statement
} while (expression);

And here’s an example of its usage:

image
var i = 0;
do {
   i += 2;
} while (i < 10);

DoWhileStatementExample01.htm

In this example, the loop continues as long as i is less than 10. The variable starts at 0 and is incremented by two each time through the loop.

image

Post-test loops such as this are most often used when the body of the loop should be executed at least once before exiting.

The while Statement

The while statement is a pretest loop. This means the escape condition is evaluated before the code inside the loop has been executed. Because of this, it is possible that the body of the loop is never executed. Here’s the syntax:

while(expression) statement

And here’s an example of its usage:

var i = 0;
while (i < 10) {
   i += 2;
}

WhileStatementExample01.htm

In this example, the variable i starts out equal to 0 and is incremented by two each time through the loop. As long as the variable is less than 10, the loop will continue.

The for Statement

The for statement is also a pretest loop with the added capabilities of variable initialization before entering the loop and defining postloop code to be executed. Here’s the syntax:

for (initialization; expression; post-loop-expression) statement

And here’s an example of its usage:

image
var count = 10;
for (var i=0; i < count; i++){
    alert(i);
}

ForStatementExample01.htm

This code defines a variable i that begins with the value 0. The for loop is entered only if the conditional expression (i < count) evaluates to true, making it possible that the body of the code might not be executed. If the body is executed, the postloop expression is also executed, iterating the variable i. This for loop is the same as the following:

var count = 10;
var i = 0;
while (i < count){
    alert(i);
    i++;
}

Nothing can be done with a for loop that can’t be done using a while loop. The for loop simply encapsulates the loop-related code into a single location.

It’s important to note that there’s no need to use the var keyword inside the for loop initialization. It can be done outside the initialization as well, such as the following:

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

ForStatementExample02.htm

This code has the same affect as having the declaration of the variable inside the loop initialization. There are no block-level variables in ECMAScript (discussed further in Chapter 4), so a variable defined inside the loop is accessible outside the loop as well. For example:

var count = 10;
for (var i=0; i < count; i++){
    alert(i);
}
alert(i);    //10

ForStatementExample03.htm

In this example, an alert displays the final value of the variable i after the loop has completed. This displays the number 10, because the variable i is still accessible even though it was defined inside the loop.

The initialization, control expression, and postloop expression are all optional. You can create an infinite loop by omitting all three, like this:

for (;;) {              //infinite loop
    doSomething();
}

Including only the control expression effectively turns a for loop into a while loop, as shown here:

image
var count = 10;
var i = 0;
for (; i < count; ){
    alert(i);
    i++;
}

ForStatementExample04.htm

This versatility makes the for statement one of the most used in the language.

The for-in Statement

The for-in statement is a strict iterative statement. It is used to enumerate the properties of an object. Here’s the syntax:

for (property in expression) statement

And here’s an example of its usage:

for (var propName in window) {
     document.write(propName);
}

ForInStatementExample01.htm

Here, the for-in statement is used to display all the properties of the BOM window object. Each time through the loop, the propName variable is filled with the name of a property that exists on the window object. This continues until all of the available properties have been enumerated over. As with the for statement, the var operator in the control statement is not necessary but is recommended for ensuring the use of a local variable.

Object properties in ECMAScript are unordered, so the order in which property names are returned in a for-in statement cannot necessarily be predicted. All enumerable properties will be returned once, but the order may differ across browsers.

Note that the for-in statement will throw an error if the variable representing the object to iterate over is null or undefined. ECMAScript 5 updates this behavior to not throw an error and simply doesn’t execute the body of the loop. For best cross-browser compatibility, it’s recommended to check that the object value isn’t null or undefined before attempting to use a for-in loop.

image

In versions of Safari earlier than 3, the for-in statement had a bug in which some properties were returned twice.

Labeled Statements

It is possible to label statements for later use with the following syntax:

label: statement

Here’s an example:

start: for (var i=0; i < count; i++) {
    alert(i);
}

In this example, the label start can be referenced later by using the break or continue statement. Labeled statements are typically used with nested loops.

The break and continue Statements

The break and continue statements provide stricter control over the execution of code in a loop. The break statement exits the loop immediately, forcing execution to continue with the next statement after the loop. The continue statement, on the other hand, exits the loop immediately, but execution continues from the top of the loop. Here’s an example:

image
var num = 0;
                   
for (var i=1; i < 10; i++) {
    if (i % 5 == 0) {
        break;
    }
    num++;
}
                   
alert(num);    //4

BreakStatementExample01.htm

In this code, the for loop increments the variable i from 1 to 10. In the body of loop, an if statement checks to see if the value of i is evenly divisible by 5 (using the modulus operator). If so, the break statement is executed and the loop is exited. The num variable starts out at 0 and indicates the number of times the loop has been executed. After the break statement has been hit, the next line of code to be executed is the alert, which displays 4. So the number of times the loop has been executed is four because when i equals 5, the break statement causes the loop to be exited before num can be incremented. A different effect can be seen if break is replaced with continue like this:

image
var num = 0;
                   
for (var i=1; i < 10; i++) {
    if (i % 5 == 0) {
        continue;
    }
    num++;
}
                   
alert(num);    //8

ContinueStatementExample01.htm

Here, the alert displays 8, the number of times the loop has been executed. When i reaches a value of 5, the loop is exited before num is incremented, but execution continues with the next iteration, when the value is 6. The loop then continues until its natural completion, when i is 10. The final value of num is 8 instead of 9, because one increment didn’t occur because of the continue statement.

Both the break and continue statements can be used in conjunction with labeled statements to return to a particular location in the code. This is typically used when there are loops inside of loops, as in the following example:

var num = 0;
                   
outermost:
for (var i=0; i < 10; i++) {
     for (var j=0; j < 10; j++) {
        if (i == 5 && j == 5) {
            break outermost;
        }
        num++;
    }
}
                   
alert(num);    //55

BreakStatementExample02.htm

In this example, the outermost label indicates the first for statement. Each loop normally executes 10 times, meaning that the num++ statement is normally executed 100 times and, consequently, num should be equal to 100 when the execution is complete. The break statement here is given one argument: the label to break to. Adding the label allows the break statement to break not just out of the inner for statement (using the variable j) but also out of the outer for statement (using the variable i). Because of this, num ends up with a value of 55, because execution is halted when both i and j are equal to 5. The continue statement can be used in the same way, as shown in the following example:

image
var num = 0;
                   
outermost:
for (var i=0; i < 10; i++) {
     for (var j=0; j < 10; j++) {
        if (i == 5 && j == 5) {
            continue outermost;
        }
        num++;
    }
}
                   
alert(num);    //95

ContinueStatementExample02.htm

In this case, the continue statement forces execution to continue — not in the inner loop but in the outer loop. When j is equal to 5, continue is executed, which means that the inner loop misses five iterations, leaving num equal to 95.

Using labeled statements in conjunction with break and continue can be very powerful but can cause debugging problems if overused. Always use descriptive labels and try not to nest more than a few loops.

The with Statement

The with statement sets the scope of the code within a particular object. The syntax is as follows:

with (expression) statement;

The with statement was created as a convenience for times when a single object was being coded to over and over again, such as in this example:

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

Here, the location object is used on every line. This code can be rewritten using the with statement as follows:

with(location){
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

WithStatementExample01.htm

In this rewritten version of the code, the with statement is used in conjunction with the location object. This means that each variable inside the statement is first considered to be a local variable. If it’s not found to be a local variable, the location object is searched to see if it has a property of the same name. If so, then the variable is evaluated as a property of location.

In strict mode, the with statement is not allowed and is considered a syntax error.

image

It is widely considered a poor practice to use the with statement in production code because of its negative performance impact and the difficulty in debugging code contained in the with statement.

The switch Statement

Closely related to the if statement is the switch statement, another flow-control statement adopted from other languages. The syntax for the switch statement in ECMAScript closely resembles the syntax in other C-based languages, as you can see here:

switch (expression)  {
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
  default: statement
}

Each case in a switch statement says, “If the expression is equal to the value, execute the statement.” The break keyword causes code execution to jump out of the switch statement. Without the break keyword, code execution falls through the original case into the following one. The default keyword indicates what is to be done if the expression does not evaluate to one of the cases. (In effect, it is an else statement.)

Essentially, the switch statement prevents a developer from having to write something like this:

if (i == 25){
  alert("25");
} else if (i == 35) {
  alert("35");
} else if (i == 45) {
  alert("45");
} else {
  alert("Other");
}

The equivalent switch statement is as follows:

image
switch (i) {
    case 25: 
        alert("25");
        break;
    case 35: 
        alert("35");
        break;
    case 45: 
        alert("45");
        break;
    default: 
        alert("Other");
}

SwitchStatementExample01.htm

It’s best to always put a break statement after each case to avoid having cases fall through into the next one. If you need a case statement to fall through, include a comment indicating that the omission of the break statement is intentional, such as this:

switch (i) {
    case 25: 
        /* falls through */
    case 35: 
        alert("25 or 35");
        break;
    case 45: 
        alert("45");
        break;
    default: 
        alert("Other");
}

SwitchStatementExample02.htm

Although the switch statement was borrowed from other languages, it has some unique characteristics in ECMAScript. First, the switch statement works with all data types (in many languages it works only with numbers), so it can be used with strings and even with objects. Second, the case values need not be constants; they can be variables and even expressions. Consider the following example:

switch ("hello world") {
    case "hello" + " world": 
        alert("Greeting was found.");
        break;
    case "goodbye": 
        alert("Closing was found.");
        break;
    default: 
        alert("Unexpected message was found.");
}

SwitchStatementExample03.htm

In this example, a string value is used in a switch statement. The first case is actually an expression that evaluates a string concatenation. Because the result of this concatenation is equal to the switch argument, the alert displays "Greeting was found." The ability to have case expressions also allows you to do things like this:

image
var num = 25;
switch (true) {
    case num < 0: 
        alert("Less than 0.");
        break;
    case num >= 0 && num <= 10: 
        alert("Between 0 and 10.");
        break;
    case num > 10 && num <= 20: 
        alert("Between 10 and 20.");
        break;
    default: 
        alert("More than 20.");
}

SwitchStatementExample04.htm

Here, a variable num is defined outside the switch statement. The expression passed into the switch statement is true, because each case is a conditional that will return a Boolean value. Each case is evaluated, in order, until a match is found or until the default statement is encountered (which is the case here).

image

The switch statement compares values using the identically equal operator, so no type coercion occurs (for example, the string "10" is not equal to the number 10).

FUNCTIONS

Functions are the core of any language, because they allow the encapsulation of statements that can be run anywhere and at any time. Functions in ECMAScript are declared using the function keyword, followed by a set of arguments and then the body of the function. The basic syntax is as follows:

function functionName(arg0, arg1,...,argN) {
    statements
}

Here’s an example:

function sayHi(name, message) {
    alert("Hello " + name + ", " + message);
}

FunctionExample01.htm

This function can then be called by using the function name, followed by the function arguments enclosed in parentheses (and separated by commas, if there are multiple arguments). The code to call the sayHi() function looks like this:

sayHi("Nicholas", "how are you today?");

The output of this function call is, "Hello Nicholas, how are you today?" The named arguments name and message are used as part of a string concatenation that is ultimately displayed in an alert.

Functions in ECMAScript need not specify whether they return a value. Any function can return a value at any time by using the return statement followed by the value to return. Consider this example:

image
function sum(num1, num2) {
    return num1 + num2;
}

FunctionExample02.htm

The sum() function adds two values together and returns the result. Note that aside from the return statement, there is no special declaration indicating that the function returns a value. This function can be called using the following:

var result = sum(5, 10);

Keep in mind that a function stops executing and exits immediately when it encounters the return statement. Therefore, any code that comes after a return statement will never be executed. For example:

function sum(num1, num2) {
    return num1 + num2;
    alert("Hello world");    //never executed
}

In this example, the alert will never be displayed because it appears after the return statement.

It’s also possible to have more than one return statement in a function, like this:

function diff(num1, num2) {
    if (num1 < num2) {
        return num2 - num1;
    } else {
        return num1 - num2;
    }
}

FunctionExample03.htm

Here, the diff() function determines the difference between two numbers. If the first number is less than the second, it subtracts the first from the second; otherwise it subtracts the second from the first. Each branch of the code has its own return statement that does the correct calculation.

The return statement can also be used without specifying a return value. When used in this way, the function stops executing immediately and returns undefined as its value. This is typically used in functions that don’t return a value to stop function execution early, as in the following example, where the alert won’t be displayed:

image
function sayHi(name, message) {
    return;
    alert("Hello " + name + ", " + message);    //never called
}

FunctionExample04.htm

image

It’s recommended that a function either always return a value or never return a value. Writing a function that sometimes returns a value causes confusion, especially during debugging.

Strict mode places several restrictions on functions:

  • No function can be named eval or arguments.
  • No named parameter can be named eval or arguments.
  • No two named parameters can have the same name.

If these occur, it’s considered a syntax error and the code will not execute.

Understanding Arguments

Function arguments in ECMAScript don’t behave in the same way as function arguments in most other languages. An ECMAScript function doesn’t care how many arguments are passed in, nor does it care about the data types of those arguments. Just because you define a function to accept two arguments doesn’t mean you can pass in only two arguments. You could pass in one or three or none, and the interpreter won’t complain. This happens because arguments in ECMAScript are represented as an array internally. The array is always passed to the function, but the function doesn’t care what (if anything) is in the array. If the array arrives with zero items, that’s fine; if it arrives with more, that’s okay too. In fact, there actually is an arguments object that can be accessed while inside a function to retrieve the values of each argument that was passed in.

The arguments object acts like an array (though it isn’t an instance of Array) in that you can access each argument using bracket notation (the first argument is arguments[0], the second is arguments[1], and so on) and determine how many arguments were passed in by using the length property. In the previous example, the sayHi() function’s first argument is named name. The same value can be accessed by referencing arguments[0]. Therefore, the function can be rewritten without naming the arguments explicitly, like this:

image
function sayHi() {
    alert("Hello " + arguments[0] + ", " + arguments[1]);
}

FunctionExample05.htm

In this rewritten version of the function, there are no named arguments. The name and message arguments have been removed, yet the function will behave appropriately. This illustrates an important point about functions in ECMAScript: named arguments are a convenience, not a necessity. Unlike in other languages, naming your arguments in ECMAScript does not create a function signature that must be matched later on; there is no validation against named arguments.

The arguments object can also be used to check the number of arguments passed into the function via the length property. The following example outputs the number of arguments passed into the function each time it is called:

function howManyArgs() {
    alert(arguments.length);
}
                   
howManyArgs("string", 45);    //2
howManyArgs();                //0
howManyArgs(12);              //1

FunctionExample06.htm

This example shows alerts displaying 2, 0, and 1 (in that order). In this way, developers have the freedom to let functions accept any number of arguments and behave appropriately. Consider the following:

function doAdd() {
    if(arguments.length == 1) {
        alert(arguments[0] + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + arguments[1]);
    }
}
                   
doAdd(10);        //20
doAdd(30, 20);    //50

FunctionExample07.htm

The function doAdd() adds 10 to a number only if there is one argument; if there are two arguments, they are simply added together and returned. So doAdd(10) returns 20, whereas doAdd(30,20) returns 50. It’s not quite as good as overloading, but it is a sufficient workaround for this ECMAScript limitation.

Another important thing to understand about arguments is that the arguments object can be used in conjunction with named arguments, such as the following:

image
function doAdd(num1, num2) {
    if(arguments.length == 1) {
        alert(num1 + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + num2);
    }
}

FunctionExample08.htm

In this rewrite of the doAdd() function, two-named arguments are used in conjunction with the arguments object. The named argument num1 holds the same value as arguments[0], so they can be used interchangeably (the same is true for num2 and arguments[1]).

Another interesting behavior of arguments is that its values always stay in sync with the values of the corresponding named parameters. For example:

function doAdd(num1, num2) {
    arguments[1] = 10;
    alert(arguments[0] + num2);
}

FunctionExample09.htm

This version of doAdd() always overwrites the second argument with a value of 10. Because values in the arguments object are automatically reflected by the corresponding named arguments, the change to arguments[1] also changes the value of num2, so both have a value of 10. This doesn’t mean that both access the same memory space, though; their memory spaces are separate but happen to be kept in sync. This effect goes only one way: changing the named argument does not result in a change to the corresponding value in arguments. Another thing to keep in mind: if only one argument is passed in, then setting arguments[1] to a value will not be reflected by the named argument. This is because the length of the arguments object is set based on the number of arguments passed in, not the number of named arguments listed for the function.

Any named argument that is not passed into the function is automatically assigned the value undefined. This is akin to defining a variable without initializing it. For example, if only one argument is passed into the doAdd() function, then num2 has a value of undefined.

Strict mode makes several changes to how the arguments object can be used. First, assignment, as in the previous example, no longer works. The value of num2 remains undefined even though arguments[1] has been assigned to 10. Second, trying to overwrite the value of arguments is a syntax error. (The code will not execute.)

image

All arguments in ECMAScript are passed by value. It is not possible to pass arguments by reference.

No Overloading

ECMAScript functions cannot be overloaded in the traditional sense. In other languages, such as Java, it is possible to write two definitions of a function so long as their signatures (the type and number of arguments accepted) are different. As just covered, functions in ECMAScript don’t have signatures, because the arguments are represented as an array containing zero or more values. Without function signatures, true overloading is not possible.

If two functions are defined to have the same name in ECMAScript, it is the last function that becomes the owner of that name. Consider the following example:

image
function addSomeNumber(num){
    return num + 100;
}
                   
function addSomeNumber(num) {
    return num + 200;
}
                   
var result = addSomeNumber(100);    //300

FunctionExample10.htm

Here, the function addSomeNumber() is defined twice. The first version of the function adds 100 to the argument, and the second adds 200. When the last line is called, it returns 300 because the second function has overwritten the first.

As mentioned previously, it’s possible to simulate overloading of methods by checking the type and number of arguments that have been passed into a function and then reacting accordingly.

SUMMARY

The core language features of JavaScript are defined in ECMA-262 as a pseudolanguage named ECMAScript. ECMAScript contains all of the basic syntax, operators, data types, and objects necessary to complete basic computing tasks, though it provides no way to get input or to produce output. Understanding ECMAScript and its intricacies is vital to a complete understanding of JavaScript as implemented in web browsers. The most widely implemented version of ECMAScript is the one defined in ECMA-262, third edition, though many are starting to implement the fifth edition. The following are some of the basic elements of ECMAScript:

  • The basic data types in ECMAScript are Undefined, Null, Boolean, Number, and String.
  • Unlike other languages, there’s no separate data type for integers versus floating-point values; the Number type represents all numbers.
  • There is also a complex data type, Object, that is the base type for all objects in the language.
  • A strict mode places restrictions on certain error-prone parts of the language.
  • ECMAScript provides a lot of the basic operators available in C and other C-like languages, including arithmetic operators, Boolean operators, relational operators, equality operators, and assignment operators.
  • The language features flow-control statements borrowed heavily from other languages, such as the if statement, the for statement, and the switch statement.

Functions in ECMAScript behave differently than functions in other languages:

  • There is no need to specify the return value of the function since any function can return any value at any time.
  • Functions that don’t specify a return value actually return the special value undefined.
  • There is no such thing as a function signature, because arguments are passed as an array containing zero or more values.
  • Any number of arguments can be passed into a function and are accessible through the arguments object.
  • Function overloading is not possible because of the lack of function signatures.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.241.250