Copyright © 2008 Yahoo! Inc.
O'Reilly Media
For the Lads: Clement, Philbert, Seymore, Stern, and, lest we forget, C. Twildo.
If we offend, it is with our good will That you should think, we come not to offend, But with good will. To show our simple skill, That is the true beginning of our end.
--William Shakespeare, A Midsummer Night's DreamWhen you see a Safari® Books Online icon on the cover of your favorite technology book, that means the book is available online through the O'Reilly Network Safari Bookshelf.
Safari offers a solution that's better than e-books. It's a virtual library that lets you easily search thousands of top tech books, cut and paste code samples, download chapters, and find quick answers when you need the most accurate, current information. Try it for free at http://safari.oreilly.com.
Please address comments and questions concerning this book to the publisher:
...setting the attractions of my good parts aside I have no other charms.
--William Shakespeare, The Merry Wives of WindsorWhen I was a young journeyman programmer, I would learn about every feature of the languages I was using, and I would attempt to use all of those features when I wrote. I suppose it was a way of showing off, and I suppose it worked because I was the guy you went to if you wanted to know how to use a particular feature.
JavaScript is built on some very good ideas and a few very bad ones.
The very good ideas include functions, loose typing, dynamic objects, and an expressive object literal notation. The bad ideas include a programming model based on global variables.
JavaScript's functions are first class objects with (mostly) lexical scoping. JavaScript is the first lambda language to go mainstream. Deep down, JavaScript has more in common with Lisp and Scheme than with Java. It is Lisp in C's clothing. This makes JavaScript a remarkably powerful language.
I know it well: I read it in the grammar long ago.
--William Shakespeare, The Tragedy of Titus AndronicusThis chapter introduces the grammar of the good parts of JavaScript, presenting a quick overview of how the language is structured. We will represent the grammar with railroad diagrams.
The rules for interpreting these diagrams are simple:
A name is a letter optionally followed by one or more letters, digits, or underbars. A name cannot be one of these reserved words:
JavaScript has a single number type. Internally, it is represented as 64-bit floating point, the same as Java's double. Unlike most other programming languages, there is no separate integer type, so 1 and 1.0
A string literal can be wrapped in single quotes or double quotes. It can contain zero or more characters. The \
A compilation unit contains a set of executable statements. In web browsers, each <script> tag delivers a compilation unit that is compiled and immediately executed. Lacking a linker, JavaScript throws them all together in a common global namespace. There is more on global variables in Appendix A.
When used inside of a function, the var statement defines the function's private variables.
The switch, while, for, and do statements are allowed to have an optional label prefix that interacts with the break statement.
The simplest expressions are a literal value (such as a string or number), a variable, a built-in value (true, false, null, undefined, NaN, or Infinity), an invocation expression preceded by new, a refinement expression preceded by delete, an expression wrapped in parentheses, an expression preceded by a prefix operator, or an expression followed by:
Object literals are a convenient notation for specifying new objects. The names of the properties can be specified as names or as strings. The names are treated as literal names, not as variable names, so the names of the properties of the object must be known at compile time. The values of the properties are expressions. There will be more about object literals in the next chapter.
Array literals are a convenient notation for specifying new arrays. There will be more about array literals in Chapter 6.
There will be more about regular expressions in Chapter 7.
A function literal defines a function value. It can have an optional name that it can use to call itself recursively. It can specify a list of parameters that will act as variables initialized by the invocation arguments. The body of the function includes variable definitions and statements. There will be more about functions in Chapter 4.
Upon a homely object Love can wink.
--William Shakespeare, The Two Gentlemen of VeronaThe simple types of JavaScript are numbers, strings, booleans (true and false), null, and undefined. All other values are
Values can be retrieved from an object by wrapping a string expression in a [ ] suffix. If the string expression is a constant, and if it is a legal JavaScript name and not a reserved word, then the .
A value in an object can be updated by assignment. If the property name already exists in the object, the property value is replaced:
stooge['first-name'] = 'Jerome';If the object does not already have that property name, the object is augmented:
stooge['middle-name'] = 'Lester'; stooge.nickname = 'Curly'; flight.equipment = { model: 'Boeing 777' }; flight.status = 'overdue';Objects are passed around by reference. They are never copied:
var x = stooge; x.nickname = 'Curly'; var nick = stooge.nickname; // nick is 'Curly' because x and stooge // are references to the same object var a = {}, b = {}, c = {}; // a, b, and c each refer to a // different empty object a = b = c = {}; // a, b, and c all refer to // the same empty objectEvery object is linked to a prototype object from which it can inherit properties. All objects created from object literals are linked to Object.prototype, an object that comes standard with JavaScript.
It is easy to inspect an object to determine what properties it has by attempting to retrieve the properties and examining the values obtained. The typeof
The for in
The delete operator can be used to remove a property from an object. It will remove a property from the object if it has one. It will not touch any of the objects in the prototype linkage.
Removing a property from an object may allow a property from the prototype linkage to shine through:
another_stooge.nickname // 'Moe' // Remove nickname from another_stooge, revealing // the nickname of the prototype. delete another_stooge.nickname; another_stooge.nickname // 'Curly'Why, every fault's condemn'd ere it be done: Mine were the very cipher of a function. . .
--William Shakespeare,Function objects are created with function literals:
Invoking a function suspends the execution of the current function, passing control and parameters to the new function. In addition to the declared parameters, every function receives two additional parameters: this and arguments. The this parameter is very important in object oriented programming, and its value is determined by the invocation pattern. There are four patterns of invocation in JavaScript: the method invocation pattern, the function invocation pattern, the constructor invocation pattern, and the apply invocation pattern. The patterns differ in how the bonus parameter this is initialized.
The invocation operator is a pair of parentheses that follow any expression that produces a function value. The parentheses can contain zero or more expressions, separated by commas. Each expression produces one argument value. Each of the argument values will be assigned to the function's parameter names. There is no runtime error when the number of arguments and the number of parameters do not match. If there are too many argument values, the extra argument values will be ignored. If there are too few argument values, the undefined value will be substituted for the missing values. There is no type checking on the argument values: any type of value can be passed to any parameter.
A bonus parameter that is available to functions when they are invoked is the arguments
JavaScript allows the basic types of the language to be augmented. In Chapter 3, we saw that adding a method to Object.prototype makes that method available to all objects. This also works for functions, arrays, strings, numbers, regular expressions, and booleans.
A recursive function is a function that calls itself, either directly or indirectly. Recursion is a powerful programming technique in which a problem is divided into a set of similar subproblems, each solved with a trivial solution. Generally, a recursive function calls itself to solve its subproblems.
Scope
The good news about scope is that inner functions get access to the parameters and variables of the functions they are defined within (with the exception of this and arguments). This is a very good thing.
Our getElementsByAttribute function worked because it declared a results variable, and the inner function that it passed to walk_the_DOM also had access to the results variable.
A more interesting case is when the inner function has a longer lifetime than its outer function.
Earlier, we made a myObject that had a value and an increment method. Suppose we wanted to protect the value from unauthorized changes.
Instead of initializing myObject with an object literal, we will initialize myObject by calling a function that returns an object literal. That function defines a value variable. That variable is always available to the increment and getValue methods, but the function's scope keeps it hidden from the rest of the program:
We can use functions and closure to make modules. A module is a function or object that presents an interface but that hides its state and implementation. By using functions to produce modules, we can almost completely eliminate our use of global variables, thereby mitigating one of JavaScript's worst features.
For example, suppose we want to augment String with a deentityify
Some methods do not have a return value. For example, it is typical for methods that set or change the state of an object to return nothing. If we have those methods return this
Functions are values, and we can manipulate function values in interesting ways. Currying allows us to produce a new function by combining a function and an argument:
Functions can use objects to remember the results of previous operations, making it possible to avoid unnecessary work. This optimization is called memoization. JavaScript's objects and arrays are very convenient for this.
Let's say we want a recursive function to compute Fibonacci numbers. A Fibonacci number is the sum of the two previous Fibonacci numbers. The first two are 0 and 1:
Divides one thing entire to many objects; Like perspectives, which rightly gazed upon Show nothing but confusion. . .
--William Shakespeare, The Tragedy of King Richard the SecondInheritance is an important topic in most programming languages.
In the classical languages (such as Java), inheritance (or extends) provides two useful services. First, it is a form of code reuse. If a new class is mostly similar to an existing class, you only have to specify the differences. Patterns of code reuse are extremely important because they have the potential to significantly reduce the cost of software development. The other benefit of classical inheritance is that it includes the specification of a system of types. This mostly frees the programmer from having to write explicit casting operations, which is a very good thing because when casting, the safety benefits of a type system are lost.
JavaScript, being a loosely typed language, never casts. The lineage of an object is irrelevant. What matters about an object is what it can do, not what it is descended from.
One weakness of the inheritance patterns we have seen so far is that we get no privacy. All properties of an object are visible. We get no private variables and no private methods. Sometimes that doesn't matter, but sometimes it matters a lot. In frustration, some uninformed programmers have adopted a pattern of pretend privacy. If they have a property that they wish to make private, they give it an odd-looking name, with the hope that other users of the code will pretend that they cannot see the odd looking members. Fortunately, we have a much better alternative in an application of the module pattern.
We start by making a function that will produce objects. We will give it a name that starts with a lowercase letter because it will not require the use of the new prefix. The function contains four steps:
It creates a new object. There are lots of ways to make an object. It can make an object literal, or it can call a constructor function with the new prefix, or it can use the Object.create method to make a new instance from an existing object, or it can call any function that returns an object.
It optionally defines private instance variables and methods. These are just ordinary vars of the function.
It augments that new object with methods. Those methods will have privileged access to the parameters and the vars defined in the second step.
We can compose objects out of sets of parts. For example, we can make a function that can add simple event processing features to any object. It adds an on method, a fire method, and a private event registry:
Thee I'll chase hence, thou wolf in sheep's array.
--William Shakespeare, The First Part of Henry the SixthAn array
Every array has a length property. Unlike most other languages, JavaScript's array length is not an upper bound. If you store an element with a subscript that is greater than or equal to the current length, the length
Since JavaScript's arrays are really objects, the delete
JavaScript provides a set of methods for acting on arrays. The methods are functions stored in Array.prototype. In Chapter 3, we saw that Object.prototype can be augmented. Array.prototype can be augmented as well.
JavaScript arrays usually are not initialized. If you ask for a new array with [], it will be empty. If you access a missing element, you will get the undefined value. If you are aware of that, or if you will naturally set every element before you attempt to retrieve it, then all is well. But if you are implementing algorithms that assume that every element starts with a known value (such as 0), then you must prep the array yourself. JavaScript should have provided some form of an Array.dim
Whereas the contrary bringeth bliss, And is a pattern of celestial peace. Whom should we match with Henry, being a king. . .
--William Shakespeare, The First Part of Henry the SixthMany of JavaScript's features were borrowed from other languages. The syntax came from Java, functions came from Scheme, and prototypal inheritance came from Self. JavaScript's Regular Expression feature was borrowed from Perl.
A regular expression is the specification of the syntax of a simple language. Regular expressions are used with methods to search, replace, and extract information from strings. The methods that work with regular expressions are regexp.exec, regexp.test, string.match, string.replace, string.search, and string.split. These will all be described in Chapter 8. Regular expressions usually have a significant performance advantage over equivalent string operations in JavaScript.
Regular expressions came from the mathematical study of formal languages. Ken Thompson adapted Stephen Kleene's theoretical work on type-3 languages into a practical pattern matcher that could be embedded in tools such as text editors and programming languages.
The syntax of regular expressions in JavaScript conforms closely to the original formulations from Bell Labs, with some reinterpretation and extension adopted from Perl. The rules for writing regular expressions can be surprisingly complex because they interpret characters in some positions as operators, and in slightly different positions as literals. Worse than being hard to write, this makes regular expressions hard to read and dangerous to modify. It is necessary to have a fairly complete understanding of the full complexity of regular expressions to correctly read them. To mitigate this, I have simplified the rules a little. As presented here, regular expressions will be slightly less terse, but they will also be slightly easier to use correctly. And that is a good thing because regular expressions can be very difficult to maintain and debug.
There are two ways to make a RegExp object. The preferred way, as we saw in the examples, is to use a regular expression literal.
Regular expression literals are enclosed in slashes. This can be a little tricky because slash is also used as the division operator and in comments.
There are three flags that can be set on a RegExp. They are indicated by the letters g, i, and m, as listed in Table 7-1. The flags are appended directly to the end of the RegExp literal:
Let's look more closely at the elements that make up regular expressions.
A regexp choice contains one or more regexp sequences. The sequences are separated by the | (vertical bar) character. The choice matches if any of the sequences match. It attempts to match each of the sequences in order. So:
"into".match(/in|int/)matches the in in into. It wouldn't match int because the match of in was successful.
A regexp sequence contains one or more regexp factors. Each factor can optionally be followed by a quantifier that determines how many times the factor is allowed to appear. If there is no quantifier, then the factor will be matched one time.
A regexp factor can be a character, a parenthesized group, a character class, or an escape sequence. All characters are treated literally except for the control characters and the special characters:
\ / [ ] ( ) { } ? + * | . ^ $which must be escaped with a \ prefix if they are to be matched literally. When in doubt, any special character can be given a \ prefix to make it literal. The \ prefix does not make letters or digits literal.
An unescaped . matches any character except a line-ending character.
An unescaped ^ matches the beginning of the text when the lastIndex property is zero. It can also match line-ending characters when the m flag is specified.
An unescaped $ matches the end of the text. It can also match line-ending characters when the m flag is specified.
Though this be madness, yet there is method in 't.
--William Shakespeare, The Tragedy of Hamlet, Prince of DenmarkJavaScript includes a small set of standard methods that are available on the standard types.
Arrayarray.concat(item...)The concat method produces a new array containing a shallow copy of this array with the items appended to it. If an item is an array, then each of its elements is appended individually. Also see array.push(item...) later in this chapter.
var a = ['a', 'b', 'c']; var b = ['x', 'y', 'z']; var c = a.concat(b, true); // c is ['a', 'b', 'c', 'x', 'y', 'z', true]array.join(separator)The join method makes a string from an array . It does this by making a string of each of the array 's elements, and then concatenating them all together with a separator between them. The default separator is ','. To join without separation, use an empty string as the separator.
If you are assembling a string from a large number of pieces, it is usually faster to put the pieces into an array and join them than it is to concatenate the pieces with the + operator:
var a = ['a', 'b', 'c']; a.push('d'); var c = a.join(''); // c is 'abcd';array.pop( )The pop and push methods make an array work like a stack. The pop method removes and returns the last element in this array . If the array is empty, it returns undefined.
var a = ['a', 'b', 'c']; var c = a.pop( ); // a is ['a', 'b'] & c is 'c'pop can be implemented like this:
Array.method('pop', function ( ) { return this.splice(this.length - 1, 1)[0]; });array.push(item...)The push method appends items to the end of an array. Unlike the concat method, it modifies the array and appends array items whole. It returns the new length of the array:
var a = ['a', 'b', 'c']; var b = ['x', 'y', 'z']; var c = a.push(b, true); // a is ['a', 'b', 'c', ['x', 'y', 'z'], true] // c is 5;push can be implemented like this:
Array.method('push', function ( ) { this.splice.apply( this, [this.length, 0]. concat(Array.prototype.slice.apply(arguments))); return this.length; });array.reverse( )The reverse method modifies the array by reversing the order of the elements. It returns the array:
var a = ['a', 'b', 'c']; var b = a.reverse( ); // both a and b are ['c', 'b', 'a']array.shift( )The shift method removes the first element from an array and returns it. If the array is empty, it returns undefined. shift is usually much slower than pop:
var a = ['a', 'b', 'c']; var c = a.shift( ); // a is ['b', 'c'] & c is 'a'shift can be implemented like this:
Array.method('shift', function ( ) { return this.splice(0, 1)[0]; });array.slice(start, end )The slice method makes a shallow copy of a portion of an array . The first element copied will be array [ start ]. It will stop before copying array [ end ]. The end parameter is optional, and the default is array .length. If either parameter is negative, array .length will be added to them in an attempt to make them nonnegative. If start is greater than or equal to array .length, the result will be a new empty array. Do not confuse slice with splice. Also see string .slice later in this chapter.
var a = ['a', 'b', 'c']; var b = a.slice(0, 1); // b is ['a'] var c = a.slice(1); // c is ['b', 'c'] var d = a.slice(1, 2); // d is ['b']array.sort(comparefn )The sort method sorts the contents of an array in place. It sorts arrays of numbers incorrectly:
var n = [4, 8, 15, 16, 23, 42]; n.sort( ); // n is [15, 16, 23, 4, 42, 8]JavaScript's default comparison function assumes that the elements to be sorted are strings. It isn't clever enough to test the type of the elements before comparing them, so it converts the numbers to strings as it compares them, ensuring a shockingly incorrect result.
Fortunately, you may replace the comparison function with your own. Your comparison function should take two parameters and return 0 if the two parameters are equal, a negative number if the first parameter should come first, and a positive number if the second parameter should come first. (Old-timers might be reminded of the FORTRAN II arithmetic IF statement.)
n.sort(function (a, b) { return a − b; }); // n is [4, 8, 15, 16, 23, 42];That function will sort numbers, but it doesn't sort strings. If we want to be able to sort any array of simple values, we must work harder:
var m = ['aa', 'bb', 'a', 4, 8, 15, 16, 23, 42]; m.sort(function (a, b) { if (a === b) { return 0; } if (typeof a === typeof b) { return a < b ? −1 : 1; } return typeof a < typeof b ? −1 : 1; }); // m is [4, 8, 15, 16, 23, 42, 'a', 'aa', 'bb']If case is not significant, your comparison function should convert the operands to lowercase before comparing them. Also see string .localeCompare later in this chapter.
With a smarter comparison function, we can sort an array of objects. To make things easier for the general case, we will write a function that will make comparison functions:
Here is a silly stately style indeed!
--William Shakespeare, The First Part of Henry the SixthComputer programs are the most complex things that humans make. Programs are made up of a huge number of parts, expressed as functions, statements, and expressions that are arranged in sequences that must be virtually free of error. The runtime behavior has little resemblance to the program that implements it. Software is usually expected to be modified over the course of its productive life. The process of converting one correct program into a different correct program is extremely challenging.
Good programs have a structure that anticipates—but is not overly burdened by—the possible modifications that will be required in the future. Good programs also have a clear presentation. If a program is expressed well, then we have the best chance of being able to understand it so that it can be successfully modified or repaired.
These concerns are true for all programming languages, and are especially true for JavaScript. JavaScript's loose typing and excessive error tolerance provide little compile-time assurance of our programs' quality, so to compensate, we should code with strict discipline.
Thus, expecting thy reply, I profane my lips on thy foot, my eyes on thy picture, and my heart on thy every part. Thine, in the dearest design of industry. . .
--William Shakespeare, Love's Labor's LostI was invited last year to contribute a chapter to Andy Oram's and Greg Wilson's Beautiful Code (O'Reilly), an anthology on the theme of beauty as expressed in computer programs. I wanted to write my chapter in JavaScript. I wanted to use it to present something abstract, powerful, and useful to show that the language was up to it. And I wanted to avoid the browser and other venues in which JavaScript is typecast. I wanted to show something respectable with some heft to it.
That will prove awful both in deed and word.
--William Shakespeare, Pericles, Prince of TyreThe following words are reserved in JavaScript:
JavaScript was designed at a time when Unicode was expected to have at most 65,536 characters. It has since grown to have a capacity of more than 1 million characters.
JavaScript's characters are 16 bits. That is enough to cover the original 65,536 (which is now known as the Basic Multilingual Plane). Each of the remaining million characters can be represented as a pair of characters. Unicode considers the pair to be a single character. JavaScript thinks the pair is two distinct characters.
The typeof operator returns a string that identifies the type of its operand. So:
typeof 98.6parseInt
The + operator can add or concatenate. Which one it does depends on the types of the parameters. If either operand is an empty string, it produces the other operand converted to a string. If both operands are numbers, it produces the sum. Otherwise, it converts both operands to strings and concatenates them. This complicated behavior is a common source of bugs. If you intend + to add, make sure that both operands are numbers.
The value NaN is a special quantity defined by IEEE 754. It stands for not a number, even though:
typeof NaN === 'number' // trueJavaScript has a surprisingly large set of falsy values, shown in See Table A-1.
Table A-1. The many falsy values of JavaScript
In Chapter 3, the hasOwnProperty method was offered as a filter to work around a problem with the for in statement. Unfortunately, hasOwnProperty is a method, not an operator, so in any object it could be replaced with a different function or even a value that is not a function:
var name; another_stooge.hasOwnProperty = null; // trouble for (name in another_stooge) { if (another_stooge.hasOwnProperty(name)) { // boom document.writeln(name + ': ' + another_stooge[name]); } }JavaScript's objects are never truly empty because they can pick up members from the prototype chain. Sometimes that matters. For example, suppose you are writing a program that counts the number of occurrences of each word in a text. We can use the toLowerCase
And, I pray thee now, tell me for which of my bad parts didst thou first fall in love with me?
--William Shakespeare, Much Ado About NothingJavaScript has a with
The eval
The continue statement jumps to the top of the loop. I have never seen a piece of code that was not improved by refactoring it to remove the continue statement.
The switch
An if or while or do
JavaScript has a function statement as well as a function expression. This is confusing because they can look exactly the same. A function
JavaScript has a set of typed wrappers. For example:
new Boolean(false)produces an object that has a valueOf method that returns the wrapped value. This turns out to be completely unnecessary and occasionally confusing. Don't use new Boolean or new Number or new String.
Also avoid new Object and new Array. Use {} and [] instead.
JavaScript's new
In many languages, void is a type that has no values. In JavaScript, void is an operator that takes an operand and returns undefined. This is not useful, and it is very confusing. Avoid void.
What error drives our eyes and ears amiss?
--William Shakespeare, The Comedy of ErrorsWhen C was a young programming language, there were several common programming errors that were not caught by the primitive compilers, so an accessory program called lint was developed that would scan a source file, looking for problems.
As C matured, the definition of the language was strengthened to eliminate some insecurities, and compilers got better at issuing warnings. lint is no longer needed.
The implementation of JSLint accepts an option object that allows you to determine the subset of JavaScript that is acceptable to you. It is also possible to set those options within the source of a script.
An option specification can look like this:
/*jslint nomen: true, evil: false */An option specification starts with /*jslint. Notice that there is no space before the j. The specification contains a sequence of name/value pairs, where the names are JSLint options and the values are true or false. An option specification takes precedence over the option object. All of the options default to false
JavaScript uses a C-like syntax, which requires the use of semicolons to delimit statements. JavaScript attempts to make semicolons optional with a semicolon insertion mechanism. This is dangerous.
Like C, JavaScript has ++ and -- and ( operators, which can be prefixes or suffixes. The disambiguation is done by the semicolon.
In JavaScript, a linefeed can be whitespace, or it can act as a semicolon. This replaces one ambiguity with another.
JSLint expects that every statement be followed by ; except for for, function, if, switch, try, and while. JSLint does not expect to see unnecessary semicolons or the empty statement.
As a further defense against the masking of errors by the semicolon insertion mechanism, JSLint expects long statements to be broken only after one of these punctuation characters or operators:
, . ; : { } ( [ = < > ? ! + - * / % ˜ ^ | & == != <= >= += -= *= /= %= ^= |= &= << >> || && === !== <<= >>= >>> >>>=JSLint does not expect to see a long statement broken after an identifier, a string, a number, a closer, or a suffix operator:
) ] ++ −−JSLint allows you to turn on the "Tolerate sloppy line breaking" (laxbreak) option.
Semicolon insertion can mask copy/paste errors. If you always break lines after operators, then JSLint can do a better job of finding those errors.
The comma operator can lead to excessively tricky expressions. It can also mask some programming errors.
JSLint expects to see the comma used as a separator, but not as an operator (except in the initialization and incrementation parts of the for statement). It does not expect to see elided elements in array literals. Extra commas should not be used. A comma should not appear after the last element of an array literal or object literal because it can be misinterpreted by some browsers.
JSLint expects that if and for statements will be made with blocks—that is, with statements enclosed in braces ({}).
JavaScript allows an if to be written like this:
if (condition) statement;That form is known to contribute to mistakes in projects where many programmers are working on the same code. That is why JSLint expects the use of a block:
if (condition) { statements; }Experience shows that this form is more resilient.
In many languages, a block introduces a scope. Variables introduced in a block are not visible outside of the block.
In JavaScript, blocks do not introduce a scope. There is only function-scope. A variable introduced anywhere in a function is visible everywhere in the function. JavaScript's blocks confuse experienced programmers and lead to errors because the familiar syntax makes a false promise.
JSLint expects blocks with function, if, switch, while, for, do, and try statements and nowhere else. An exception is made for an unblocked if statement on an else or for in.
An expression statement is expected to be an assignment, a function/method call, or delete. All other expression statements are considered errors.
A common error in switch statements is to forget to place a break statement after each case, resulting in unintended fall-through. JSLint expects that the statement before the next case or default is one of these: break, return, or throw.
JavaScript allows var definitions to occur anywhere within a function. JSLint is stricter.
JSLint expects that:
A var will be declared only once, and that it will be declared before it is used.
A function will be declared before it is used.
Parameters will not also be declared as vars.
JSLint does not expect:
The arguments array to be declared as a var.
That a variable will be declared in a block. This is because JavaScript blocks do not have block scope. This can have unexpected consequences, so define all variables at the top of the function body.
The with statement was intended to provide a shorthand in accessing members in deeply nested objects. Unfortunately, it behaves very badly when setting new members. Never use the with statement. Use a var instead.
JSLint does not expect to see a with statement.
JSLint does not expect to see an assignment statement in the condition part of an if or while statement. This is because it is more likely that:
if (a = b) { ... }was intended to be:
if (a == b) { ... }If you really intend an assignment, wrap it in another set of parentheses:
if ((a = b)) { ... }The == and != operators do type coercion before comparing. This is bad because it causes ' \f\r \n\t ' == 0 to be true. This can mask type errors.
When comparing to any of the following values, always use the === or !== operators, which do not do type coercion:
0 '' undefined null false trueIf you want the type coercion, then use the short form. Instead of:
(foo != 0)just say:
(foo)And instead of:
(foo == 0)say:
(!foo)Use of the === and !== operators is always preferred. There is a "Disallow == and != " (eqeqeq) option, which requires the use of === and !== in all cases.
JavaScript allows any statement to have a label, and labels have a separate namespace. JSLint is stricter.
JSLint expects labels only on statements that interact with break: switch, while, do, and for. JSLint expects that labels will be distinct from variables and parameters.
JSLint expects that a return, break, continue, or throw statement will be followed by a } or case or default.
JSLint expects that + will not be followed by + or ++, and that - will not be followed by - or --. A misplaced space can turn + + into ++, an error that is difficult to see. Use parentheses to avoid confusion.
The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by encouraging excessive trickiness. They are second only to faulty architecture in enabling viruses and other security menaces. The JSLint option plusplus prohibits the use of these operators.
JavaScript does not have an integer type, but it does have bitwise operators. The bitwise operators convert their operands from floating-point to integers and back, so they are not nearly as efficient as they are in C or other languages. They are rarely useful in browser applications. The similarity to the logical operators can mask some programming errors. The bitwise option prohibits the use of these operators.
The eval function and its relatives (Function, setTimeout, and setInterval) provide access to the JavaScript compiler. This is sometimes useful, but in most cases it indicates the presence of extremely bad coding. The eval function is the most misused feature of JavaScript.
In most C-like languages, void is a type. In JavaScript, void is a prefix operator that always returns undefined. JSLint does not expect to see void because it is confusing and not very useful.
Regular expressions are written in a terse and cryptic notation. JSLint looks for problems that may cause portability problems. It also attempts to resolve visual ambiguities by recommending explicit escapement.
JavaScript's syntax for regular expression literals overloads the / character. To avoid ambiguity, JSLint expects that the character preceding a regular expression literal is a ( or = or : or , character.
Constructors are functions that are designed to be used with the new prefix. The new
JSLint does not do flow analysis to determine that variables are assigned values before they are used. This is because variables are given a value (undefined) that is a reasonable default for many applications.
JSLint does not do any kind of global analysis. It does not attempt to determine that functions used with new are really constructors (except by enforcing capitalization conventions), or that method names are spelled correctly.
JSLint is able to handle HTML text. It can inspect the JavaScript content contained within <script>...</script>
JSLint can also check that JSON data structures are well formed. If the first character JSLint sees is { or [, then it strictly enforces the JSON rules. See Appendix E.
Thou map of woe, that thus dost talk in signs!
--William Shakespeare, The Tragedy of Titus AndronicusFarewell: the leisure and the fearful time Cuts off the ceremonious vows of love And ample interchange of sweet discourse, Which so long sunder'd friends should dwell upon: God give us leisure for these rites of love! Once more, adieu: be valiant, and speed well!
--William Shakespeare, The Tragedy of Richard the ThirdJSON is particularly easy to use in web applications because JSON is JavaScript. A JSON text can be turned into a useful data structure with the eval function:
var myData = eval('(' + myJSONText + ')');(The concatenation of the parentheses around the JSON text is a workaround for an ambiguity in JavaScript's grammar.)
The eval
This is an implementation of a JSON parser in JavaScript:
The animal on the cover of JavaScript: The Good Parts is a Plain Tiger butterfly (Danaus chrysippus