Further Topics in JavaScript

18 488 0
Further Topics in JavaScript

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Chapter 11. Further Topics in had they been covered earlier. Now that you have read through the and are experienced with the core JavaScript language, you are he more advanced and detailed concepts presented here. You may prefer, however, to move on to other chapters and learn about the specifics of client-side JavaScript before returning to this chapter. 11.1 Data Type Conversion We've seen that JavaScript is an untyped language (or, perhaps more accurately, a loosely typed or dynamically typed language). This means, for example, that we don't have to specify the data type of a variable when we declare it. Being untyped gives JavaScript the flexibility and simplicity that are desirable for a scripting language (although those features come at the expense of rigor, which is important for the longer, more complex programs often written in stricter languages such as C and Java). An important feature of JavaScript's flexible treatment of data types is the automatic type conversions it performs. For example, if you pass a number to the document.write( ) method, JavaScript automatically converts that value into its equivalent string representation. Similarly, if you test a string value in the condition of an if statement, JavaScript automatically converts that string to a boolean value -- to false if the string is empty and to true otherwise. The basic rule is that when a value of one type is used in a context that requires a value of some other type, JavaScript automatically attempts to convert the value as needed. So, for example, if a number is used in a boolean context, it is converted to a boolean. If an object is used in a string context, it is converted to a string. If a string is used in a numeric context, JavaScript attempts to convert it to a number. Table 11-1 JavaScript This chapter covers miscellaneous JavaScript topics that would have bogged down previous chapters preceding chapters tprepared to tackle summarizes each of these conversions -- it shows the conversion that is performed when a particular type of value is used in a particular context. The sections that follow the table provide more detail about type conversions in JavaScript. Table 11-1. Automatic data type conversions Value Context in which value is used String Number Boolean Object Undefined value "undefined" NaN false Error null "null" 0 false Error Table 11-1. Automatic data type conversions Value Context in which value is used String Number Boolean Object Nonempty string As is Numeric value of string or NaN true String object Empty string As is 0 false String object 0 "0" As is false Number object NaN "NaN" As is false Number object Infinity "Infinity" As is true Number object Negative infinity "-Infinity" As is true Number object Any other String value of true Number number number As is object true "true" 1 As is Boolean object false "false" 0 As is object Boolean Object toString( ) valueOf( ) or toString( ) or NaN true As is 11.1.1 Object-to-Primitive Conversion Table 11-1 specifies how JavaScript objects are converted to primitive values. Several additional discussion, however. First, note that whenever a non-null context, it converts to true. This is true for all objects (including all arrays and functions), even wrapper objects that represent the following objects convert to true when used in a boolean context: details of this conversion require object is used in a boolean primitive values that convert to false. For example, all of [1] [1] Note, though, that in JavaScript 1.1 and 1.2 these objects all convert to false, which is ECMAScript compliant. new Boolean(false) // Internal value is false, but object converts to true new Number(0) new String("") new Array( ) Table 11-1 shows that objects are converted to num method of the object. Most objects inherit the defau bers by first calling the valueOf( ) lt valueOf( ) method of Object, which simply returns the object itself. Since the default valueOf( ) method does not return a primitive value, JavaScript next tries to convert the object to a number by calling its toString( ) method and converting the resulting string to a number. s empty string, which (as you can see in the table) converts to the number zero! Also, if an array has a single element that is a number n, the array converts to a string representation t, NaN. [2] This leads to interesting results for arrays. Recall that the toString( ) method of array converts the array elements to strings, then returns the result of concatenating these strings, with commas in between. Therefore, an array with no elements converts to the of n, which is then converted back to n itself. If an array contains more than one elemen or if its one element is not a number, the array converts to [2] Note, however, that in JavaScript 1.1 and 1.2, when an array is used in a numeric context it is converted to its length. Table 11-1 specifies how an object is converted when used in a string context and how it is converted when used in a numeric context. However, there are a couple of places in JavaScript where the context is ambiguous! The + operator and the comparison operators (<, <=, >, and >=) operate on both numbers and strings, so when an object is used with one of these operators, it is not clear whether it should be converted to a number or a string. In most cases, JavaScript first attempts to convert the object by calling its e case, JavaScript then tries to convert the object to a string by calling its toString( ) d with the + toString( ) because Date has both toString( ) and valueOf( ) methods. When a Date is used with +, you almost always want to perform a string concatenation. But when using a Date with the comparison operators, you almost always want to perform a numeric comparison to determine which of two times is earlier than the other. Most objects either don't have valueOf( ) methods or don't have valueOf( ) methods that return useful results. When you use an object with the + operator, you usually get valueOf( ) method. If this method returns a primitive value (usually a number), that value is used. Often, however, valueOf( ) simply returns the unconv rted object; in this method. There is only one exception to this conversion rule: when a Date object is use perator, conversion is performed with the method. This exception exists o string concatenation rather than addition. When you use an object with a comparison operator, you usually get string comparison rather than numeric comparison. An object that defines a custom valueOf( ) method may behave differently. If you define a valueOf( ) method that returns a number, you can use arithmetic and other operators with your object, but adding your object to a string may not behave as you expect: the toString( ) method is no longer called, and a string representation of the number returned by valueOf( ) is concatenated to the string. Finally, remember that valueOf( ) is not called toNumber( ): strictly speaking, its job is to convert an object to a reasonable primitive value, so some objects may have valueOf( ) t return strings. methods tha 11.1.2 Explicit Type Conversions Table 11-1 listed the automatic data type conv possible to explicitly convert values from one ersions that JavaScript performs. It is also type to another. JavaScript does not define a cast operator as C, C++, and Java do, but it does provide similar facilities for converting g( e type. For example, you could convert any value x to a string with String(x) ject(y). here are a few other tricks that can be useful for performing explicit conversions. To convert a value to a string, concatenate it with the empty string: var x_as_string = x + ""; To force a value to a number, subtract zero from it: var x_as_number = x - 0; And to force a value to boolean, use the ! operator twice: rt data to whatever type is required, explicit conversions are usually unnecessary hey are occasionally helpful, and more precise. 11.1.3 Converting Numbers to Strings ed in JavaScript. Although it usually happens automatically, there are a couple of useful ways to explicitly data values. As of JavaScript 1.1 (and the ECMA-262 standard), Number( ) , Boolean( ), Strin ), and Object( ) may be called as functions as well as being invoked as constructors. When invoked in this way, these functions attempt to convert their arguments to the appropriat and convert any value y to an object with Ob T var x_as_boolean = !!x; Because of JavaScript's tendency to automatically conve . T however, and can also be used to make your code clearer The number-to-string conversion is probably the one most often perform perform this conversion. We saw two of them above: var string_value = String(number); // Use the String( ) constructor var string_value = number + ""; // Concatenate with the empty string Another technique for converting numbers to strings is with the toString( ) method: The toString( ) method of the Number object (primitive numbers are converted to nal argument that fy the argument, the conversion is done in base 10. But you can also convert numbers in other bases (between 2 and 36). [3] as a function string_value = number.toString( ); Number objects so that this method can be called) takes an optio specifies a radix, or base, for the conversion. If you do not speci For example: [3] Note that the ECMAScript specification supports the radix argument to the toString( ) method, but it allows the method to return an implementation-defined string for any radix other than 10. Thus, conforming implementations may simply ignore the argument and always return a base-10 result. In practice, implementations from Netscape and Microsoft do honor the requested radix. var n = 17; binary_string = n.toString(2); // Evaluates to "10001" octal_string = "0" + n.toString(8); // Evaluates to "021" hex_string = "0x" + n.toString(16); // Evaluates to "0x11" A shortcoming of JavaScript prior to JavaScript 1.5 is that there is no built-in way to convert a number to a string and specify the number of decimal places to be included, or to specify whether exponential notation should be used. This can make it difficult to display numbers that have traditional formats, such as numbers that represent monetary values. ECMAScript v3 and JavaScript 1.5 solve this problem by adding three new number-to- string methods to the Number class. toFixed( ) converts a number to a string and displays a specified number of digits after the decimal point. It does not use exponential notation. toExponential( ) converts a number to a string using exponential notation, with one digit before the decimal point and a specified number of digits after the decimal point. toPrecision( ) displays a number using the specified number of significant digits. It uses exponential notation if the number of significant digits is not large enough to display the entire integer portion of the number. Note that all three of these methods round the trailing digits of the resulting string as appropriate. Consider the following examples: var n = 123456.789; n.toFixed(0); // "123457" n.toFixed(2); // "123456.79" n.toExponential(1); // "1.2e+5" n.toExponential(3); // "1.235e+5" n.toPrecision(4); // "1.235e+5" n.toPrecision(7); // "123456.8" var number = string_value - 0; The trouble with this g-to-number conversion is strict. It works only with base- s, and although it does allow leading and trailing spaces, it does not s t To allow m s, you can . These func rn any num of a string, ignoring any trailing no s o s both intege parseInt( 11.1.4 Converting Strings to Numbers We've seen that strings that represent numbers are automatically converted to actual numbers when used in a numeric context. As shown earlier, we can make this conversion explicit: var number = Number(string_value); sort of strin 10 number that it is overly allow any non-space character ore flexible conversion tions convert and retu o appear in the string following the number. use parseInt( ) and parseFloat( ) ber at the beginning n-numbers. parseInt( ) parse rs and floating-point numbers. If ) interprets it as a hexadecimal nly integers, while parseFloat( ) parse a string begins with "0x" or "0X", number. [4] For example: (but not "0x" or "0X"), parseInt( ) may parse it as an octal [4] The ECMASc ays that if a string begins with "0" number or as a de unspecified, y leading zeros, un parseInt( Ret parseFloa 4 meters"); // Ret arseInt("12.34"); // Returns 12 arseInt("0xFF"); // Returns 255 parseInt( ) can even take a second argument specifying the radix (base) of the number to be parsed. Legal values are between 2 and 36. For example: parseInt("11", 2); // Returns 3 (1*2 + 1) parseInt("ff", 16); // Returns 255 (15*16 + 15) parseInt("zz", 36); // Returns 1295 (35*36 + 35) parseInt("077", 8); // Returns 63 (7*8 + 7) parseInt("077", 10); // Returns 77 (7*10 + 7) If parseInt( ) or parseFloat( ) cannot convert the specified string to a number, it returns NaN: parseInt("eleven"); // Returns NaN parseFloat("$72.47"); // Returns NaN ript specification s cimal number. Because the behavior is less you explicitly specify the radix to be used! "3 blind mice"); // t("3.1 ou should never use parseInt( ) to parse numbers with urns 3 urns 3.14 p p 11.2 By Value Versus by Reference ou ons are performed in that language. e data values. These techniques is the e and d the is the datum is passed to the function; if the um, the two distinct pieces of data must represent exactly the same value (which only y of the value; references to that value are manipulated. In JavaScript, as in all programming languages, there are three important ways that y can manipulate a data value. First, you can copy it; for example, by assigning it to a new variable. Second, you can pass it as an argument to a function or method. Third, you can compare it with another value to see if the two values are equal. To understand any programming language, you must understand how these three operati There are two fundamentally distinct ways to manipulat are called "by value" and "by reference." When a value is manipulated by value, it lue of the datum that matters. In an assignment, a copy of the actual value is madva that copy is stored in a variable, object property, or array element; the copy an original are two totally independent valu ssed by value to a function, a copy of es that are stored separately. When a datum pa function modifies the value, the change affects only the function's copy of the datum -- it does not affect the original datum. Finally, when a datum is compared by value to another dat usually means that a byte-by-byte comparison finds them to be equal). The other way of manipulating a value is by reference. With this technique, there is one actual cop [5] If a value is references to it. It is these references that are copied, passed, and compared. So, in an assignment made by reference, it is the reference to the value that is assigned, not a copy s to the to. Both references are equally valid and both ca the value -- if the value is changed through one reference, that e situation is similar when a value are visible outside the function. Finally, when a value is compared to another by [5] C programmers and anyone else familiar with the concept of pointers should understand the idea of a reference in this context. Note, however, that JavaScript does not support pointers. manipulated by reference, variables do not hold that value directly; they hold only of the value and not the value itself. After the assignment, the new variable refer same value that the original variable refers n be used to manipulate change also appears through the original reference. Th is passed to a function by reference. A reference to the value is passed to the function, and the function can use that reference to modify the value itself; any such modifications reference, the two references are compared to see if they refer to the same unique copy of a value; references to two distinct values that happen to be equivalent (i.e., consist of the same bytes) are not treated as equal. These are two very different ways of manipulating values, and they have important implications that you should understand. Table 11-2 summarizes these implications. This discussion of manipulating data by value and by reference has been a general one, but the distinctions apply to all programming languages. The sections that follow explain how these distinctions app ly specifically to JavaScript; they discuss which data types are manipulated by value and which are manipulated by reference. Table 11-2. By value versus by reference By value By reference Copy The value is actually copied; there are two distinct, Only a reference to the value is copied. If the value is modified through the new reference, that change is also visible through the independent copies. original reference. Pass to it have no effect outside the A reference to the value is passed to the value through the passed reference, the . A distinct copy of the value is passed to the function; changes function. If the function modifies the function. modification is visible outside the function Compare to see if they are the same distinct values are not equal, even if the two Two distinct values are compared (often byte by byte) Two references are compared to see if they refer to the same value. Two references to value. values consist of the same bytes. 11.2.1 Primitive Types and Reference Types itive types are manipulated by value, and erence types, as the name suggests, are manipulated by reference. Numbers and booleans are primitive types in JavaScript -- primitive because they consist of nothing types. Arrays and functions, which are specialized types of objects, are therefore also ry numbers of properties or elements, by value, as this could involve the inefficien d comparing of large amounts of should be reference types. In fact, though, they are usually considered to be primitive types in JavaScript simply because they are not objects. Strings don't actually fit into the primitive versus reference type dichotomy. We'll have more to say about strings and their Th f e basic rule in JavaScript is this: prim re more than a small, fixed number of bytes that are easily manipulated at the low (primitive) levels of the JavaScript interpreter. Objects, on the other hand, are reference reference types. These data types can contain arbitra so they cannot be manipulated as easily as fixed-size primitive values can. Since object sense to manipulate these typesand array values can become quite large, it doesn't make t copying an memory. What about strings? A string can have an arbitrary length, so it would seem that strings behavior a little later. The best way to explore the differences between data manipulation by value and by reference is through example. Study the following examples carefully, paying attention to the comments. Example 11-1 copies, passes, and compares numbers. Since numbers are primitive types, this example illustrates data manipulation by value. Example 11-1. Copying, passing, and comparing by value // First we illustrate copying by value var n = 1; // Variable n holds the value 1 var m = n; // Copy by value: variable m holds a distinct value 1 // Here's a function we'll use to illustrate passing by value // As we'll see, the function doesn't work the way we'd like it to function add_to_total(total, x) { y of umbers contained in n and m by value. The value of n is copied, and that copied value is named total py of m to that copy of n. But adding affect the original value of n ad _to_total(n, m); Now, we'll look at comparison by value. / In the following line of code, the literal 1 is clearly a distinct are it to the value held in // n. In comparison by value, the bytes of the two numbers are checked to // see if they are the same. if (n == 1) m = 2; // n contains the same value as the literal 1; m is now 2 Now, consider Example 11-2 total = total + x; // This line changes only the internal cop total } // Now call the function, passing the n // within the // function. The function adds a co // something to a copy of n doesn't outside // of the function. So calling this function doesn't accomplish ything.an d // / numeric // value encoded in the program. We comp variable . This example copies, passes, and compares an object. Since objects are reference types, these manipulations are performed by reference. This example uses Date objects, which you can read more about in the core reference section of this book. Example 11-2. Copying, passing, and comparing by reference th variables now refer to the same object as well // Here we create an object representing the date of Christmas, 2001 // The variable xmas contains a reference to the object, not the object itself var xmas = new Date(2001, 11, 25); // When we copy by reference, we get a new reference to the original object var solstice = xmas; // Bo value // Here we change the object through our new reference to it solstice.setDate(21); // The change is visible through the original reference, xmas.getDate( ); // Returns 21, not the original value of 25 the function can change the contents of the array through totals[1] = totals[1] + x; totals[2] = totals[2] + x; find they are // equal, because they refer to the same object, even though we were Before we leave the topic of manipulating objects and arrays by reference, we need to allows a function to assign new values to its arguments and to have those modified values visible outside t . Here, we mean simply tha passed to a function. A function c ference to erties of th on overwrites the reference with a reference to a new object hat modification is not visible of the function. Readers familiar with the ning of may prefer to say that objects and a e passed by value, a reference rather than the object itself. Example // The same is true when objects and arrays are passed to functions. // The following function adds a value to each element of an array. // A reference to the array is passed to the function, not a copy of the array. // Therefore, // the reference, and those changes will be visible when the function returns. function add_to_totals(totals, x) { totals[0] = totals[0] + x; } // Finally, we'll examine comparison by reference. the two variables defined above, we// When we compare trying / to make them refer to different dates: / (xmas == solstice) // Evaluates to true // The two variables defined next refer to two distinct objects, both // of which represent exactly the same date. var xmas = new Date(2001, 11, 25); var solstice_plus_4 = new Date(2001, 11, 25); // But, by the rules of "compare by reference," distinct objects are not equal! (xmas != solstice_plus_4) // Evaluates to true clear up a point of nomenclature. The phrase "pass by reference" can have several meanings. To some readers, the phrase refers to a function invocation technique that he function. This is not the way the term is used in this book t a reference to an object or array -- not the object itself -- is an use the re modify prop e object or elements of the array. But if the functi or array, t outside other mea but the value that is passed is actually this term rrays ar 11-3 illustrates th // This is another version of the add_to_totals( ) function. It doesn't // work, though, because instead of changing the array itself, it tries is issue. Example 11-3. References themselves are passed by value to // change the reference to the array. function add_to_totals2(totals, x) [...]... rather than an array containing x as its only element When splice( ) does not remove any elements from the array, it returns nothing instead of returning an empty array When String.substring( ) is called with a starting position greater than its ending position, it returns the empty string rather than correctly swapping the arguments and returning the substring between them The String.split( ) method displays... consider a function g defined within a function f g is always executed in the scope of f Its scope chain includes three objects: its own call object, the call object of f( ), and the global object Nested functions are perfectly understandable when they are invoked in the same lexical scope in which they are defined For example, the following code does not do anything particularly surprising: var x = "global";... of all properties defined by the object, returning a string formatted using object literal syntax The Array.toString( ) method separates array elements with a comma and a space, instead of just a comma, and returns the list of elements within square brackets In addition, string elements of the array are quoted, so that the result is a string in legal array literal syntax When a single numeric argument... is no JavaScript syntax, method, or property that allows you to change the characters in a string Since strings are immutable, our original question is moot: there is no way to tell if strings are passed by value or by reference We can assume that, for efficiency, JavaScript is implemented so that strings are passed by reference, but in actuality it doesn't matter, since it has no practical bearing on... newtotals; // This line has no effect outside of the function } 11.2.2 Copying and Passing Strings As mentioned earlier, JavaScript strings don't fit neatly into the primitive type versus reference type dichotomy Since strings are not objects, it is natural to assume that they are primitive If they are primitive types, then by the rules given above, they should be manipulated by value But since strings can be... write 11.2.3 Comparing Strings Despite the fact that we cannot determine whether strings are copied and passed by value or by reference, we can write JavaScript code to determine whether they are compared by value or by reference Example 11-4 shows the code we might use to make this determination Example 11-4 Are strings compared by value or by reference? // Determining whether strings are compared... run in the scope in which they are defined, not the scope from which they are executed Prior to JavaScript 1.2, functions could be defined only in the global scope, and lexical scoping was not much of an issue: all functions were executed in the same global scope (with the call object of the function chained to that global scope) In JavaScript 1.2 and later, however, functions can be defined anywhere,... lexical scoping work with nested functions, a JavaScript implementation must use a closure, which can be thought of as a combination of a function definition and the scope chain that was in effect when the function was defined 11.5 The Function( ) Constructor and Function Literals As we saw in Chapter 7, there are two ways to define functions other than the basic function statement As of JavaScript. .. reference stored in another variable or passed to a function When we set out to write the code to perform this experiment, however, we run into a major stumbling block: there is no way to modify the contents of a string The charAt( ) method returns the character at a given position in a string, but there is no corresponding setCharAt( ) method This is not an oversight JavaScript strings are intentionally... modern JavaScript implementations use some kind of mark-and-sweep garbage collection However, JavaScript 1.1, as implemented in Netscape 3, used a somewhat simpler garbage-collection scheme that has some shortcomings If you are writing JavaScript code to be compatible with Netscape 3, the following section explains the shortcomings of the garbage collector in that browser Netscape 2 used an even simpler . appear in the string following the number. use parseInt( ) and parseFloat( ) ber at the beginning n-numbers. parseInt( ) parse rs and floating-point numbers any num of a string, ignoring any trailing no s o s both intege parseInt( 11.1.4 Converting Strings to Numbers We've seen that strings that represent

Ngày đăng: 05/10/2013, 12:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan