Some simple JavaScript functionality is easy to accomplish in the
browser.Numerous articles on the Internet show you how to accomplish
what many term “stupid Web tricks” using JavaScript.These tricks
include how to pop up notices to the user,swap images,and create simple
games.Although these are all interesting pieces of functionality to add
to Web sites, copying and pasting code doesn’t provide an understanding
of why or how something works.This chapter aims to provide you with a
deeper knowledge base about how JavaScript works by examining its
core,ECMAScript.
ECMAScript provides JavaScript with syntax,operators,and basic objects necessary to complete common programming tasks.
Syntax
.
The sayHi() function doesn’t specify a return value, but it requires no special declaration (such as void is used in Java) to do so. Likewise, a function doesn’t need to explicitly declare a return value type if the function does indeed return a value. The function need only use the return operator followed by the value to return:
If a function doesn’t return a value,it can use the return operator without any parameters to exit a function at any time. Example:
No overloading
ECMAScript functions cannot be overloaded. This may come as a surprise, considering ECMAScript closely resembles other higher-level programming languages that support overloading. You can define two functions with the same name in the same scope without an error; however, the last function becomes the one that is used. Consider the following example:
The arguments object
Within a function’s code, a special object called arguments gives the developer access to the function’s arguments without specifically naming them. For example,in the sayHi() function,the first argument is given the name message.The same value can also be accessed by referencing arguments[0],which asks for the value of the first argument (the first argument is in position 0, the second is in position 1,and so on.). Therefore, the function can be rewritten without naming the argument explicitly:
By using the arguments object to determine the number of arguments passed into the function, it is possible to simulate the overloading of functions:
The Function class
Perhaps the most interesting aspect of ECMAScript is that functions are actually full-fledged objects. A Function class represents each and every function a developer defines. The syntax for creating a function using the Function class directly is as follows:
var function_name = new Function(argument1, argument2,..,argumentN, function_body);
In this form, each of the function arguments is one parameter, with the final parameter being the function body (the code to execute). Each of these parameters must be a string. Remember this function?
for them. Remember this example?
Because functions are reference types, they can also have properties and methods. The one property defined in ECMAScript is length, which indicates the number of arguments that a function expects. Example:
Function objects also have the standard valueOf() and toString() methods shared by all objects. Both of these methods return the source code for the function and are particularly useful in debugging. For example:
Closures
One of the most misunderstood aspects of ECMAScript is its support for closures. Closures are functions whose lexical representation includes variables that aren’t evaluated, meaning that functions are capable of using variables defined outside of the function itself. Using global variables in ECMAScript is a simple example of a closure. Consider the following example:
As you can see, closures are a very powerful, versatile part of ECMAScript that can be used to perform complex calculations. Just as when you use any advanced functionality, exercise caution when using closures because they can get extremely complex.
ECMAScript provides JavaScript with syntax,operators,and basic objects necessary to complete common programming tasks.
Syntax
Developers familiar with
languages such as Java,C,and Perl will find ECMAScript syntax easy to
pick up because it borrows syntax from each.Java and ECMAScript have
several key syntax features in common,as well as some that are
completely different.
The basic concepts of ECMAScript are the following:
Variables
The basic concepts of ECMAScript are the following:
- Everything is case-sensitive. Just as with Java, variables, function names, operators, and everything else is case-sensitive, meaning that a variable named test is different from one named Test.
- Variables are loosely typed.Unlike Java and C, variables in ECMAScript are not given a specific type.Instead, each variable is defined using the var operator and can be initialized with any value.This enables you to change the type of data a variable contains at any point in time (although you should avoid doing so whenever possible).Some examples:
- End-of-line semicolons are optional.Java, C, and Perl require that every line end with a semicolon (;) to be syntactically correct;ECMAScript allows the developer to decide whether or not to end a line with a semicolon.If the semicolon is not provided,ECMAScript considers the end of the line as the end of the statement (similar to Visual Basic and VBScript),provided that this doesn’t break the semantics of the code.Proper coding practice is to always include the semicolons because some browsers won’t run properly without them,but according to the letter of the ECMAScript standard,both of the following lines are proper syntax:
- Comments are the same as in Java, C, and Perl.ECMAScript borrowed its comments from these languages. There are two types of comments: single-line and multiline. The single-line comments begin with two forward-slashes (//), whereas multiline comments begin with a forward-slash and asterisk (/*) and end with an asterisk followed by a forward-slash (*/).
- Braces indicate code blocks.Another concept
borrowed from Java is the code block.Code blocks are used to indicate a
series of statements that should be executed in sequence and are
indicated by enclosing the statements between an opening brace ({) and a
closing brace (}).
For example:
var color = “red”; var num = 25; var visible = true;
var test1 = “red” var test2 = “blue”;
//this is a single-line comment /* this is a multiline comment */
if (test1 == “red”) { test1 = “blue”; alert(test1); }
Variables in ECMAScript are defined by using the var operator(short for variable),followed by the variable name,such as:
In this example, the variable test is declared and given an initialization value of “hi”(a string). Because ECMAScript is loosely typed, the interpreter automatically creates a string value for test without any explicit type declaration.You can also define two or more variables using the same var statement:
Unlike Java,variables in ECMAScript do not require initialization(they are actually initialized behind the scenes, which I discuss later). Therefore, this line of code is valid:
In terms of variables names, a name must follow two simple rules:
Pascal Notation —the first letter is uppercase and each appended word begins with an uppercase letter. For example:
Hungarian Type Notation — prepends a lowercase letter (or
sequence of lowercase letters) to the beginning of a Pascal Notation
variable name to indicate the type of the variable. For example, i
means integer and s means string in the following line:
Another interesting aspect of ECMAScript (and a major difference from most programming languages) is that variables don’t have to be declared before being used. For example:
When the ECMAScript interpreter sees an identifier that hasn’t been declared, it creates a global variable with the given name of the identifier and initializes it with the value specified. This is a handy feature of the language, but it can also be dangerous if you don’t keep track of variables closely. Best practice is always to declare all variables as you would with other programming languages.
Keywords
In this example, the variable test is declared and given an initialization value of “hi”(a string). Because ECMAScript is loosely typed, the interpreter automatically creates a string value for test without any explicit type declaration.You can also define two or more variables using the same var statement:
var test = “hi”,test2 = “hola”;The previous code defines the variable test to have a value of “hi” and the variable test2 to have a value of “hola”.Variables using the same var statement don’t have to be of the same type, however, as shown in the following:
var test = “hi”,age = 25;This example defines test (yet again) in addition to another variable named age that is set to the value of 25.Even though test and age are two different data types,this is perfectly legal in ECMAScript.
Unlike Java,variables in ECMAScript do not require initialization(they are actually initialized behind the scenes, which I discuss later). Therefore, this line of code is valid:
var test;Also unlike Java,variables can hold different types of values at different times;this is the advantage of loosely typed variables.A variable can be initialized with a string value,for instance, and later on be set to a number value,like this:
var test = “hi”; alert(test); //outputs “hi” //do something else here test = 55; alert(test); //outputs “55”This code outputs both the string and the number values without incident (or error). As mentioned previously, it is best coding practice for a variable to always contain a value of the same type throughout its use.
In terms of variables names, a name must follow two simple rules:
- The first character must be a letter, an underscore (_),or a dollar sign ($).
- All remaining characters may be underscores, dollar signs,or any alphanumeric characters.
var test; var $test; var $1; var _$te$t2;Of course, just because variable names are syntactically correct doesn’t mean you should use them.Variables should adhere to one of the well-known naming conventions:
- Camel Notation — the first letter is lowercase and each appended word begins with an uppercase letter. For example:
var my TestValue = 0,my Second Test Value = “hi”;
var MyTestValue = 0,MySecondTestValue = “hi”;
var iMyTestValue = 0,sMySecondTestValue = “hi”;
Another interesting aspect of ECMAScript (and a major difference from most programming languages) is that variables don’t have to be declared before being used. For example:
var sTest = “hello “; sTest2 = sTest + “world”; alert(sTest2); //outputs “hello world”In the previous code, sTest is declared with a string value of “hello”. The next line uses a variable named sTest2 to create a concatenation of sTest and the string “world”. The variable sTest2 hasn’t been defined using the var operator; it has just been inserted as if it has already been declared.
When the ECMAScript interpreter sees an identifier that hasn’t been declared, it creates a global variable with the given name of the identifier and initializes it with the value specified. This is a handy feature of the language, but it can also be dangerous if you don’t keep track of variables closely. Best practice is always to declare all variables as you would with other programming languages.
ECMA-262 describes a set of keywordsthat
ECMAScript supports. These keywords indicate beginnings and/or endings
of ECMAScript statements. By rule, keywords are reserved and cannot be
used as variable or function names. Here is the complete list of
ECMAScript keywords:
Break else new var
Case finally Return void
Catch for switch while
continue function this with
default if throw
delete in try
do instance of type of
If you use a keyword as a variable or function name, you will probably be greeted with an error message like this: “Identifier expected.”
Reserved Words
Break else new var
Case finally Return void
Catch for switch while
continue function this with
default if throw
delete in try
do instance of type of
If you use a keyword as a variable or function name, you will probably be greeted with an error message like this: “Identifier expected.”
ECMAScript also defines a number of reserved words.
The reserved words are, in a sense, words that are reserved for future
use as keywords.Because of this,reserved words cannot be used as
variable or function names.The complete list of reserved words in
ECMA-262 Edition 3 is as follows:
Abstract enum int s hort
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
If you use a reserved word as a variable or function name, more than likely you will not receive an error...until a future browser implements one of them.Then the word will be considered a keyword, and you will get a keyword error.
Primitive and Reference Values
Abstract enum int s hort
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
If you use a reserved word as a variable or function name, more than likely you will not receive an error...until a future browser implements one of them.Then the word will be considered a keyword, and you will get a keyword error.
In ECMAScript, a variable can hold one of two types of values:primitive values and reference values.
If you use a reserved word as a variable or function name, more than likely you will not receive an error...until a future browser implements one of them.Then the word will be considered a keyword, and you will get a keyword error.
Primitive Types
- Primitive values are simple pieces of data that are stored on the stack, which is to say that their value is stored directly in the location that the variable accesses.
- Reference values,on the other hand, are objects that are stored in the heap, meaning that the value stored in the variable location is a pointer to a location in memory where the object is stored.
- In image laguages, strings are considered as reference type and not a primitive type because a string can vary in length. ECMAScript breaks from this tradition
If you use a reserved word as a variable or function name, more than likely you will not receive an error...until a future browser implements one of them.Then the word will be considered a keyword, and you will get a keyword error.
As mentioned previously,ECMAScript has five primitive types: Undefined, Null, Boolean, Number, and String. ECMA-262 defines the term type as
a set of values, and each of the primitive types defines a range of
values it can contain as well as literal representations of that
type.To determine if a value is in the range of values for a particular
type, ECMAScript provides the typeof operator.This operator can be
used to determine if a value represents a primitive type and if so,
which primitive type it represents.
The typeof operator
The typeof operator takes one parameter: the variable or value to check.For example:
As previously mentioned, the Undefined type has only one value, undefined. When a variable is declared and not initialized, it is given the value of undefined by default.
var oTemp;
The previous line of code declares a variable named oTemp, which has no initialization value. This variable is given a value of undefined, which is the literal representation of the Undefined type.You can test that the variable is equal to the literal yourself by running this code snippet:
Another type with just one value,the Null type, has only the special value null,which is also its literal.The value undefined is actually a derivative of the value null,so ECMAScript defines them as equal to each other.
The Boolean type
The Boolean type is one of the most frequently used in the language.It has two values, true and false (which are also the two Boolean literals). Even though false isn’t equal to 0, 0 is converted to false when necessary, making it safe to use either in a Boolean statement.
The most unique type defined in ECMA-262 is the Number type. The Number type can represent both 32-bit integer and 64-bit floating-point values. A Number type literal is considered any number entered directly (not accessed from another variable). For example, the following line of code declares a variable to hold an integer value, which is defined by the literal 55:
For very large or very small numbers, floating-point values can be represented using e-notation. In e-notation, a number is represented by digits (including decimal digits), followed by an e (or an E), followed by the number of times to multiply it by 10. Confused? Here’s an example:
E-notation can also be used to represent very small numbers, such as 0.00000000000000003, which can be written as 3e-17 (here, 10 is raised to the –17 power, meaning that you will actually be dividing by 10 17 times). ECMAScript, by default, converts any floating-point number with six or more leading zeros into e-notation.
A few special values are also defined as part of the Number type. The first two are Number.MAX_VALUE and Number.MIN_VALUE,which define the outer bounds of the Number value set. All ECMAScript numbers must fall between these two values, without exception.A calculation can,however, result in a number that does not fall in between these two numbers.
When a calculation results in a number greater than Number.MAX_VALUE,it is assigned a value of Number.POSITIVE_INFINITY, meaning that it has no numeric value anymore.Likewise a calculation that results in a number less than Number.MIN_VALUE is assigned a value of Number.NEGATIVE_ INFINITY,which also has no numeric value.If a calculation returns an infinite value, the result cannot be used in any further calculations.
Because an infinite number can be positive or negative, a method can be used to determine if a number is finite (instead of testing for each infinite number separately).The isFinite() method can be called on any number to ensure that the number isn’t infinite.For example:
The String type is unique in that it is the only primitive type that doesn’t have a definite size. A string can be used to store zero or more Unicode characters, represented by 16-bit integers (Unicode is an international character set)
Each character in a string is given a position, starting with the first character in position 0, the second character in position 1, and so on. This means that the position of the final character in a string is always the length of the string minus 1.
String literals are specified by using either double quotes (“) or single quotes (‘). This differs from Java, where double quotes are used to specify strings and single quotes are used to specify characters. However, because ECMAScript has no character type, it is permissible to use either notation. For example, the following two lines are valid:
Conversions
The typeof operator
The typeof operator takes one parameter: the variable or value to check.For example:
var sTemp = “test string”; alert(typeof sTemp); //outputs “string” alert(typeof 95); //outputs “number”Calling typeof on a variable or value returns one of the following values:
- “undefined” if the variable is of the Undefined type.
- “boolean” if the variable is of the Boolean type.
- “number” if the variable is of the Number type.
- “string” if the variable is of the String type.
- “object” if the variable is of a reference type or of the Null type.
As previously mentioned, the Undefined type has only one value, undefined. When a variable is declared and not initialized, it is given the value of undefined by default.
var oTemp;
The previous line of code declares a variable named oTemp, which has no initialization value. This variable is given a value of undefined, which is the literal representation of the Undefined type.You can test that the variable is equal to the literal yourself by running this code snippet:
var oTemp; alert(oTemp == undefined);This code displays an alert with the word “true”,indicating that these two values are indeed equal.You can also use the typeof operator to show that the variable has a value of undefined.
var oTemp; alert(typeof oTemp); //outputs “undefined”Note that a variable having the value of undefined is different from a value being undefined.However,the typeof operator doesn’t actually distinguish between the two. Consider the following:
var oTemp; //makesure this variable isn’t defined //var oTemp2; //try outputting alert(typeof oTemp); //outputs “undefined” alert(typeof oTemp2); //outputs “undefined”The previous code outputs “undefined” for both variables, even though only one of them (oTemp2) is undefined.If you try to use oTemp2 with any operator other than typeof, it causes an error because operators can only be applied to defined variables. For example, this causes an error:
//make sure this variable isn’t defined //var oTemp2; //try outputting alert(oTemp2 == undefined); //causes errorThe value undefined is also returned when a function doesn’t explicitly return a value, as in the following:
function testFunc() { //leave the function blank } alert(testFunc() == undefined); //outputs “true”The Null type
Another type with just one value,the Null type, has only the special value null,which is also its literal.The value undefined is actually a derivative of the value null,so ECMAScript defines them as equal to each other.
alert(null == undefined); //outputs “true”Even though the values are both true, they are considered to have different meanings. Whereas undefined is the value assigned when a variable is declared and not initialized, null is the value used to represent an object that doesn’t exist (which I touched upon briefly in the discussion of the typeof operator).If a function or method is supposed to return an object, it usually returns null when the object isn’t found.
The Boolean type
The Boolean type is one of the most frequently used in the language.It has two values, true and false (which are also the two Boolean literals). Even though false isn’t equal to 0, 0 is converted to false when necessary, making it safe to use either in a Boolean statement.
var bFound = true; var bLost = false;The Number type
The most unique type defined in ECMA-262 is the Number type. The Number type can represent both 32-bit integer and 64-bit floating-point values. A Number type literal is considered any number entered directly (not accessed from another variable). For example, the following line of code declares a variable to hold an integer value, which is defined by the literal 55:
var iNum = 55;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), and the following digits can be any octal digit (0 through 7), as in this line of code:
var iNum = 070; //070 is equal to 56 in decimalTo create a hexadecimal literal, the first digit must be a zero (0) followed by the letter x, followed by any number of hexadecimal digits (0-9 and A-F). The digits may be in uppercase or lowercase. For example:
var iNum = 0x1f; //0x1f is equal to 31 in decimal var iNum2 = 0xAB; //0xAB is equal to 171 in decimalTo define a floating-point value, you must include a decimal point and one digit after the decimal point (for instance, use 1.0 not 1.). This is considered a floating-point number literal. Example:
var fNum = 5.0;The interesting thing about this form of floating-point literal is that it is actually stored as a string until it’s needed for calculation.
For very large or very small numbers, floating-point values can be represented using e-notation. In e-notation, a number is represented by digits (including decimal digits), followed by an e (or an E), followed by the number of times to multiply it by 10. Confused? Here’s an example:
var fNum = 3.125e7;This notation represents the number 31250000. You can get this value by converting the e-notation to a calculation: 3.125 107, which is exactly equal to 3.125 10 10 10 10 10 10 10.
E-notation can also be used to represent very small numbers, such as 0.00000000000000003, which can be written as 3e-17 (here, 10 is raised to the –17 power, meaning that you will actually be dividing by 10 17 times). ECMAScript, by default, converts any floating-point number with six or more leading zeros into e-notation.
A few special values are also defined as part of the Number type. The first two are Number.MAX_VALUE and Number.MIN_VALUE,which define the outer bounds of the Number value set. All ECMAScript numbers must fall between these two values, without exception.A calculation can,however, result in a number that does not fall in between these two numbers.
When a calculation results in a number greater than Number.MAX_VALUE,it is assigned a value of Number.POSITIVE_INFINITY, meaning that it has no numeric value anymore.Likewise a calculation that results in a number less than Number.MIN_VALUE is assigned a value of Number.NEGATIVE_ INFINITY,which also has no numeric value.If a calculation returns an infinite value, the result cannot be used in any further calculations.
Because an infinite number can be positive or negative, a method can be used to determine if a number is finite (instead of testing for each infinite number separately).The isFinite() method can be called on any number to ensure that the number isn’t infinite.For example:
var iResult = iNum* some_really_large_number; if (isFinite(iResult)) { alert(“Number is finite.”); } else { alert(“Number is infinite.”); }The final special number value is NaN,which stands for Not a Number. NaN is an odd special value. In general, this occurs when conversion from another type (String, Boolean, and so on) fails. For example, trying to convert the word blue into a number value will fail because there is no numeric equivalent. Just like the infinity values, NaN cannot be used in mathematical calculations. Another oddity of NaN is that it is not equal to itself, meaning that the following will return false:
alert(NaN == NaN); //outputs “false”For this reason, it is not recommended to use the NaN value itself. Instead, the function isNaN() will do the job quite nicely:
alert(isNaN(“blue”)); //outputs “true” alert(isNaN(“123”)); //outputs “false”The String type
The String type is unique in that it is the only primitive type that doesn’t have a definite size. A string can be used to store zero or more Unicode characters, represented by 16-bit integers (Unicode is an international character set)
Each character in a string is given a position, starting with the first character in position 0, the second character in position 1, and so on. This means that the position of the final character in a string is always the length of the string minus 1.
String literals are specified by using either double quotes (“) or single quotes (‘). This differs from Java, where double quotes are used to specify strings and single quotes are used to specify characters. However, because ECMAScript has no character type, it is permissible to use either notation. For example, the following two lines are valid:
var sColor1 = “blue”; var sColor2 = ‘blue’;The string type also encompasses several character literals, which should be very familiar to Java, C, and Perl developers.
One of the most important
features of any programming language is the capability to convert
between types, and ECMAScript provides developers with a number of easy
conversion routines. Most types contain methods that provide for simple
conversion, and several global methods are available for more complex
conversion. In either case, type conversion is a short,one-step process
in ECMAScript.
Converting to a string
The interesting thing about ECMAScript primitive values for Booleans, numbers,and strings is that they are pseudo-objects, meaning that they actually have properties and methods. For example, to get the length of a string,you can do the following:
The Boolean toString() method simply outputs the string “true” or “false”,depending on the value of the variable:
Converting to a number
ECMAScript provides two methods for converting non-number primitives into numbers: parseInt() and parseFloat().As you may have guessed, the former converts a value into an integer whereas the latter converts a value into a floating-point number.These methods only work properly when called on strings; all other types return NaN.
Both parseInt() and parseFloat() look at a string carefully before deciding what its numeric value should be. The parseInt() method starts with the character in position 0 and determines if this is a valid number; if it isn’t, the method returns NaN and doesn’t continue. If, however, the number is valid, the method goes on to the character in position 1 and does the same test. This process continues until a character isn’t a valid number, at which point parseInt() takes the string (up to that point) and converts it into a number. For example, if you want to convert the string “1234blue” to an integer, parseInt() would return a value of 1234 because it stops processing one it reaches the character b. Any number literal contained in a string is also converted correctly, so the string “0xA” is properly converted into the number 10. However, the string “22.5” will be converted to 22, because the decimal point is an invalid character for an integer. Some examples:
Of course, this can also be done for binary, octal, and even decimal (which is the default mode):
The parseFloat() method works in a similar way to parseInt(),looking at each character starting in position 0.It also continues until the first invalid character and then converts the string it has seen up to that point. For this method, however, the decimal point is a valid character the first time it appears. If two decimal points are present, the second is considered invalid and the parseFloat() method converts the string up until that position. This means that the string “22.34.5” will be parsed into 22.34.
Another difference when using parseFloat() is that the string must represent a floating-point number in decimal form, not octal or hexadecimal.This method ignores leading zeros, so the octal number 0908 will be parsed into 908, and the hexadecimal number 0xA will return NaN because x isn’t a valid character for a floating-point number.There is also no radix mode for parseFloat().
Some examples of using parseFloat():
It’s also possible to convert values using a process called type casting. Type casting allows you to access a specific value as if it were of a different type. Three type casts are available in ECMAScript:
The Boolean() type cast returns true when the value is a string with at least one character, a number other than 0, or an object (discussed in the next section); it returns false when the value is an empty string, the number 0, undefined, or null. The following code snippet can be used to test type casting as a Boolean:
The last type cast, String(),is the simplest because it can accurately convert any value to a string value.To execute the type cast, it simply calls the toString() method of the value that was passed in, which converts 1 to “1”, true to “true”, false to “false”, and so on. The only difference between type casting as a string and using toString() is that the type cast can produce a string for a null or undefined value without error:
Reference Types
Converting to a string
The interesting thing about ECMAScript primitive values for Booleans, numbers,and strings is that they are pseudo-objects, meaning that they actually have properties and methods. For example, to get the length of a string,you can do the following:
var sColor = “blue”; alert(sColor.length); //outputs “4”Even though the value “blue” is a primitive string,it still has a length property holding the size of the string. To that end, the three main primitive values,Booleans, numbers, and strings, all have a toString() method to convert their value to a string.
The Boolean toString() method simply outputs the string “true” or “false”,depending on the value of the variable:
var bFound = false; alert(bFound.toString()); //outputs “false”The Number toString() method is unique in that it has two modes: default and radix mode. In default mode,the toString() method simply outputs the numeric value in an appropriate string (whether that is integer, floating point, or e-notation),like this:
var iNum1 = 10; var fNum2 = 10.0; alert(iNum1.toString()); //outputs “10” alert(fNum2.toString()); //outputs “10”When you use the Number’s toString() method in radix mode, it is possible to output the number using a different base, such as 2 for binary, 8 for octal, or 16 for hexadecimal. The radix is just a fancy name for the base to convert to, and it is specified as an argument to the toString() method:
var iNum = 10; alert(iNum1.toString(2)); //outputs “1010” alert(iNum1.toString(8)); //outputs “12” alert(iNum1.toString(16)); //outputs “A”In the previous example, the number 10 is output in three different ways: binary, octal, and hexadecimal.This functionality can be very useful for dealing with numbers in HTML,which use hexadecimal representations for each color.
Converting to a number
ECMAScript provides two methods for converting non-number primitives into numbers: parseInt() and parseFloat().As you may have guessed, the former converts a value into an integer whereas the latter converts a value into a floating-point number.These methods only work properly when called on strings; all other types return NaN.
Both parseInt() and parseFloat() look at a string carefully before deciding what its numeric value should be. The parseInt() method starts with the character in position 0 and determines if this is a valid number; if it isn’t, the method returns NaN and doesn’t continue. If, however, the number is valid, the method goes on to the character in position 1 and does the same test. This process continues until a character isn’t a valid number, at which point parseInt() takes the string (up to that point) and converts it into a number. For example, if you want to convert the string “1234blue” to an integer, parseInt() would return a value of 1234 because it stops processing one it reaches the character b. Any number literal contained in a string is also converted correctly, so the string “0xA” is properly converted into the number 10. However, the string “22.5” will be converted to 22, because the decimal point is an invalid character for an integer. Some examples:
var iNum1 = parseInt(“1234blue”); //returns 1234 var iNum2 = parseInt(“0xA”); //returns 10 var iNum3 = parseInt(“22.5”); //returns 22 var iNum4 = parseInt(“blue”); //returns NaNThe parseInt() method also has a radix mode,allowing you to convert strings in binary, octal,hexadecimal, or any other base into an integer. The radix is specified as a second argument to parseInt(),so a call to parse a hexadecimal value looks like this: var iNum1 = parseInt(“AF”, 16); //returns 175
Of course, this can also be done for binary, octal, and even decimal (which is the default mode):
var iNum1 = parseInt(“10”, 2); //returns 2 var iNum2 = parseInt(“10”, 8); //returns 8 var iNum2 = parseInt(“10”, 10); //returns 10If decimal numbers contain a leading zero,it’s always best to specify the radix as 10 so that you won’t accidentally end up with an octal value.For example:
var iNum1 = parseInt(“010”); //returns 8 var iNum2 = parseInt(“010”, 8); //returns 8 var iNum3 = parseInt(“010”, 10); //returns 10In this code, both lines are parsing the string “010” into a number.The first line thinks that the string is an octal value and parses it the same way as the second line (which specifies the radix as 8).The last line specifies a radix of 10, so iNum3 ends up equal to 10.
The parseFloat() method works in a similar way to parseInt(),looking at each character starting in position 0.It also continues until the first invalid character and then converts the string it has seen up to that point. For this method, however, the decimal point is a valid character the first time it appears. If two decimal points are present, the second is considered invalid and the parseFloat() method converts the string up until that position. This means that the string “22.34.5” will be parsed into 22.34.
Another difference when using parseFloat() is that the string must represent a floating-point number in decimal form, not octal or hexadecimal.This method ignores leading zeros, so the octal number 0908 will be parsed into 908, and the hexadecimal number 0xA will return NaN because x isn’t a valid character for a floating-point number.There is also no radix mode for parseFloat().
Some examples of using parseFloat():
var fNum1 = parseFloat(“1234blue”); //returns 1234.0 var fNum2 = parseFloat(“0xA”); //returns NaN var fNum3 = parseFloat(“22.5”); //returns 22.5 var fNum4 = parseFloat(“22.34.5”); //returns 22.34 var fNum5 = parseFloat(“0908”); //returns 908 var fNum6 = parseFloat(“blue”); //returns NaNType Casting
It’s also possible to convert values using a process called type casting. Type casting allows you to access a specific value as if it were of a different type. Three type casts are available in ECMAScript:
- Boolean(value) – casts the given value as a Boolean
- Number(value) – casts the given value as a number (either integer or floating-point)
- String(value) – casts the given value a string
The Boolean() type cast returns true when the value is a string with at least one character, a number other than 0, or an object (discussed in the next section); it returns false when the value is an empty string, the number 0, undefined, or null. The following code snippet can be used to test type casting as a Boolean:
var b1 = Boolean(“”); //false – empty string var b2 = Boolean(“hi”); //true – non-empty string var b3 = Boolean(100); //true – non-zero number var b4 = Boolean(null); //false - null var b5 = Boolean(0); //false - zero var b6 = Boolean(new Object()); //true – objectThe Number() type cast works in a manner similar to parseInt() and parseFloat(),except that it converts the entire value, not just part of it.Remember that parseInt() and parseFloat() only convert up to the first invalid character (in strings), so “4.5.6” becomes “4.5”.Using the Number() type cast, “4.5.6” becomes NaN because the entire string value cannot be converted into a number. If a string value can be converted entirely, Number() decides whether to use parseInt() or parseFloat(). The following table illustrates what happens when Number() is used on various values:
The last type cast, String(),is the simplest because it can accurately convert any value to a string value.To execute the type cast, it simply calls the toString() method of the value that was passed in, which converts 1 to “1”, true to “true”, false to “false”, and so on. The only difference between type casting as a string and using toString() is that the type cast can produce a string for a null or undefined value without error:
var s1 = String(null); //”null” var oNull = null; var s2 = oNull.toString(); //won’t work, causes an errorType casting is very helpful when dealing with the loosely typed nature of ECMAScript, although you should ensure that only proper values are used.
Reference types are commonly referred to as classes,which is to say that when you have a reference value,you are dealing with an object.
Objects are created by using the new operator and providing the name of the class to instantiate.For example,this line creates an instance of the Object class:
The Object class itself isn’t very useful, but you should understand it before moving on to the other classes.Why is that? Because the Object class in ECMAScript is similar to java.lang.Object in Java:It is the base class from which all ECMAScript classes inherit. All the properties and methods of the Object class are also present in the other classes, and so to understand the Object class is to understand all the others better.
The Object class has the following properties:
The Boolean class
The Boolean class is the reference type for the Boolean primitive type.To create a Boolean object,you need only pass in a Boolean value as a parameter:
The problem typically occurs when trying to use Boolean objects in Boolean expressions. For example:
The Number class
As you might have assumed, the Number class is the reference type for the Number primitive type. To create a Number object, do the following:
Another method related to formatting numbers is the toExponential() method, which returns a string with the number formatted in e-notation. Just as with toFixed(),toExponential() accepts one argument, which is the number of decimal places to output. For example:
The toPrecision() method returns either the fixed or exponential representation of a number, depending on which makes the most sense. This method takes one argument, which is the total number of digits to use to represent the number (not including exponents). Example:
The toFixed(), toExponential(), and toPrecision() methods round up or down to accurately represent a number with the correct number of decimal places.
The String class
The String class is the object representation of a String primitive and is created in the following manner:
The String class is one of the more complicated reference types in ECMAScript. As such, this section focuses only on the basic functionality of the String class. More advanced functionality is split into suitable topics throughout the book.
The String class has one property, length, which gives the number of characters in the string:
The String class also has a large number of methods. The first two, charAt() and charCodeAt(),have to do with accessing the individual characters in the string. As described in the section on String primitives, the first character is in position 0, the second is in position 1,and so on. Both these methods accept one argument, the position of the character to act on. The charAt() method returns a string containing the character in that position:
Next up is the concat() method, which is used to concatenate one or more strings to the primitive value of the String object.This method actually returns a String primitive value as a result and leaves the original String object intact:
Both the indexOf() and lastIndexOf() methods return the position of a given substring within another string (or –1 if the substring isn’t found).The difference between the two is that the indexOf() method begins looking for the substring at the beginning of the string (character 0) whereas the lastIndexOf() method begins looking for the substring at the end of the string. For example:
The next method is localeCompare(), which helps sort string values. This method takes one argument, the string to compare to, and it returns one of three values:
ECMAScript provides two methods for creating string values from a substring: slice() and substring().
Both methods return a substring of the string they act on, and both accept either one or two arguments. The first argument is the position where capture of the substring begins; the second argument, if used,is the position before which capture is stopped (which is to say that the character at this position is not included in the returned value).If the second argument is omitted, it is assumed that the ending position is the length of the string. Just as with the concat() method, slice() and substring() do not alter the value of the String object itself: They simply return a primitive String value as the result,leaving the String object unchanged.
For the slice() method,a negative argument is treated as the length of the string plus the negative argument;the substring() method treats a negative argument as 0 (which means that it is ignored).
For example:
The last set of methods to be discussed involves case conversion.Four methods perform case conversion:
The instanceof operator
One of the problems with using reference types to store values has been the use of the typeof operator, which returns “object” no matter what type of object is being referenced. To provide a solution, ECMAScript introduced another Java operator: instanceof.
The instanceof operator works in a similar way to the typeof operator:It identifies the type of object you are working with. Unlike typeof, instanceof requires the developer to explicitly ask if an object is of a particular type. For example:
Operators
Objects are created by using the new operator and providing the name of the class to instantiate.For example,this line creates an instance of the Object class:
var o = new Object();This syntax is similar to Java,although ECMAScript requires parentheses to be used only if there are one or more parameters.If there are no parameters,such as in the previous line of code,then the parentheses can be safely omitted:
var o = new Object;The Object class
The Object class itself isn’t very useful, but you should understand it before moving on to the other classes.Why is that? Because the Object class in ECMAScript is similar to java.lang.Object in Java:It is the base class from which all ECMAScript classes inherit. All the properties and methods of the Object class are also present in the other classes, and so to understand the Object class is to understand all the others better.
The Object class has the following properties:
- constructor — A reference value (pointer) to the function that created the object. For the Object class, this points to the native Object() function.
- prototype — A reference value to the object prototype for this object. Prototypes are discussed further in Chapter 3. For the all classes, this returns an instance of Object by default.
- hasOwnProperty(property) — Determines if a given property exists for the object. The property must be specified as a string (for example, o.hasOwnProperty(“name”)).
- isPrototypeOf(object) — Determines if the object is a prototype of another object.
- propertyIsEnumerable(property) — Determines if a given property can be enumerated by using the for...in statement (discussed later in this chapter).
- toString() — Returns a primitive string representation of the object. For the Object class, this value is undefined in ECMA-262 and, as such, differs in each implementation.
- valueOf() — Returns the most appropriate primitive value of this object. For many classes, this returns the same value as toString().
The Boolean class
The Boolean class is the reference type for the Boolean primitive type.To create a Boolean object,you need only pass in a Boolean value as a parameter:
var oBooleanaobject = new Boolean(true);Boolean objects override the valueOf() method of the Object class to return a primitive value of either true or false; the toString() method is also overridden to return a string of “true” or “false” when called. Unfortunately,not only are Boolean objects of little use in ECMAScript,they can actually be rather confusing.
The problem typically occurs when trying to use Boolean objects in Boolean expressions. For example:
var oFalseObject = new Boolean(false); var bResult = oFalseObject && true; //outputs trueIn this code,a Boolean object is created with a value of false.That same object is then ANDed with the primitive value true. In Boolean math, false AND true is equal to false. However,in this line of code it is the oFalseObject being evaluated,not its value (false). As discussed earlier, all objects are automatically converted to true in Boolean expressions,so oFalseObject actually is given a value of true in the expression. Then, true ANDed with true is equal to true.
The Number class
As you might have assumed, the Number class is the reference type for the Number primitive type. To create a Number object, do the following:
var oNumberObject = new Number(55);You may recognize the Number class from earlier in this chapter,where the special number values are discussed (such as Number. MAX_VALUE). All the special values are static properties of the Number class.To get the Number primitive value for a number object, simply use the valueOf() method:
var iNumber =oNumberObject.valueOf();Of course, the Number class also has a toString() method,which was discussed at length in the section on conversions. Aside from the standard methods inherited from the Object class, the Number class has several methods specifically for working with number values.The toFixed() method returns a string representation of a number with a specified number of decimal points.For example:
var oNumberObject = new Number(99); alert(oNumberObject.toFixed(2));//outputs “99.00”Here, the toFixed() method is given an argument of 2, which indicates how many decimal places should be displayed. As a result, the method returns the string “99.00”, filling out the empty decimal places with 0s. This method can be very useful for applications dealing with currency. The toFixed() method can represent numbers with 0 to 20 decimal places; other values may cause errors.
Another method related to formatting numbers is the toExponential() method, which returns a string with the number formatted in e-notation. Just as with toFixed(),toExponential() accepts one argument, which is the number of decimal places to output. For example:
var oNumberObject = new Number(99); alert(oNumberObject.toExponential(1));//outputs “9.9e+1”This code outputs “9.9e+1” as the result, which you may remember from the earlier explanation, represents 9.9 x 101. The question is, what if you don’t know the proper format to use for a number: fixed or exponential? That’s where the toPrecision() method comes in.
The toPrecision() method returns either the fixed or exponential representation of a number, depending on which makes the most sense. This method takes one argument, which is the total number of digits to use to represent the number (not including exponents). Example:
var oNumberObject = new Number(99); alert(oNumberObject.toPrecision(1)); //outputs “1e+2”In this example, the task is to represent the number 99 with a single digit, which results in “1e+2”,otherwise known as 100. Yes, toPrecision() rounded the number to get as close as possible to the actual value. Because you can’t represent 99 with any fewer than 2 digits, this rounding had to occur. If,however, you want to represent 99 using two digits, well,that’s easy:
var oNumberObject = new Number(99); alert(oNumberObject.toPrecision(2));//outputs “99”Of course the output is “99”,because that is the exact representation of the number.But what if you specify more than the number of digits needed?
var oNumberObject = new Number(99); alert(oNumberObject.toPrecision(3));//outputs “99.0”In this case, toPrecision(3) is exactly equivalent to toFixed(1),outputting “99.0” as the result.
The toFixed(), toExponential(), and toPrecision() methods round up or down to accurately represent a number with the correct number of decimal places.
The String class
The String class is the object representation of a String primitive and is created in the following manner:
var oStringObject = new String(“hello world”);Both valueOf() and toString() return the String primitive value for a String object:
alert(oStringObject.valueOf() ==oStringObject.toString()); //outputs “true”If you run this code, the output is “true”, indicating that the values are indeed equal.
The String class is one of the more complicated reference types in ECMAScript. As such, this section focuses only on the basic functionality of the String class. More advanced functionality is split into suitable topics throughout the book.
The String class has one property, length, which gives the number of characters in the string:
var oStringObject = new String(“hello world”); alert(oStringObject.length); //outputs “11”This example outputs “11”,the number of characters in “hello world”.Note that even if the string contains a double-byte character (as opposed to an ASCII character, which uses just one byte), each character is still counted as one.
The String class also has a large number of methods. The first two, charAt() and charCodeAt(),have to do with accessing the individual characters in the string. As described in the section on String primitives, the first character is in position 0, the second is in position 1,and so on. Both these methods accept one argument, the position of the character to act on. The charAt() method returns a string containing the character in that position:
var oStringObject = new String(“hello world”); alert(oStringObject.charAt(1)); //outputs “e”The character in position 1 of “hello world” is “e”,so calling charAt(1) returns “e”.If instead of the actual character you want the character code, then calling charCodeAt() is the appropriate choice:
var oStringObject = new String(“hello world”); alert(oStringObject.charCodeAt(1)); //outputs “101”This example outputs “101”,which is the character code for the lowercase e character.
Next up is the concat() method, which is used to concatenate one or more strings to the primitive value of the String object.This method actually returns a String primitive value as a result and leaves the original String object intact:
var oStringObject = new String(“hello “); var sResult = oStringObject.concat(“world”); alert(sResult); //outputs “hello world” alert(oStringObject); //outputs “hello “The result of calling the concat() method in the previous code is “hello world”,whereas the contents of the String object remains “hello “. For this reason,it is much more common to use the add operator (+) to concatenate strings because it more logically indicates the actual behavior:
var oStringObject = new String(“hello “); var sResult = oStringObject + “world”; alert(sResult); //outputs “hello world” alert(oStringObject); //outputs “hello “So far,you have seen methods of concatenating strings and accessing individual characters in strings, but what if you are unsure if a character exists in a particular string? That’s where the indexOf() and lastIndexOf() methods are useful.
Both the indexOf() and lastIndexOf() methods return the position of a given substring within another string (or –1 if the substring isn’t found).The difference between the two is that the indexOf() method begins looking for the substring at the beginning of the string (character 0) whereas the lastIndexOf() method begins looking for the substring at the end of the string. For example:
var oStringObject = new String(“hello world”); alert(oStringObject.indexOf(“o”)); //outputs “4” alert(oStringObject.lastIndexOf(“o”)); //outputs “7”Here, the first occurrence of the string “o” occurs at position 4, which is the “o” in “hello”. The last occurrence of the string “o” is in the word “world”, at position 7. If there is only one occurrence of “o” in the string,then indexOf() and lastIndexOf() return the same position.
The next method is localeCompare(), which helps sort string values. This method takes one argument, the string to compare to, and it returns one of three values:
- If the String object should come alphabetically before the string argument, a negative number is returned (most often this is –1, but it is up to each implementation as to the actual value).
- If the String object is equal to the string argument, 0 is returned.
- If the String object should come alphabetically after the string argument, a positive number is returned (most often this is 1, but once again, this is implementation-specific).
var oStringObject = new String(“yellow”); alert(oStringObject.localeCompare(“brick”)); //outputs “1” alert(oStringObject.localeCompare(“yellow”)); //outputs “0” alert(oStringObject.localeCompare (“zoo”)); //outputs “-1”In this code, the string “yellow” is compared to three different values, “brick”, “yellow”, and “zoo”. Because “brick” comes alphabetically before “yellow”,localCompare() returns 1; “yellow” is equal to “yellow”, so localCompare() returns 0 for that line; “zoo” comes after “yellow”, so localCompare() returns –1. Once again, because the values are implementation-specific, it is best to use localCompare() in this way:
var oStringObject1 = new String(“yellow”); var oStringObject2 = new String(“brick”); var iResult = sTestString.localeCompare(“brick”); if(iResult < 0) { alert(oStringObject1 + “ comes before “ + oStringObject2); } else if (iResult > 0) { alert(oStringObject1 + “ comes after “ + oStringObject2); } else { alert(“The two strings are equal”); }By using this sort of construct,you can be sure that the code works correctly in all implementations. The unique part of localeCompare() is that an implementation’s locale (country and language) indicates exactly how this method operates.In the United States,where English is the standard language for ECMAScript implementations,localCompare() is case-sensitive,determining that uppercase letters come alphabetically after lowercase letters.However,this may not be the case in other locales.
ECMAScript provides two methods for creating string values from a substring: slice() and substring().
Both methods return a substring of the string they act on, and both accept either one or two arguments. The first argument is the position where capture of the substring begins; the second argument, if used,is the position before which capture is stopped (which is to say that the character at this position is not included in the returned value).If the second argument is omitted, it is assumed that the ending position is the length of the string. Just as with the concat() method, slice() and substring() do not alter the value of the String object itself: They simply return a primitive String value as the result,leaving the String object unchanged.
var oStringObject = new String(“hello world”); alert(oStringObject.slice(3)); //outputs “lo world” alert(oStringObject.substring(3)); //outputs “lo world” alert(oStringObject.slice(3, 7)); //outputs “lo w” alert(oStringObject.substring(3,7)); //outputs “lo w”In this example,slice() and substring() are used in the same manner and, ironically enough, return the same values.When given just one argument, 3, both methods return “lo world”, as the second “l” in “hello” is in position 3. When given two arguments, 3 and 7, both methods return “lo w” (the “o” in “world” is in position 7, so it is not included).Why have two methods that do the exact same thing? Truthfully, the methods aren’t identical,but they differ only in how they deal with arguments that are negative numbers.
For the slice() method,a negative argument is treated as the length of the string plus the negative argument;the substring() method treats a negative argument as 0 (which means that it is ignored).
For example:
var oStringObject= new String(“hello world”); alert(oStringObject.slice(-3)); //outputs “rld” alert(oStringObject.substring(-3)); //outputs “hello world” alert(oStringObject.slice(3, -4)); //outputs “lo w” alert(oStringObject.substring(3,-4)); //outputs “hel”Here, you see the main difference between slice() and substring().When you call each with one argument, -3,slice() returns “rld” while substring() returns “hello world”. This occurs because slice(-3) translates into slice(7) for the string “hello world” whereas substring(-3) translates into substring(0). Likewise,the difference is apparent when using the parameters 3 and –4. For the slice() method, this translates into slice(3,7),the same as the previous example,which returns “low” as the result. However the substring() method interprets this as substring(3,0), which is essentially substring(0, 3) because substring() always considers the smaller number as the start and the larger number as the end. As a result, substring(3,-4) returns “hel”.The bottom line here is to be clear about how you are using these two methods.
The last set of methods to be discussed involves case conversion.Four methods perform case conversion:
toLowerCase(),toLocaleLowerCase(),toUpperCase(),and toLocaleUpperCase().The uses for these methods are pretty obvious from their names—two convert the string into all lowercase and two convert the string into all uppercase. The toLowerCase() and toUpperCase() methods are the originals,modeled after the same methods in java.lang.String; the toLocaleLowerCase() and toLocaleUpperCase() methods are intended to be implemented based on a particular locale (in the same way localeCompare() is intended to be used).In many locales, the locale-specific methods are identical to the generic ones;however,a few languages apply special rules to Unicode case conversion (such as Turkish),and this necessitates using the locale-specific methods for proper conversion.
var oStringObject= new String(“Hello World”); alert(oStringObject.toLocaleUpperCase()); //outputs “HELLO WORLD” alert(oStringObject.toUpperCase()); //outputs “HELLO WORLD” alert(oStringObject.toLocaleLowerCase()); //outputs “hello world” alert(oStringObject.toLowerCase()); //outputs “hello world”This code outputs “HELLO WORLD” for both toLocaleUpperCase() and toUpperCase(),just as “hello world” is output for both toLocaleLowerCase() and toLowerCase().Generally speaking, if you do not know the language in which the code will be running,it is safer to use the locale-specific methods.
The instanceof operator
One of the problems with using reference types to store values has been the use of the typeof operator, which returns “object” no matter what type of object is being referenced. To provide a solution, ECMAScript introduced another Java operator: instanceof.
The instanceof operator works in a similar way to the typeof operator:It identifies the type of object you are working with. Unlike typeof, instanceof requires the developer to explicitly ask if an object is of a particular type. For example:
var oStringObject = new String(“hello world”); alert(oStringObject instanceof String); //outputs “true”Here, the code asks, “Is variable s an instance of the class String?” Yes it is, so the result is “true”. Although not as versatile as typeof, instanceof does offer enough help for the cases when typeof returns “object”.
ECMA-262 describes a set of operatorsthat
can be used to manipulate variables.The operators range from
mathematical operators (such as addition and subtraction) and bitwise
operators to relational operators and equality operators.Any time a
native action is performed on a value,it is considered an operator.
Unary operators
Unary operators take only one parameter:the object or value to operate on.They are the simplest operators in ECMAScript.
delete
The delete operator erases a reference to an object property or method that was previously defined.
Example:
The delete operator cannot be used to delete properties and methods that are not defined by the developer.
For instance,the following line causes an error:
void
The void operator returns undefined for any value. This is typically used to avoid outputting a value that shouldn’t be output, such as when calling a JavaScript function from an HTML <a> element. To do this properly, the function cannot return a valid value; otherwise the browser erases the page and displays only the result of the function. For example:
To avoid this, use the window.open() call with the void operator:
Prefix increment/decrement
Two operators taken directly from C (and Java) are prefix increment and prefix decrement.Prefix increment, which adds one to a number value, is indicated by placing two plus signs (++) in front of a variable:
When you use prefix operators,note that the increment/decrement takes place before the expression is evaluated.Consider the following example:
The prefix increment and decrement are equal in terms of order of precedence when evaluating a mathematical expression and, therefore,are evaluated left to right. For instance:
Postfix increment/decrement
Two operators, also taken directly from C (and Java), are the postfix increment and postfix decrement. They also add one to a number value, as indicated by the two plus signs (++) placed after a variable:
Unlike the prefix operators, postfix operators increment or decrement after the containing expression is evaluated. Consider the following example:
The postfix increment and decrement are also equal in terms of order of precedence when evaluating a mathematical expression, and they are both evaluated left to right.For instance:
Unary plus and minus
The unary plus and minus are familiar symbols to most people and operate the same way in ECMAScript as they do in high school math. The unary plus essentially has no effect on a number:
The unary minus, on the other hand, negates the value of a number (for example, converting 25 into –25):
Bitwise operators
The following set of operators work on numbers at their very base level, with the 32 bits that represent them. Before examining these operators, I begin with a more detailed look into integers in ECMAScript.
Integers revisited
ECMAScript integers come in two specific flavors: signed (allowing both positive and negative values) and unsigned (allowing only positive numbers).In ECMAScript, all integer literals are signed by default. But what exactly does this mean?
Signed integers use the first 31 bits to represent the numeric value of the integer, whereas the 32nd bit represents the sign of the number, 0 for positive or 1 for negative. The number values can range from –2147483648 to 2147483647.
You can store signed integers in binary form in two different ways, one for positive numbers and one for negative numbers. Positive numbers are stored in true binary format, with each of the first 31 bits representing a power of 2, starting with the first bit (called bit 0), which represents 20; the second bit (bit 1) represents 21, and so on. If any bits are unused, they are filled with 0s and essentially ignored. For example, the number 18 is represented as below.
The binary version of 18 uses only the first five bits, which are the significant bits for this number. When converting a number into a binary string (as discussed earlier), you see only the significant bits:
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:
0000 0000 0000 0000 0000 0000 0001 0010
Next, take the one’s complement, which is the inverse:
1111 1111 1111 1111 1111 1111 1110 1101
Finally, add 1 to the one’s complement:
1111 1111 1111 1111 1111 1111 1110 1101
1
----------------------------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
So, the binary equivalent of –18 is 1111 1111 1111 1111 1111 1111 1110 1110. Keep in mind that the developer has no access to bit 31 when dealing with signed integers.
The interesting thing about negative integers is that conversion to a binary string does not show the two’s complement form. Instead, ECMAScript outputs the standard binary code for the number’s absolute value preceded by a minus sign. For example:
Unsigned integers, on the other hand, treat the final bit just like the other bits. In this mode, the 32nd bit doesn’t represent the sign of the number but rather the value 231. Because of this extra bit, unsigned integers range in value from 0 to 4294967295. For numbers less than or equal to 2147483647, unsigned integers look the same as positive signed integers; numbers greater than 2147483647 require the use of bit 31 (which is always 0 in a signed positive integer). Unsigned integers only return the significant bits when they are converted into a binary string.
Remember, all integer literals are stored as signed integers by default. Unsigned integers can only be created by using one of the ECMAScript bitwise operators.
Bitwise NOT
The bitwise NOT is represented by a tilde (~) and is one of just a few ECMAScript operators related to binary mathematics. The bitwise NOT is a three-step process:
The bitwise AND operator is indicated by the ampersand (&) and works directly on the binary form of numbers. Essentially, bitwise AND lines up the bits in each number and then, using the following rules, 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
For example, if you wanted to AND the numbers 25 and 3 together, the code looks like this:
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 indicated by the pipe (|) and also works directly on the binary form of numbers.Essentially, bitwise OR follows these rules when evaluating bits:
Bit from First Number Bit from Second Number Result
1 1 1
1 0 1
0 1 1
0 0 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:
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
As you can see, four bits contain 1 in either number, so these are passed through to the result. The binary code 11011 is equal to 27.
Bitwise XOR
The bitwise XOR operator is indicated by a caret (^) and, of course, works directly on the binary form of numbers. Bitwise XOR is different from bitwise OR in that it returns 1 only when exactly one bit has a value of 1. Here is the truth table:
Bit from First Number Bit from Second Number Result
1 1 0
1 0 1
0 1 1
0 0 0
To XOR the numbers 25 and 3 together, use the following code:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
2 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
As you can see, four bits contain 1 in either number, so these are passed through to the result. The binary code 11010 is equal to 26.
Left shift
The left shift is represented by two less-than signs (<<). It shifts all bits in a number to the left by the number of positions given. For example, if you take the number 2 (which is equal to 10 in binary) and shifted it 5 bits to the left, you end up with 64 (which is equal to 1000000 in binary):
Note that left shift preserves the sign of the number it’s operating on. For instance, if –2 is shifted to the left by 5 spaces, it becomes –64, not positive 64. “But isn’t the sign stored in the 32nd bit?” you ask. Yes it is, but that is behind the scenes of ECMAScript. The developer can never have access to that 32nd bit directly. Even printing out a negative number as a binary string shows the negative sign (for instance, –2 is displayed as –10 instead of 10000000000000000000000000000010).
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); signed right shift is the exact opposite of left shift. For example, if 64 is shifted to the right five bits, it becomes 2:
Unsigned right shift
The unsigned right shift is represented by three greater-than signs (>>>) and shifts all bits in an unsigned 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:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
"Secret" sign bit The number 64
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
The number 64 shifted to the right 5 bits (the number 2) Padded with zeros
First, look at the true 32-bit representation of –64. To do so, you need to create an unsigned version of the number, which can be attained by using unsigned right shift with a bit count of 0:
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 in ECMAScript is the same as in C and Java, indicated by an exclamation point (!).Unlike logical OR and logical AND operators,the logical NOT always returns a Boolean value. The logical NOT operator behaves in the following way:
The logical NOT operator is also useful in determining the Boolean equivalent of an ECMAScript variable. In order to do this, you use two logical NOT operators in a row. 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 logical AND operator in ECMAScript is indicated by the double ampersand (&&):
Logical AND can be used with any type of operands, not just Boolean values. When either operand is not a primitive Boolean, logical AND does not always return a Boolean value:
Logical OR
The logical OR operator in ECMAScript is the same as in Java,using the double pipe (||):
Just like logical AND,if either operand is not a Boolean,logical OR will not always return a Boolean value:
This next section deals with the three multiplicative operators: multiple, 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 you need to be aware of.
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:
The divide operator is represented by a slash (/) and divides the first operand by the second operand:
The modulus (remainder) operator is represented by a percent sign (%) and is used in the following way:
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.
Add
The add operator (+) is used just as one would expect:
10 (a primitive 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 translated to “5” as well.
Subtract
The subtract operator (–) is another that is used quite frequently:
The less-than (<), greater-than (>), less-than-or-equal (<=), and greater-than-or-equal (>=) relational operators perform comparisons between numbers in the same way that you learned in math class. Each of these operators returns a Boolean value:
Equality operators
Determining whether two variables are equivalent is one of the most important operations in programming. This is fairly straightforward when dealing with primitive values, but the task gets a little complicated when you take objects into account. To deal with this problem, ECMAScript provides two sets of operators: equal and not equal to deal with primitive values, and identically equal and not identically equal to deal with objects.
Equal and not equal
The equal operator in ECMAScript is the double equal sign (==), and it returns true if—and only if— both operands are equal. The not equal operator is the exclamation point followed by an equal sign (!=), and it returns true if—and only if—two operands are not equal. Both operators do conversions in order to determine if two operands are equal.
When performing conversions, follow these basic rules:
Identically equal and not identically equal
The brothers of the equal and not equal operators are the identically equal and not identically equal operators. These two 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 only returns true if the operands are equal without conversion. For example:
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:
Conditional operator
The conditional operator is one of the most versatile in ECMAScript, and it takes on the same form as in Java:
Assignment operators
Simple assignment is done with the equals sign (=) and simply assigns the value on the right to the variable on the left. For example:
The comma operator allows execution of more than one operation in a single statement. Example:
Statements
Unary operators
Unary operators take only one parameter:the object or value to operate on.They are the simplest operators in ECMAScript.
delete
The delete operator erases a reference to an object property or method that was previously defined.
Example:
var o = new Object; o.name = “Nicholas”; alert(o.name); //outputs “Nicholas” delete o.name; alert(o.name); //outputs “undefined”In this example, the name property is deleted,which means that it is forcibly de-referenced and set to undefined (which you will remember is the same value a variable has when it is created and not initialized).
The delete operator cannot be used to delete properties and methods that are not defined by the developer.
For instance,the following line causes an error:
delete o.toString;Even though toString is a valid name of a method, this code line causes an error because the toString() method is native to ECMAScript and not developer-defined.
void
The void operator returns undefined for any value. This is typically used to avoid outputting a value that shouldn’t be output, such as when calling a JavaScript function from an HTML <a> element. To do this properly, the function cannot return a valid value; otherwise the browser erases the page and displays only the result of the function. For example:
<a href=”javascript:window.open(‘about:blank’)”>Click Me</a>If you place this line of code into an HTML page, and click the link, you see “[Object]” printed on the screen.This occurs because window.open() returns a reference to the newly opened window. That object is then converted to a string for display.
To avoid this, use the window.open() call with the void operator:
<a href=”javascript:void(window.open(‘about:blank’))”>Click Me</a>This makes the window.open() call return undefined, which is not a valid value and is not displayed in the browser window. Remember, functions that have no return value actually return undefined.
Prefix increment/decrement
Two operators taken directly from C (and Java) are prefix increment and prefix decrement.Prefix increment, which adds one to a number value, is indicated by placing two plus signs (++) in front of a variable:
var iNum = 10; ++iNumThe second line increments iNum to 11. This is effectively equal to:
var iNum = 10; iNum = iNum + 1;Likewise, the prefix decrement subtracts one from a value. The prefix decrement is indicated by two minus signs (– –) placed before the variable:
var iNum = 10; --iNum;In this example, the second line decreases the value of iNum to 9.
When you use prefix operators,note that the increment/decrement takes place before the expression is evaluated.Consider the following example:
var iNum = 10; --iNum; alert(iNum); //outputs “9” alert(--iNum); //outputs “8” alert(iNum); //outputs “8”The second line decrements num,and the third line displays the result (“9”).The fourth line displays num once again, but this time the prefix decrement is applied in the same statement, which results in the number “8” being displayed.To prove that all decrements are complete, the fifth line once again outputs “8”.
The prefix increment and decrement are equal in terms of order of precedence when evaluating a mathematical expression and, therefore,are evaluated left to right. For instance:
var iNum1 = 2; var iNum2 = 20; var iNum3 = --iNum1 + ++iNum2; //equals 22 var iNum4 = iNum1 + iNum2; //equals 22In the previous code, iNum3 is equal to 22 because the expression evaluates to 1 + 21. The variable iNum4 is also equal to 22 and also adds 1 + 21.
Postfix increment/decrement
Two operators, also taken directly from C (and Java), are the postfix increment and postfix decrement. They also add one to a number value, as indicated by the two plus signs (++) placed after a variable:
var iNum = 10; iNum++As you might expect, postfix decrement subtracts one from a value and is indicated by two minus signs (– –) placed after the variable:
var iNum = 10; iNum--;The second line of code decreases the value of iNum to 9.
Unlike the prefix operators, postfix operators increment or decrement after the containing expression is evaluated. Consider the following example:
var iNum = 10; iNum--; alert(iNum); //outputs “9” alert(iNum--); //outputs “9” alert(iNum); //outputs “8”Just as in the prefix example, the second line decrements iNum, and the third line displays the result (9). The fourth line displays num once again, but this time the postfix decrement is applied in the same statement. However, because the decrement doesn’t happen until after the expression is evaluated, this alert also displays the number 9. When the fifth line is executed, the alert displays 8,because the postfix decrement was executed after line 4 but before line 5.
The postfix increment and decrement are also equal in terms of order of precedence when evaluating a mathematical expression, and they are both evaluated left to right.For instance:
var iNum1 = 2; var iNum2 = 20; var iNum3 = iNum1-- + iNum2++; //equals 22 var iNum4 = iNum1 + iNum2; //equals 22In the previous code, iNum3 is equal to 22 because the expression evaluates to 2 + 20. The variable iNum4 is also equal to 22, although it evaluates 1 + 21 because the increment and decrement aren’t completed until after the value of iNum3 has been assigned.
Unary plus and minus
The unary plus and minus are familiar symbols to most people and operate the same way in ECMAScript as they do in high school math. The unary plus essentially has no effect on a number:
var iNum= 25; iNum = +iNum; alert(iNum); //outputs “25”In this code, the unary plus is applied to the number 25, which returns the exact same value. Although unary plus has no effect on numbers,it has an interesting effect on strings:It converts them to numbers.
var sNum = “25”; alert(typeof sNum); //outputs “string” var iNum = +sNum; alert(typeof iNum); //outputs “number”This code converts a string representation of 25 into the actual number. When the unary plus operates on strings, it evaluates strings the same way as parseInt() with one major difference: Unless the string begins with “0x” (indicating a hexadecimal number), the string is converted as if it were decimal. So “010” is always 10 when converted using unary plus, however, “0xB” is converted to 11.
The unary minus, on the other hand, negates the value of a number (for example, converting 25 into –25):
var iNum= 25; iNum = -iNum; alert(iNum); //outputs “-25”Similar to unary plus, unary minus converts a string into a number with one slight difference: Unary minus also negates the value. For example:
var sNum = “25”; alert(typeof sNum); //outputs “string” var iNum = -sNum; alert(iNum); //outputs “-25” alert(typeof iNum); //outputs “number”The unary minus converted the string “25” into the number –25 in the previous code (unary minus also acts the same way as unary plus regarding hexadecimal and decimal values, but it also negates the value).
Bitwise operators
The following set of operators work on numbers at their very base level, with the 32 bits that represent them. Before examining these operators, I begin with a more detailed look into integers in ECMAScript.
Integers revisited
ECMAScript integers come in two specific flavors: signed (allowing both positive and negative values) and unsigned (allowing only positive numbers).In ECMAScript, all integer literals are signed by default. But what exactly does this mean?
Signed integers use the first 31 bits to represent the numeric value of the integer, whereas the 32nd bit represents the sign of the number, 0 for positive or 1 for negative. The number values can range from –2147483648 to 2147483647.
You can store signed integers in binary form in two different ways, one for positive numbers and one for negative numbers. Positive numbers are stored in true binary format, with each of the first 31 bits representing a power of 2, starting with the first bit (called bit 0), which represents 20; the second bit (bit 1) represents 21, and so on. If any bits are unused, they are filled with 0s and essentially ignored. For example, the number 18 is represented as below.
The binary version of 18 uses only the first five bits, which are the significant bits for this number. When converting a number into a binary string (as discussed earlier), you see only the significant bits:
var iNum = 18; alert(iNum.toString(2)); //outputs “10010”This code outputs only “10010” instead of the whole 32-bit representation. The other bits really aren’t important because using just these five bits makes possible to determine the decimal value (Figure ).
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:
- Determine the binary representation of the non-negative version (for example, to find –18, first determine the binary representation of 18).
- Find the one’s complement of the number, which essentially means that every 0 must be replaced with 1 and vice versa.
- Add 1 to the one’s complement.
0000 0000 0000 0000 0000 0000 0001 0010
Next, take the one’s complement, which is the inverse:
1111 1111 1111 1111 1111 1111 1110 1101
Finally, add 1 to the one’s complement:
1111 1111 1111 1111 1111 1111 1110 1101
1
----------------------------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
So, the binary equivalent of –18 is 1111 1111 1111 1111 1111 1111 1110 1110. Keep in mind that the developer has no access to bit 31 when dealing with signed integers.
The interesting thing about negative integers is that conversion to a binary string does not show the two’s complement form. Instead, ECMAScript outputs the standard binary code for the number’s absolute value preceded by a minus sign. For example:
var iNum = -18; alert(iNum.toString(2)); //outputs “-10010”This code outputs only “-10010” instead of the two’s complement in order to protect bit 31 from being accessed. To put it simply, ECMAScript aims to deal with integers in such a simple way that developers need not spend any time worrying about their usage.
Unsigned integers, on the other hand, treat the final bit just like the other bits. In this mode, the 32nd bit doesn’t represent the sign of the number but rather the value 231. Because of this extra bit, unsigned integers range in value from 0 to 4294967295. For numbers less than or equal to 2147483647, unsigned integers look the same as positive signed integers; numbers greater than 2147483647 require the use of bit 31 (which is always 0 in a signed positive integer). Unsigned integers only return the significant bits when they are converted into a binary string.
Remember, all integer literals are stored as signed integers by default. Unsigned integers can only be created by using one of the ECMAScript bitwise operators.
Bitwise NOT
The bitwise NOT is represented by a tilde (~) and is one of just a few ECMAScript operators related to binary mathematics. The bitwise NOT is a three-step process:
- The operand is converted to a 32-bit number.
- The binary form is converted into its one’s complement.
- The one’s complement is converted back to a floating-point number.
var iNum1 = 25;//25 is equal to 00000000000000000000000000011001 var iNum2 = ~iNum1; //convert to 111111111111111111111111111100110 alert(iNum2); //outputs “-26”The bitwise NOT essentially negates a number and then subtracts 1 from it, so 25 becomes –26. Really, the same effect can be achieved by doing this:
var iNum1 = 25; var iNum2 = -iNum1 – 1; alert(iNum2); //outputs “-26”Bitwise AND
The bitwise AND operator is indicated by the ampersand (&) and works directly on the binary form of numbers. Essentially, bitwise AND lines up the bits in each number and then, using the following rules, 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
For example, if you wanted to AND the numbers 25 and 3 together, the code looks like this:
var iResult = 25 & 3; alert(iResult); //outputs “1”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 indicated by the pipe (|) and also works directly on the binary form of numbers.Essentially, bitwise OR follows these rules when evaluating bits:
Bit from First Number Bit from Second Number Result
1 1 1
1 0 1
0 1 1
0 0 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:
var iResult = 25 | 3; alert(iResult); //outputs “27”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
As you can see, four bits contain 1 in either number, so these are passed through to the result. The binary code 11011 is equal to 27.
Bitwise XOR
The bitwise XOR operator is indicated by a caret (^) and, of course, works directly on the binary form of numbers. Bitwise XOR is different from bitwise OR in that it returns 1 only when exactly one bit has a value of 1. Here is the truth table:
Bit from First Number Bit from Second Number Result
1 1 0
1 0 1
0 1 1
0 0 0
To XOR the numbers 25 and 3 together, use the following code:
var iResult = 25 ^ 3; alert(iResult); //outputs “26”The result of a bitwise XOR between 25 and 3 is 26:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
2 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
As you can see, four bits contain 1 in either number, so these are passed through to the result. The binary code 11010 is equal to 26.
Left shift
The left shift is represented by two less-than signs (<<). It shifts all bits in a number to the left by the number of positions given. For example, if you take the number 2 (which is equal to 10 in binary) and shifted it 5 bits to the left, you end up with 64 (which is equal to 1000000 in binary):
var iOld = 2;//equal to binary 10 var iNew = iOld << 5;//equal to binary 1000000 which is decimal 64Note that when the bits are shifted, five empty bits remain to the right of the number. The left shift fills these bits with the value in the 32nd bit (the sign bit) to make the result a complete 32-bit number (Figure).
Note that left shift preserves the sign of the number it’s operating on. For instance, if –2 is shifted to the left by 5 spaces, it becomes –64, not positive 64. “But isn’t the sign stored in the 32nd bit?” you ask. Yes it is, but that is behind the scenes of ECMAScript. The developer can never have access to that 32nd bit directly. Even printing out a negative number as a binary string shows the negative sign (for instance, –2 is displayed as –10 instead of 10000000000000000000000000000010).
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); signed right shift is the exact opposite of left shift. For example, if 64 is shifted to the right five bits, it becomes 2:
var iOld = 64;//equal to binary 1000000 var iNew = iOld >> 5;//equal to binary 10 with is decimal 2Once 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. 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 an unsigned 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:
var iOld = 64; //equal to binary 1000000 var iNew = iOld >>> 5; //equal to binary 10 with is decimal 2For numbers that are negative, however, something quite different happens. You see, the unsigned right shift operator fills all empty bits with the value contained in the 32nd bit. For positive numbers, this bit is 0; so the empty bits are filled with zero. For negative numbers, however, this bit is 1, meaning that all empty bits are filled with 1. Because the result of unsigned right shift is an unsigned 32-bit number, you end up with a very large number. For example, if you shift –64 to the right by five bits, you end up with 2147483616. How does this happen?
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
"Secret" sign bit The number 64
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
The number 64 shifted to the right 5 bits (the number 2) Padded with zeros
First, look at the true 32-bit representation of –64. To do so, you need to create an unsigned version of the number, which can be attained by using unsigned right shift with a bit count of 0:
var iUnsigned64 = 64 >>> 0;Then, to get the actual bit representation, use the toString() method of the Number type with a radix of 2:
alert(iUnsigned64.toString(2));This yields a value of 11111111111111111111111111000000, which is the two’s complement representation of –64 for a signed integer, but it is equal to 4294967232 as an unsigned integer. For this reason, use caution with the unsigned right shift operator.
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 in ECMAScript is the same as in C and Java, indicated by an exclamation point (!).Unlike logical OR and logical AND operators,the logical NOT always returns a Boolean value. The logical NOT operator behaves in the following way:
- If the operand is an object,false is returned.
- If the operand is the number 0, true is returned.
- If the operand is any number other than 0,false is returned.
- If the operand is null, true is returned.
- If the operand is NaN, true is returned.
- If the operand is undefined,an error occurs.
var bFound = false; var i = 0; while (!bFound) { if (aValues[i] == vSearchValue) { bFound = true; } else { i++; } }In this example, a Boolean variable (found) keeps track of the success of a search. When the item in question is located,found is set to true, which causes !found to equal false, meaning that execution will escape the while loop.
The logical NOT operator is also useful in determining the Boolean equivalent of an ECMAScript variable. In order to do this, you use two logical NOT operators in a row. 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.
var bFalse = false; var sBlue = “blue”; var iZero = 0; var iThreeFourFive = 345; var oObject = new Object; document.write(“The Boolean value of bFalse is “ + (!!bFalse)); document.write(“<br />The Boolean value of sBlue is “ + (!!sBlue)); document.write(“<br />The Boolean value of iZero is “ + (!!iZero)); document.write(“<br />The Boolean value of iThreeFourFive is “ + (!!iThreeFourFive)); document.write(“<br />The Boolean value of oObject is “ + (!!oObject));Running this example yields the following output:
The Boolean value of bFalse is false The Boolean value of sBlue is true The Boolean value of iZero is false The Boolean value of iThreeFourFive is true The Boolean value of oObject is trueLogical AND
The logical AND operator in ECMAScript is indicated by the double ampersand (&&):
var bTrue = true; var bFalse = false; var bResult = bTrue && bFalse;Logical AND behaves as described in the following truth table:
Logical AND can be used with any type of operands, not just Boolean values. When either operand is not a primitive Boolean, logical AND does not always return a Boolean value:
- If one operand is an object and one is a Boolean, the object is returned.
- If both operands are objects,the second operand is returned.
- If either operand is null, null is returned.
- If either operand is NaN, NaN is returned.
- If either operand is undefined, an error occurs.
var bTrue = true; var bResult = (bTrue && bUnknown); //error occurs here alert(bResult); //this line never executesThis code causes an error when the logical AND is evaluated because the variable bUnknown is undefined.The value of variable bTrue is true, so the logical AND operator continued on to evaluate variable bUnknown.When it did, an error occurred because bUnknown is undefined and, therefore, cannot be used in a logical AND operation.If this example is changed so that a is set to false, the error won’t occur:
var bFalse = false; var bResult = (bFalse && bUnknown); alert(bResult); //outputs “false”In this code,the script writes out the string “false”, the value returned by the logical AND operator. Even though the variable bUnknown is undefined, it never gets evaluated because the first operand is false.You must always keep in mind short-circuiting when using logical AND.
Logical OR
The logical OR operator in ECMAScript is the same as in Java,using the double pipe (||):
var bTrue = true; var bFalse = false; var bResult = bTrue || bFalse;Logical OR behaves as described in the following truth table:
Just like logical AND,if either operand is not a Boolean,logical OR will not always return a Boolean value:
- If one operand is an object and one is a Boolean, the object is returned.
- If both operands are objects, the first operand is returned.
- If both operands are null, null is returned.
- If either operand is NaN, NaN is returned.
- If either operand is undefined,an error occurs.
var bTrue = true; var bResult = (bTrue || bUnknown); alert(bResult); //outputs “true”As with the previous example, the variable c is undefined. However, because the variable bTrue is set to true, variable bUnknown is never evaluated and thus the output is “true”. If the value of bTrue is changed to false, an error occurs:
var bFalse = false; var bResult = (bTrue || bUnknown); //error occurs here alert(bResult); //this line never executesMultiplicative operators
This next section deals with the three multiplicative operators: multiple, 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 you need to be aware of.
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:
var iResult = 34 * 56;However, the multiply operator also has some unique behaviors when dealing with special values:
- If the operands are numbers,regular arithmetic multiply is performed, meaning that two positives or two negatives equal a positive, whereas operands with different signs yield a negative. If the result is too high or too low, the result is either Infinity or –Infinity.
- 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 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.
The divide operator is represented by a slash (/) and divides the first operand by the second operand:
var iResult = 66 / 11;The divide operator, like the multiply operator, has special behaviors for special values:
- 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 is too high or too low, the result is either Infinity or – Infinity.
- If either operand is NaN, the result is NaN.
- If Infinity is divided by Infinity,the result is NaN.
- If Infinity is divided by any number,the result is Infinity.
- Division of a non-infinite number by 0 always equals NaN.
- If Infinity is divided by any number other than 0, the result is either Infinity or –Infinity, depending on the sign of the second operand.
The modulus (remainder) operator is represented by a percent sign (%) and is used in the following way:
var iResult = 26 % 5; //equal to 1Just like the other multiplicative operators, the modulus operator behaves differently for special values:
- If the operands are numbers, regular arithmetic division is performed,and the remainder of that division is returned.
- If the dividend is Infinity or the divisor is 0, the result is NaN.
- If Infinity is divided by Infinity, the result is NaN.
- If the divisor is an infinite number, the result is the dividend.
- If the dividend is 0,the result is 0.
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.
Add
The add operator (+) is used just as one would expect:
var iResult = 1 + 2;Just like the multiplicative operators, additive operators also behave in special ways when dealing with special values. If the two operands are numbers, they perform an arithmetic add and return the result according to these rules:
- If either number 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 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.
var result1 = 5 + 5; //two numbers alert(result); //outputs “10” var result2 = 5 + “5”; //a number and a string alert(result); //outputs “55”This code illustrates the difference between the two modes for the add operator. Normally, 5 + 5 equals
10 (a primitive 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 translated to “5” as well.
Subtract
The subtract operator (–) is another that is used quite frequently:
var iResult = 2 – 1;Just like the add operator, the subtract operator has special rules to deal with the variety of type conversions present in ECMAScript:
- If the two operands are numbers, perform arithmetic subtract and return the result.
- If either number 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 of the two operands is not a number, the result is NaN.
The less-than (<), greater-than (>), less-than-or-equal (<=), and greater-than-or-equal (>=) relational operators perform comparisons between numbers in the same way that you learned in math class. Each of these operators returns a Boolean value:
var bResult1 = 5 > 3; //true var bResult2 = 5 < 3; //falseWhen a relational operator is used on two strings, however, a different 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 bResult = “Brick” < “alphabet”; alert(bResult); //outputs “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 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:
var bResult = “Brick”.toLowerCase() < “alphabet”.toLowerCase(); alert(bResult); //outputs “false”Converting both operands to lowercase ensures that “alphabet” is correct identified as alphabetically before “Brick”. Another sticky situation occurs when comparing numbers that are strings, for example:
var bResult = “23” < “3”; alert(bResult); //outputs “true”This code will output “true” when comparing the string “23” to “3”. Because both operand 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, the result makes more sense:
var bResult = “23” < 3; alert(bResult); //outputs “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, ECMAScript says that the string should be converted into a number and then numerically compared with 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 bResult = “a” < 3; alert(bResult);What would you expect this to output? The letter “a” can’t be meaningfully converted into a number. After all, if you were to use parseInt() on it, NaN would be returned. As a rule, any relational operation that contains NaN returns false, so this code also outputs false:
var bResult = “a” >= 3; alert(bResult);Typically, if two values return false for a less-than operation, they must return true for a greater-thanor- equal operation, but this is not the case when one number is NaN.
Equality operators
Determining whether two variables are equivalent is one of the most important operations in programming. This is fairly straightforward when dealing with primitive values, but the task gets a little complicated when you take objects into account. To deal with this problem, ECMAScript provides two sets of operators: equal and not equal to deal with primitive values, and identically equal and not identically equal to deal with objects.
Equal and not equal
The equal operator in ECMAScript is the double equal sign (==), and it returns true if—and only if— both operands are equal. The not equal operator is the exclamation point followed by an equal sign (!=), and it returns true if—and only if—two operands are not equal. Both operators do conversions in order to determine if two operands are equal.
When performing conversions, 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 operand is an object and the other is a string, attempt to convert the object to a string (using the toString() method) before checking for equality.
- If one operand is an object and the other is a number, attempt to convert the object to a number before checking for equality.
- 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 the reference values are compared. If both operands point to the same object, then the equal operator returns true. Otherwise, the two are not equal.
Identically equal and not identically equal
The brothers of the equal and not equal operators are the identically equal and not identically equal operators. These two 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 only returns true if the operands are equal without conversion. For example:
var sNum = “55”; var iNum = 55; alert(sNum == iNum); //outputs “true” alert(sNum === iNum); //outputs “false”In this code, the first alert uses the equal operator to compare the string “55” and the number 55 and outputs “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 alert 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:
var sNum = “55”; var iNum = 55; alert(sNum != iNum); //outputs “false” alert(sNum !== iNum); //outputs “true”Here, the first alert 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 alert uses the not identically equal operator. It helps to think of this operation as saying, “is sNum different from iNum?” The answer to this is yes (true), because sNum is a string and iNum is a number, so they are very different.
Conditional operator
The conditional operator is one of the most versatile in ECMAScript, and it takes on the same form as in Java:
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. For instance:
var iMax =(iNum1 >iNum2)? iNum1:iNum2;In this example, iMax is to be assigned the number with the highest value. The expression states that if iNum1 is greater than iNum2, iNum1 is assigned to iMax. If, however, the expression is false (meaning that iNum2 is less than or equal to iNum1), iNum2 is assigned to iMax.
Assignment operators
Simple assignment is done with the equals sign (=) and simply assigns the value on the right to the variable on the left. For example:
var iNum = 10;Compound assignment is done with one of the multiplicative, additive, or bitwise shift operators followed by an equals sign (=). These assignments are designed as shorthand for such common situations as:
var iNum = 10; iNum = iNum + 10;The second line of code can be replaced with a compound assignment:
var iNum = 10; iNum += 10;Compound assignment operators exist for each of the major mathematical operations and a few others as well:
- Multiply/Assign (*=)
- Divide/Assign (/=)
- Modulus/Assign (%=)
- Add/Assign (+=)
- Subtract/Assign (-=)
- Left Shift/Assign (<<=)
- Signed Right Shift/Assign (>>=)
- Unsigned Right Shift/Assign (>>>=)
The comma operator allows execution of more than one operation in a single statement. Example:
var iNum1=1, iNum2=2, iNum3=3;Most often, the comma operator is used in the declaration of variables.
ECMA-262 describes several statements for
ECMAScript.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. This section introduces all the standard ECMAScript
statements.
The if statement
One of the mostfrequently used statements in ECMAScript (and indeed, in many languages), is the if statement.The if statement has the following syntax:
ECMAScript converts it to a Boolean for you.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). For example:
Iterative statements, also called loop statements,specify certain commands to be executed repeatedly until some condition is met. The loops are often used to iterate the values of an array (hence the name) or to work though repetitious mathematical tasks. ECMAScript provides four types of iterative statements to aid in the process.
do-while
The do-while statement is a post-test loop, meaning that the evaluation of the escape condition is only done after the code inside the loop has been executed.This means that the body of the loop is always executed at least once before the expression is evaluated. Syntax:
The while statement is a pretest loop.This means the evaluation of the escape condition is done before the code inside the loop has been executed. Because of this,it is possible that the body of the loop is never executed. Syntax:
while(expression) statement
For example:
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 entered. Syntax:
for (initialization; expression; post-loop-expression) statement
For example:
for-in
The for-in statement is a strict iterative statement. It is used to enumerate the properties of an object.
Syntax:
Labeled statements
It is possible to label statements for later use with the following syntax:
label: statement
For example:
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, preventing any further repetition of the code while the continue statement exits the current repetition. It does, however, allow further repetition based on the control expression. For example:
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:
As you can tell, using labeled statements in conjunction with break and continue can be powerful, but this practice can also make debugging code a problem, if it is overused. Make sure to always use descriptive labels and try not to nest more than a handful of loops.
The with statement
The with statement is used to set the scope of the code within a particular object. Its syntax is the following:
with (expression) statement;
For example:
The switch statement
The cousin of the if statement, the switch statement, allows a developer to provide a series of cases for an expression. The syntax for the switch statement is:
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:
Functions
Functions are the heart of
ECMAScript: a collection of statements that can be run anywhere at
anytime. Functions are declared with the keyword function, followed by a
set of arguments, and finally by the code to execute enclosed in
braces. The basic syntax is:The if statement
One of the mostfrequently used statements in ECMAScript (and indeed, in many languages), is the if statement.The if statement has the following syntax:
if (condition) statement1 else statement2The condition can be any expression; it doesn’t even have to evaluate to an actual Boolean value.
ECMAScript converts it to a Boolean for you.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). For example:
if (i > 25) alert(“Greater than 25.”); //one-line statement else { alert(“Less than or equal to 25.”); //block statement }You can also chain if statements together like so:
if (condition1) statement1 else if (condition2) statement2 else statement3 Example: if (i > 25) { alert(“Greater than 25.”) } else if (i < 0) { alert(“Less than 0.”); } else { alert(“Between 0 and 25,inclusive.”); }Iterative statements
Iterative statements, also called loop statements,specify certain commands to be executed repeatedly until some condition is met. The loops are often used to iterate the values of an array (hence the name) or to work though repetitious mathematical tasks. ECMAScript provides four types of iterative statements to aid in the process.
do-while
The do-while statement is a post-test loop, meaning that the evaluation of the escape condition is only done after the code inside the loop has been executed.This means that the body of the loop is always executed at least once before the expression is evaluated. Syntax:
do { statement } while (expression); For example: var i = 0; do { i += 2; } while (i < 10);while
The while statement is a pretest loop.This means the evaluation of the escape condition is done before the code inside the loop has been executed. Because of this,it is possible that the body of the loop is never executed. Syntax:
while(expression) statement
For example:
var i = 0; while (i < 10) { i += 2; }for
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 entered. Syntax:
for (initialization; expression; post-loop-expression) statement
For example:
for (var i=0; i < iCount; i++){ alert(i); }This code defines a variable i that begins with the value 0. The for loop is entered only if the conditional expression (i < iCount) 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.
for-in
The for-in statement is a strict iterative statement. It is used to enumerate the properties of an object.
Syntax:
for (property in expression) statement For example: for (sProp in window) { alert(sProp); }Here, the for-in statement is used to display all the properties of the BOM window object. The method propertyIsEnumerable(), discussed earlier, is included in ECMAScript specifically to indicate whether or not a property can be accessed using the for-in statement.
Labeled statements
It is possible to label statements for later use with the following syntax:
label: statement
For example:
start: var iCount = 10;In this example, the label start can later be referenced by using the break or continue statements.
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, preventing any further repetition of the code while the continue statement exits the current repetition. It does, however, allow further repetition based on the control expression. For example:
var iNum = 0; for (var i=1; i < 10; i++) { if (i % 5 == 0) { break; } iNum++; } alert(iNum); //outputs “4”In the previous code, the for loop is to iterate 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 alert displays “4”, indicating the number of times the loop has been executed before exiting. If this example is updated to use continue instead of break, a different outcome occurs:
var iNum = 0; for (var i=1; i < 10; i++) { if (i % 5 == 0) { continue; } iNum++; } alert(iNum); //outputs “8”Here, the alert displays “8”, the number of times the loop has been executed. The total number of times that the loop can possibly be executed is 9, but when i reaches a value of 5, the continue statement is executed, causing the loop to skip the expression iNum++ and return to the top.
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 iNum = 0; outermost: for (var i=0; i < 10; i++) { for (var j=0; j < 10; j++) { if (i == 5 && j == 5) { break outermost; } iNum++; } } alert(iNum); //outputs “55”In this example one label, outermost,indicates the first for statement.Each loop normally executes 10 times a piece, meaning that the iNum++ statement is normally executed 100 times and, consequently, iNum should be equal to 100 when the execution is complete. The break statement here is given one argument,the label to break to. Doing this allows the break statement not just to break 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, iNum ends up with a value of 55 because execution is halted when both i and j are equal to 5. The continue statement can also be used in the same way:
var iNum = 0; outermost: for (var i=0; i < 10; i++) { for (var j=0; j < 10; j++) { if (i == 5 && j == 5) { continue outermost; } iNum++; } } alert(iNum); //outputs “95”In this case,the continue statement forces execution to continue—not in the inner loop, but in the outer loop.Because this occurs when j is equal to 5, that means the inner loop misses five iterations, leaving iNum equal to 95.
As you can tell, using labeled statements in conjunction with break and continue can be powerful, but this practice can also make debugging code a problem, if it is overused. Make sure to always use descriptive labels and try not to nest more than a handful of loops.
The with statement
The with statement is used to set the scope of the code within a particular object. Its syntax is the following:
with (expression) statement;
For example:
var sMessage = “hello world”; with(sMessage) { alert(toUpperCase());//outputs “HELLO WORLD” }In this code, the with statement is used with a string, so when the toUpperCase() method is called, the interpreter checks to see if this is a local function. If not, it checks the sMessage pseudo-object to see if toUpperCase() is a method for it, which it is. The alert then outputs “HELLO WORLD” because the interpreter finds the implementation of toUpperCase() on the “hello world” string.
The switch statement
The cousin of the if statement, the switch statement, allows a developer to provide a series of cases for an expression. The syntax for the switch statement is:
switch (expression) { case value: statement break; case value: statement break; case value: statement break; ... case value: statement break; default: statement }Each case says “if expression is equal to value, execute 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: switch (i) { case 25: alert(“25”); break; case 35: alert(“35”); break; case 45: alert(“45”); break; default: alert(“Other”); }Two big differences exist between the switch statement in ECMAScript and Java. In ECMAScript, the switch statement can be used on strings, and it can indicate case by nonconstant values:
var BLUE = “blue”, RED = “red”, GREEN = “green”; switch (sColor) { case BLUE: alert(“Blue”); break; case RED: alert(“Red”); break; case GREEN: alert(“Green”); break; default: alert(“Other”); }Here, the switch statement is used on the string sColor, whereas the cases are indicated by using the variables BLUE, RED, and GREEN, which is completely valid in ECMAScript.
function functionName(arg0,arg1,...,argN) { statements } For example: function sayHi(sName, sMessage) { alert(“Hello “ + name + “,” + sMessage); }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?”);This code results in the alert displayed in Figure
.
The sayHi() function doesn’t specify a return value, but it requires no special declaration (such as void is used in Java) to do so. Likewise, a function doesn’t need to explicitly declare a return value type if the function does indeed return a value. The function need only use the return operator followed by the value to return:
function sum(iNum1, iNum2) { return iNum1 + iNum2; }The value of the sum function is returned and assigned to a variable like this:
var iResult = sum(1,1); alert(iResult); //outputs “2”Another important concept is that,just as in Java, the function stops executing code after a return statement is executed. Therefore, any code that comes after a return statement is not executed. For example,the alert in the following function is never displayed:
function sum(iNum1, iNum2) { return iNum1 + iNum2; alert(iNum1 + iNum2); //never reached }It is possible to have more than one return statement in a function, as in this function:
function diff(iNum1, iNum2) { if (iNum1 > iNum2) { return iNum1 – iNum2; } else { return iNum2 – iNum1; } }The previous function is designed to return the difference between two numbers. To do so, it must always subtract the smaller number from the larger, which results in an if statement to determine which return statement to execute.
If a function doesn’t return a value,it can use the return operator without any parameters to exit a function at any time. Example:
function sayHi(sMessage) { if (sMessage == “bye”){ return; } alert(sMessage); }In this code, the alert will never be displayed if the message is equal to the string “bye”.
No overloading
ECMAScript functions cannot be overloaded. This may come as a surprise, considering ECMAScript closely resembles other higher-level programming languages that support overloading. You can define two functions with the same name in the same scope without an error; however, the last function becomes the one that is used. Consider the following example:
function doAdd(iNum) { alert(iNum + 100); } function doAdd(iNum) { alert(iNum + 10); } doAdd(10);What do you think will be displayed from this code snippet? The alert will show “20”, because the second doAdd() definition overwrites the first. Although this can be annoying to a developer, you have a way to work around this limitation by using the arguments object.
The arguments object
Within a function’s code, a special object called arguments gives the developer access to the function’s arguments without specifically naming them. For example,in the sayHi() function,the first argument is given the name message.The same value can also be accessed by referencing arguments[0],which asks for the value of the first argument (the first argument is in position 0, the second is in position 1,and so on.). Therefore, the function can be rewritten without naming the argument explicitly:
function sayHi() { if (arguments[0] == “bye”) { return; } alert(arguments[0]); }The arguments object can also be used to check the number of arguments passed into the function by referencing the arguments.length property. The following example outputs the number of arguments each time the function is called:
function howManyArgs() { alert(arguments.length); } howManyArgs(“string”, 45); //outputs “2” howManyArgs(); //outputs “0” howManyArgs(12); //outputs “1”This snippet shows alerts displaying “2”, “0”, and “1” (in that order). In this way, the arguments object puts the responsibility on the developer to check the arguments that are passed into a function.
By using the arguments object to determine the number of arguments passed into the function, it is possible to simulate the overloading of functions:
function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 10); } else if (arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //outputs “20” doAdd(30, 20); //outputs “50”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) outputs “20” whereas doAdd(30,20) outputs “50”. It’s not quite as good as overloading, but it is a sufficient workaround for this ECMAScript limitation.
The Function class
Perhaps the most interesting aspect of ECMAScript is that functions are actually full-fledged objects. A Function class represents each and every function a developer defines. The syntax for creating a function using the Function class directly is as follows:
var function_name = new Function(argument1, argument2,..,argumentN, function_body);
In this form, each of the function arguments is one parameter, with the final parameter being the function body (the code to execute). Each of these parameters must be a string. Remember this function?
function sayHi(sName, sMessage) { alert(“Hello “ + sName + “,” + sMessage); } It can also be defined like this: var sayHi = new Function(“sName”,“sMessage”, “alert (”Hello ” + sName + ”, ” + sMessage + ”);”);Admittedly, this form is a little bit harder to write because of the nature of strings, but understand that functions are just reference types and they always behave as if using the Function class explicitly created
for them. Remember this example?
function doAdd(iNum) { alert(iNum + 100); } function doAdd(iNum) { alert(iNum + 10); } doAdd(10); //outputs “20”As you remember, the second function overrides the first, making doAdd(10) output “20” instead of “110”. This concept becomes a whole lot clearer if this block is rewritten as follows:
doAdd = new Function(“iNum”, “alert(iNum + 100)”); doAdd = new Function(“iNum”, “alert(iNum + 10)”); doAdd(10);Looking at this code, it is clear that the value of doAdd has changed to point to a different object. Yes, function names are just reference values pointing to a function object and behave just as other pointers do. It is even possible to have two variables point to the same function:
var doAdd = new Function(“iNum”, “alert(iNum + 10) “); var alsoDoAdd = doAdd; doAdd(10); //outputs “20” alsoDoAdd(10); //outputs “20”Here, the variable doAdd is defined as a function, and then alsoDoAdd is declared to point to the same function. Both can then be used to execute the function’s code and output the same result, “20”. So if a function name is just a variable pointing to a function, is it possible to pass a function as an argument to another function? Yes!
function callAnotherFunc(fnFunction, vArgument) { fnFunction(vArgument); } var doAdd = new Function(“iNum”, “alert(iNum + 10)”); callAnotherFunc(doAdd, 10); //outputs “20”In this example, callAnotherFunction() accepts two arguments: a function to call and an argument to pass to the function. This code passes the doAdd() function into callAnotherFunction() with an argument of 10, outputting “20”.
Because functions are reference types, they can also have properties and methods. The one property defined in ECMAScript is length, which indicates the number of arguments that a function expects. Example:
function doAdd(iNum) { alert(iNum + 10); } function sayHi() { alert(“Hi”); } alert(doAdd.length); //outputs “1” alert(sayHi.length); //outputs “0”The function doAdd() defines one argument to pass in, so its length is 1; sayHi() defines no arguments, so its length is 0. Remember, ECMAScript functions can accept any number of arguments (up to 255) regardless of how many are defined. The length property just gives a convenient way to check how many arguments are expected by default.
Function objects also have the standard valueOf() and toString() methods shared by all objects. Both of these methods return the source code for the function and are particularly useful in debugging. For example:
function doAdd(iNum) { alert(iNum + 10); } alert(doAdd.toString());This code outputs the exact text of the doAdd() function (see Figure).
Closures
One of the most misunderstood aspects of ECMAScript is its support for closures. Closures are functions whose lexical representation includes variables that aren’t evaluated, meaning that functions are capable of using variables defined outside of the function itself. Using global variables in ECMAScript is a simple example of a closure. Consider the following example:
var sMessage = “Hello World!”; function sayHelloWorld() { alert(sMessage); } sayHelloWorld();In this code, the variable sMessage isn’t evaluated for the function sayHelloWorld() while the scripts is being loaded into memory. The function captures sMessage for later use, which is to say that the interpreter knows to check the value of sMessage when the function is called. When sayHelloWorld() is called (on the last line), the value of sMessage is assigned and the message “Hello World!” is displayed. Closures can get more complicated, as when you are defining a function inside of another function, as shown here:
var iBaseNum = 10; function addNumbers(iNum1, iNum2) { function doAddition() { return iNum1 + iNum2 + iBaseNum; } return doAddition(); }Here, the function addNumbers() contains a function (the closure) named doAddition(). The internal function is a closure because it captures the arguments of the outer function, iNum1 and iNum2, as well as the global variable iBaseNum. The last step of addNumbers() calls the inner function, which adds the two arguments and the global variable and returns the value. The important concept to grasp here is that doAddition() doesn’t accept any arguments at all; the values it uses are captured from the execution environment.
As you can see, closures are a very powerful, versatile part of ECMAScript that can be used to perform complex calculations. Just as when you use any advanced functionality, exercise caution when using closures because they can get extremely complex.
No comments :
Post a Comment