JavaScript: The Definitive Guide, Fifth Edition by David Flanagan The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated April 8, 2008. UNCONFIRMED errors and comments from readers: (36) 3.6.1; "var a = new Array(10); creates a new array with 10 undefined elements. " Would that not be 11 elements? {49} Section 4.1, safari; In Java 1.5 with autoboxing the following code is legal, Object o = null; o = 10; o = "hey"; Autoboxing is overlooked in section 4.1, Variable Typing [52] 4.3.1 No Block Scope, 2nd paragraph and concept in general; The second code example that follows "The rule that all variables declared... can cause surprising results. The following code illustrates this:" var scope = "global"; function f() { alert(scope); // Displays "undefined", not "global" ... } f(); --------------------- This code does in fact display the word "global" in an alert window (IE7, FF2). It seems to me that if you couldn't reference variables outside functions by name you wouldn't actually have 'global' variables...? [52] At the bottom of the page; There is in fact NO ERROR in the last code segment on page 52 even though the "Unconfirmed error reports and comments from readers" shows that someone has claimed otherwise. I've tested the example as given in the book and the behaviour exactly matches the one indicated in the code comments and the description around the example code. (54) 3rd paragraph from bottom of page, last sentence; "there just happens to be two references to it." Change "happens" to "happen" {65} 1st full paragraph; [ For example, the following code sets both i and j to 2: i = 1; j = ++i; But these lines set i to 2 and j to 1: i = 1; j = i++; ] No, the first two lines set i to 1 and j to 2. The second two lines set both i and j to 1. [65,140] upper half; I've been checking the page "Unconfirmed error reports and comments from readers". Apart from the one remark I sent earlier here are my other findings: The errata claims that the text on the page 65 of the book: For example, the following code sets both i and j to 2: i = 1; j = ++i; But these lines set i to 2 and j to 1: i = 1; j = i++; is in error. It isn't. Instead it correctly describes how these expressions behave. The behavior is the same in C/C++, Java, JavaScript and I'm sure in some other languages too. {114} para 6; line: var undefs = [,,]; .... comment should say "An array with 3 elements, all undefined" {140} Example 8-3; In the function copyUndefinedProperties(), "if (!p in to)" is used to check whether the object "to" lacks property "p". Since the unary ! operator has a higher precendence than the "in" operator (see Table 5-1, pp. 60-61), this statement is incorrect: p is first negated to obtain !p, and then it is checked whether !p is a property of to. Parentheses are needed: "if (!(p in to))". {140} first whole function (copyUndefinedProperties); Because the ! operator has higher precedence than the in operator, the function just checks to see if "false" is in the to object for each property in the from object. Function should be: function cu(from, to) { for(p in from) { if (!(p in to)) to[p] = from[p]; // parentheses around 'p in to' } } (141) code just before section 8.8; The code could be fleshed out a bit more to illustrate the idea that 'this' is a keyword never bound to any particular call object. Yes, that info is on p 137 at the end of 8.4, but by this page that info is a bit fuzzy and still abstract. Perhaps all of example 8-5 needs to be in section 8.8.4.1. function makePropertiesInThis ( arg1, arg2, arg3 ) { for ( var i=0; i < arguments.length; i++ ) { this[ 'js' + i ] = arguments[i]; } return this; } o = {}; o.func = bindArguments( makePropertiesInThis, 'foo', 'bar' ); x = o.func( 'baz' ); // print is defined as earlier in the book print( x === o ); // true print( x === this ); // false print( "js0" in o ); // true, foo print( "js1" in o ); // true, bar print( "js2" in o ); // true, baz print( "js0" in this ); // false print( "js1" in this ); // false print( "js2" in this ); // false x = (bindArguments(makePropertiesInThis, 'apple'))('banana','cherry'); print( x === o ); // false print( x === this ); // true print( "js0" in this ); // true, apple print( "js1" in this ); // true, banana print( "js2" in this ); // true, cherry (143) first sentence; The correction at http://www.oreilly.com/catalog/jscript5/errata/jscript5.807 is incorrect; the sentence was correct before. The overall structure of the sentence is "The facts...interact". The sentence is describing three facts which interact. So it should be "facts" and not "fact". (145) Example 8-6; In Example 8-6, the "get" and "set" methods are defined, but their comments have their names confused ("getter" and "setter" should be switched). The text reads: // The setter method simply returns the value. o["get" + name] = function() { return value; }; // The getter method stores the value or throws an exception if // the predicate rejects the value. o["set" + name] = function(v) { if (predicate && !predicate(v)) {157 (4th ed.)} last paragraph (4th ed).; In Section 10.2, page 157, 4th ed., there is an example for parsing a URL using String.match(). In the last paragraph there is an off-by-one error in the description of the index property of the result: AS IS: "So, in the previous code, the value of the result.index property would be 21, since the matched URL begins at character position 21 in the text." SHOULD BE: "So, in the previous code, the value of the result.index property would be 22, since the matched URL begins at character position 22 in the text." {158} first paragraph; "When you use JavaScript objects to *simulate* object-oriented programming techniques ..." [emphasis added] maybe should be something like: "When you use JavaScript objects to simulate the object-oriented programming techniques of strictly-typed languages ... " On page 157: "JavaScript is a true object-oriented language." If that's true, we shouldn't need to simulate object-oriented programming techniques. If we do need to simulate them, then JavaScript must not be a true object-oriented language. [161] 3rd paragraph (the creation of an 'instance method'); INSTANCE/CLASS METHOD CONFUSION (5th Addition Rhino Book) The definition of Circle.prototype.area = function() { etc.... } is described as an instance method, which would be a function which is present in all instances of a Circle, but could POTENTIALLY be changed without affecting other instances. eg. for these two objects - var a = new Circle(2); var b = new Circle(6); // I could do this..... b.area = function() { return 1; } I could redefine the 'area' function for 'a' but it would NOT be changed for 'b' surely? However, by defining the 'area' function as part of the prototype, that would change the function for ALL Circle instances. Therefore it is a CLASS method, not an INSTANCE method. 'area' should be defined in the constructor, just like INSTANCE PROPERTIES are. function Circle(radius) { this.r = radius; this.area = function() { return (Circle.PI * this.r * this.r); } } // and something more likely used for a class function using prototype, like: Circle.prototype.getNumberOfSides() { return 1; } (although, Circle.getNumberOfSides() is an infinitely better way!) I find the sections from 9.3.1 - 9.3.5 very confusing to read as an experienced C++ developer, as there seems to be some fuzziness in the thinking here. Also in 9.3.2.1 you say the same thing, that an instance method can be defined via the prototype, but this is just plain wrong! ps. I am using the latest firefox, so I hope it's not a bug in it's interpreter! Page 161: The comment says that 9.3 is very confusing and in fact partly wrong. It isn't. Flanagan is just telling how things are done in JavaScript. [166] last paragraph; the "new" operator doesn't belong there. here's the output from jslint: Error: Problem at line 1 character 21: Weird construction. Delete 'new'. complexNumbers.sort(new function(a,b) { return a.compareTo(b); }); Problem at line 1 character 65: Missing '()' invoking a constructor. complexNumbers.sort(new function(a,b) { return a.compareTo(b); }); Members Occurrences compareTo 1 sort 1 {166} last line; Remove the 'new' keyword. (170) sections 9.5.1 and 9.5.2; The implementation provided for simulating "super" (via the "superclass" property) does not work when more levels of class hierarchy are used than shown. For example, if Class C extends B, and Class B extends A, then when the constructor B referenced "this.superclass" (expecting a reference to "A"), it would instead get "B" because the "this.superclass" defined by C would have overridden/obscured it. (176) Footnote in section 9.7.3; The footnote begins: 'The term "duck typing" has been popularlized' 'popularlized' should be 'popularized' (203) end of page; The following statement confuses some readers (including me at first): "The non greedy version of our pattern does match at the first character of the string, so this match is returned; matches at subsequent characters are never even considered". The "does match at the first character... so this match is returned" induces people to think "the match is the first character?". I suggest the following formulation: -- The non greedy version of our pattern does match starting from the first character of the string, so the complete match is the one retained; matches starting at subsequent characters are never even considered after the first one. In other words, the match is "leftmost" ("leftmost longest" for greedy, or "leftmost shortest" for non-greedy, but always "leftmost"!). -- (I borrowed the "leftmost" concept from "Programming Perl".) [203] Below Table 11-3 in Chapter 11; Regarding regular expressions, the following text: /\w{3}\d?/ // Match exactly three word characters and an optional digit is incorrect. This regular expression will match 'AAA3' like it is supposed to. But it will also match 'AAAA3'. Since there are four A's, this regular expression does not "match exactly three word characters and an optional digit." Page 203 (regular expressions): This states that the text in the book: "/\w{3}\d?/ // Match exactly three word characters and an optional digit" is incorrect while it isn't. Maybe the wording isn't clear in the original comment. I'm not capable of discussing that because I'm not a native speaker of English. But the claim that the comment is wrong because of the fact that the regexp matches "AAAA3" i.e. 4 letters followed by a digit is clearly wrong. The regexp produces a match but that match does not match the whole of the string "AAAA3" it only matches the first 3 letters. This can clearly be seen with the following code that uses different letters instead of A's only: y = "ABCD3".match(/\w{3}\d?/); alert(y); // shows ABC y = "ABC3D".match(/\w{3}\d?/); alert(y); // shows ABC3 {204} 3rd paragraph, 4th sentence; It looks like there's an extra closing parentheses in "/(ab|cd)+|ef)/" (218) 2nd bullet in section 12.1.1; The pattern in the bullets is Java -> JavaScript but the second bullet reads: * All java.lang.Number objects convert to Java numbers. I believe it should read: * All java.lang.Number objects convert to JavaScript numbers. (232) 2nd; I cannot get the example 14-1 and 14-2 to work. I am using IE7 and checked line by line. Could there be a problem in the code? {275} 4th paragraph, explanation of viewport (also innerwidth/innerheight p 913); your definition of 'viewport' is (as it should be) the window minus menus, scrollbars, etc. your code works as advertised in the browsers OmniWeb and Safari on MacOS X Leopard. with the browsers Camino, Firefox, Mozilla, Navigator, SeaMonkey (all on MacOS X) and Internet Explorer 7 (on Win XP) your code returns the width of a frame including the scrollbar. Specifically on Safari, the proper width is the 'computed style width' of the body; however, this value appears to be 'auto' in the other browsers. [276] Also first "if" statement on page.; If not an error, this is something that should be pointed out to the reader... if(window.screenLeft) evaluates to FALSE, even with IE, if the value of screenLeft is zero. So using this code with a window that opens full-screen, for example, results in an undefined function error. (276) line 60 in the file js5examples/14/Geometry.js in ; the book correctly says if (document.documentElement && document.documentElement.scrollWidth) { but the file contains if (document.documentElement && document.documentElemnet.scrollWidth) { (277) line 60 of provided file 14/Geometry.js; The book is correct, but the code provided has a typo in line 60: if (document.documentElement && document.documentElemnet.scrollWidth) { the second documentElement is misspelled. (291) last paragraph; In the 3rd line from the bottom of page 291 there is the code line Window.open() instead of window.open() All through this chapter 14 which I am currently reading, I find the expression "the Window object" ("window" with capital W and in Roman) where I would have expected to see "the window object" (that is "window" written with small w and in Constant Width). (323) First comment section of code; The line that says * element is looked up using getElementsById() should say * element is looked up using getElementById() There is no getElementsById() method. {332} fifth line of code; Example 15-9 table.appendChild(row); is not a good idea for Internet Explorer -- wants rows appended to tbody (or thead or tfoot) Firefox and Safari happy as clams I notice the screen shot (Figure 15-4) is for Firefox I admit I didn't use this script, but I can tell you that I had no luck directly appending rows to a table in MSIE. (Nothing displayed.) Cured by appending to tbody and then appending tbody to table. Found the cure by Googling. (335) just at the beginning of the function 'make'; See how the function begins: function make(tagname, attributes, children) { // If we were invoked with two arguments the attributes argument is // an array or string, it should really be the children arguments. The comment does not 'really' make sense (English and programming logic wise). I think that it should be rewritten as: function make(tagname, attributes, children) { // If we are invoked with two arguments and the 'attributes' argument is // an array or string, we assume that the second argument is intended // to be the 'children' arguments (ie: the real 'attributes' is missing). // This allows users not to type a null argument in this case. [361] first sample code for Section 16.2.3; The algorithm provided for determining element position fails in some circumstances, at least with Internet Explorer 6. With tables, there is not the expected containment hierarchy of DIV, DIV, TABLE, TBODY, TR, TD, SPAN. Instead, both TR and TD act as though TBODY were their container with respect to position; they provide the same vertical offset. TBODY may also show a vertical offset for a header row when no header is displayed. Either the recipe should be improved, or readers should be warned of its limitations. [380] middle; In the discussion about Computed Styles on page 347 of the fourth edition you discuss getComputedStyle() as a property of document.defaultView. On page 380 of the fifth edition you have dropped this. Unfortunately Safari doesn't have window.getComputedStyle(). Safari does have document.defaultView.getComputedStyle(). This is the way I access the function and the Yahoo! UI library does the same. (395) 3rd paragraph from bottom; The text says "The second column of Table 17-1 describes what happens when you return false." The second column of Table 17-1 actually describes what happens when the event is triggered, and the column describing what happens when the handler returns false appears to have been elided. {403} 17.2.4 "Registering Objects as Event Handlers", second paragraph; This code block is missing a comma at the end of the third line. It should read: function(event) { listener.handleEvent(event); }, [408] bottom; I'm not sure this is serious, but in this page *relatedTarget* is defined as returning a *Node* whereas in page 858 it's defined as returning an *Element*. in a book by another publisher, it mentions that Mozilla can return a text node as a relatedTarget, which is consistent with returning a Node instead of an Element. your book would be more *definitive* if you cleared up this inconsistency. (411,412,479,503) various; The changes made in the 08/07 printing used the literal tag syntax to designate code segments in paragraphs. These tags are present in the 01/08 printing of the book when they should have been removed before printing. {433} Very first line (not including the one-line header); The comment is wrong, according to the code. Instead of // Firefox gives us printing keys in e.charCode, IE in e.charCode It should be: // Firefox gives us printing keys in e.charCode, IE in e.keyCode [453] Three-quarters down the code on the page; The comment of the Validate.js code claims that the anonymous function does not define any symbols in the global namespace. The code has an error that causes one of the variables to be defined in the global namespace. for (j = 0; j < f.elements[j]; // the element we're working on The above line should properly declare the j variable for (var j = 0; j < f.elements[j]; // the element we're working on (461) example 19-1, 4th line; current: var cookie = new Cookie("vistordata"); proposed: var cookie = new Cookie("visitordata"); [462-463] function Cookie; document.cookie value on FireFox includes a blank space after ';'. As a result, the code to extract a particular cookie value doesn't work: var cookies = allcookies.split(';'); var cookie = null; for(var i = 0; i < cookies.length; i++) { // Does this cookie string begin with the name we want? if (cookies[i].substring(0, name.length+1) == (name + "=")) { cookie = cookies[i]; break; } } This can be easily fixed by first replacing all blanks with zero length strings, as in: var cookies = allcookies.replace(" ", "").split(';') {462} near bottom; As reported by a previous person the Cookie function has a problem on Firefox because of a space that is added after the semicolon to split the cookies. Thus this line: var cookies = allcookies.split(';'); Leaves a space after the split and thus doesn't work correctly when looking up cookie names. The previous errata reporter said this could be fixed like so: var cookies = allcookies.replace(' ', '').split(';'); This is not correct either because the replace needs to be global, not just for the first match: var cookies = allcookies.replace(/ /g, '').split(';'); [464] middle; It seems that neither IE 6 or Safari 2.0.4 support the max-age attribute for cookies. This means that the example 19.2 in your book cannot delete cookies in these mainstream browsers. This occurs in the enabled() method as well. I am reverting to the expires attribute. {464, 465} Throughout; The anonymous functions that are defined should end with a semicolon. Currently the books shows them as Cookie.prototype.store = function(daysToLive, path, domain, secure) { ... } when they should be Cookie.prototype.store = function(daysToLive, path, domain, secure) { ... }; There are three such errors across pages 464 and 465. The first is halfway down page 464, the second is just above the comment block at the bottom of page 464, and the third is at the end of the first code section on page 465. (480) middle; In the list of possible factories, could add another one: function() { return new ActiveXObject("MSXML2.XMLHTTP.3.0"); } The Yahoo! UI tests for this as the first option for IE. {481} At end of code sample 20-1; At the end of the code example (20-1) there is no closing semicolon. This has been copied into the http.js file supplied as part of this chapter. All other functions in this file have closing semicolons. [481] middle; "By default, the open() method sets up an asynchronous XMLHttpRequest' This isn't true with all browsers. If the third argument is omitted some browsers default to synchronous. Hence the third argument has to be included all the time if the programmers wants control of whether the request is sync or async. [486] 20.2.1 Basic Get Utilities; In HTTP.getText function, the request variable will drop out of scope once the function exits. The garbage collector might clean up the object before onreadystatechange is called [491] top; eval(request.responseText); Should be eval('(' + request.responseText + ')'); (513) line of code in middle of page; Should the xsl definition not define an xsl file, rather than an xml file? Should it not read ?? {517} RegExp.test Example; The example says: var pattern = /java/i; pattern.test("JavaScript"); // Returns true pattern.test("ECMAScript"); // Returns false But the second test returns false not because it doesn't contain the pattern, but because of the side effect on the lastIndex in the pattern regular expression. There should be a mention of this side effect on lastIndex in the Description section for test, and reference exec and lastIndex for more complete description. This *dangerous* side effect should be mentioned also in String.match, .search, .replace, and any other place that applies. [520] 21.6 paragraph 2, first sentence; Example 21-12* works in IE only, not in Firefox. Book states: "This section applies the XML techniques seen earlier in the chapter and uses XPath and the DOM to create an improved templating facility that works in IE and Firefox." *12.html and xml.js run on Web server, accessed by IE and Firefox (latest Firefox version available as of January 22, 2007) [526] 4th row from bottom; the object XML is not declared {537} example 22.4; The ImageLoop constructor function sets the image load event for each image before the event function is ready. The result is that the event handler can fail if the images have been cached. What really matters is the "var loop = this;" line. If the browser loads the image in a separate thread the event handler can fire and call countLoadedFrames() before that line is executed. I see this most often with IE7. The fix is to move the event handler code before the loop that processes the image URLs: // This nested function is an event handler that counts how many // frames have finished loading. When all are loaded, it sets a flag, // and starts the animation if it has been requested to do so. var loop = this; function countLoadedFrames( ) { loop.loadedFrames++; if (loop.loadedFrames == loop.frames.length) { loop.loaded = true; if (loop.startOnLoad) loop.start( ); } } // Initialize the frames[] array and preload the images for(var i = 0; i < frameURLs.length; i++) { this.frames[i] = new Image( ); // Create Image object // Register an event handler so we know when the frame is loaded this.frames[i].onload = countLoadedFrames; // defined above this.frames[i].src = frameURLs[i]; // Preload the frame's image } [537] 4TH EDITION The statement var loop = this; should come before (not after) the for statement initializing the frames[] array. The countLoadedFrames() function could be executed directly. If loop is declared after the for statement, I get an error: variable loop undefined. (551) End of Example 22-8; missing final closing '}' at the end of the example {667} "Returns" section of Math.random(); The book says that Math.random() returns "A pseudorandom number between 0.0 and 1.0." Section 15.8.2.14 of ECMA-262 says it "Returns a number value with positive sign, greater than or equal to 0 but less than 1". That "less than 1" can be of significance, since Math.floor(Math.random() * N) where N is an integer might return N itself; otherwise, we can safely assume that expression will return an integer from 0 to N-1. {708} in Bugs section for String.substr(); The Bugs section for String.substr() states in part, "Negative values for start do not work in IE 4 (this is fixed in later versions of IE)." However, I do not think that this problem has ever been fixed in IE. I just tried it in IE 7 and it's still broken. {833} The Image object title block; The Inherits from section should presumably end with Image rather than Input But is the inheritance chain correct otherwise?? {834} Section entitled "HTML Syntax"; The Image object is described as having onload, onerror, and onabort event handler attributes. Those attributes are not valid (X)HTML for the IMG tag, and the DOM does not indicate that the HTMLImageElement has those properties (e.g. the load event only applies to the body, FRAMESET, and OBJECT elements--see DOM2-Events section 1.6.5). However, it appears most browsers support these attributes. [841] Paragraph on Input.click() method.; The book states: Input.click() The click() method is not often useful. Because it does not invoke the onclick event handler, it is not useful to call this method on elements of type "button"; ... -------------- This is a mistake. Try this code. It executes click() method and it fires the onclick() event on button. It works on IE and mozilla firefox:
document.getElementById("mybutton").click(); (858) 4th paragraph from the bottom; The information about the related target properties refers to elements, whereas the last paragraph of page 408 refers to nodes. Both paragraphs are not consistent with each other. As text nodes can be the related target and text nodes are not elements, the paragraph on page 858 should be updated. If it is decided that text nodes should not allowed as a related target, the last paragraph on page 408 should be updated. [876] 4TH EDITION space is missing around HTMLFormElement [896] 4TH EDITION The second argument of the add() method is an index (integer) in IE: try { select.add(theOpt, anOpt); } // standards compliant catch(ex) { select.add(theOpt, indexOfAnOpt); } // IE only [913] innerHeight and innerWidth; innerHeight and innerWidth, under Firefox, are the client area size, including the scrollbars. To have the viewport size, you must use document.documentElement.clientWidth and document.documentElement.clientHeight. Tested on Firefox 2.0. [943] 5 lines from bottom of page; snapshotLength should be readonly long snapshotLength [943] 2 lines from bottom of page; stringValue should be readonly String stringValue