< JavaScript: The Good Parts by Douglas Crockford JavaScript: The Good Parts by Douglas Crockford

Full online text of JavaScript: The Good Parts by Douglas Crockford

From BookGlutton.com

JavaScript: The Good Parts

Douglas Crockford

O'Reilly Media

Dedication

For the Lads: Clement, Philbert, Seymore, Stern, and, lest we forget, C. Twildo.

Preface

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 Dream

Using Code Examples

Safari® Books Online

When 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.

How to Contact Us

Please address comments and questions concerning this book to the publisher:

Acknowledgments

Chapter 1. Good Parts

...setting the attractions of my good parts aside I have no other charms.

--William Shakespeare, The Merry Wives of Windsor

When 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.

Analyzing JavaScript

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.

A Simple Testing Ground

Chapter 2. Grammar

I know it well: I read it in the grammar long ago.

--William Shakespeare, The Tragedy of Titus Andronicus

This 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:

Names

A name is a letter optionally followed by one or more letters, digits, or underbars. A name cannot be one of these reserved words:

Numbers

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

Strings

A string literal can be wrapped in single quotes or double quotes. It can contain zero or more characters. The \

Statements

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.

Expressions

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:

Literals

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.

Functions

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.

Chapter 3. Objects

Upon a homely object Love can wink.

--William Shakespeare, The Two Gentlemen of Verona

The simple types of JavaScript are numbers, strings, booleans (true and false), null, and undefined. All other values are

Retrieval

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 .

Update

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';

Reference

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 object

Prototype

Every 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.

Reflection

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

Enumeration

The for in

Delete

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'

Global Abatement

Chapter 4. Functions

Why, every fault's condemn'd ere it be done: Mine were the very cipher of a function. . .

--William Shakespeare,

Function Literal

Function objects are created with function literals:

Invocation

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.

Arguments

A bonus parameter that is available to functions when they are invoked is the arguments

Return

Exceptions

Augmenting Types

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.

Recursion

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

Scope

Closure

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:

Callbacks

Module

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

Cascade

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

Curry

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:

Memoization

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:

Chapter 5. Inheritance

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 Second

Inheritance 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.

Object Specifiers

Prototypal

Functional

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.

Parts

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:

Chapter 6. Arrays

Thee I'll chase hence, thou wolf in sheep's array.

--William Shakespeare, The First Part of Henry the Sixth

An array

Length

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

Delete

Since JavaScript's arrays are really objects, the delete

Enumeration

Confusion

Methods

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.

Dimensions

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

Chapter 7. Regular Expressions

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 Sixth

Many 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.

Construction

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:

Elements

Let's look more closely at the elements that make up regular expressions.

Regexp Choice

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.

Regexp Sequence

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.

Regexp Factor

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.

Chapter 8. Methods

Though this be madness, yet there is method in 't.

--William Shakespeare, The Tragedy of Hamlet, Prince of Denmark

JavaScript includes a small set of standard methods that are available on the standard types.

Array

array.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:

Chapter 9. Style

Here is a silly stately style indeed!

--William Shakespeare, The First Part of Henry the Sixth

Computer 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.

Chapter 10. Beautiful Features

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 Lost

I 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.

Appendix A. Awful Parts

That will prove awful both in deed and word.

--William Shakespeare, Pericles, Prince of Tyre

Scope

Semicolon Insertion

Reserved Words

The following words are reserved in JavaScript:

Unicode

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.

typeof

The typeof operator returns a string that identifies the type of its operand. So:

typeof 98.6

parseInt

parseInt

+

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.

Floating Point

NaN

The value NaN is a special quantity defined by IEEE 754. It stands for not a number, even though:

typeof NaN === 'number' // true

Phony Arrays

Falsy Values

JavaScript has a surprisingly large set of falsy values, shown in See Table A-1.

Table A-1. The many falsy values of JavaScript

hasOwnProperty

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]); } }

Object

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

Appendix B. Bad Parts

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 Nothing

with Statement

JavaScript has a with

eval

The eval

continue Statement

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.

switch Fall Through

The switch

Block-less Statements

An if or while or do

++ −−

Bitwise Operators

The function Statement Versus the function Expression

JavaScript has a function statement as well as a function expression. This is confusing because they can look exactly the same. A function

Typed Wrappers

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.

new

JavaScript's new

void

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.

Appendix C. JSLint

What error drives our eyes and ears amiss?

--William Shakespeare, The Comedy of Errors

When 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.

Members

Options

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

Semicolon

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.

Line Breaking

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.

Comma

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.

Required Blocks

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.

Forbidden Blocks

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.

Expression Statements

An expression statement is expected to be an assignment, a function/method call, or delete. All other expression statements are considered errors.

for in Statement

switch Statement

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.

var Statement

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.

with Statement

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)) { ... }

== and !=

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 true

If 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.

Labels

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.

Unreachable Code

JSLint expects that a return, break, continue, or throw statement will be followed by a } or case or default.

Confusing Pluses and Minuses

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.

++ and −−

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.

Bitwise 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.

eval Is Evil

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.

void

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

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 and new

Constructors are functions that are designed to be used with the new prefix. The new

Not Looked For

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.

HTML

JSLint is able to handle HTML text. It can inspect the JavaScript content contained within <script>...</script>

JSON

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.

Report

Appendix D. Syntax Diagrams

Thou map of woe, that thus dost talk in signs!

--William Shakespeare, The Tragedy of Titus Andronicus

Appendix E. JSON

Farewell: 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 Third

Using JSON Securely

JSON 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

A JSON Parser

This is an implementation of a JSON parser in JavaScript:

Colophon

The animal on the cover of JavaScript: The Good Parts is a Plain Tiger butterfly (Danaus chrysippus

Share-widget Embed This Book (Widget)
Close this box

BUY THIS BOOK TO SEE THE REST OF THIS SECTION

Or continue reading chapter excerpts




Buy this Book
Close this box
Grab this widgetGrab this Reader WidgetGrab this widget
Which size LAUNCHER fits on your site? The code will open to this book.
Wide version of launcher
SQUARE 250 x 250
Narrow version of launcher
RECTANGLE 160 X 250
Copy and paste this code into your site. We’ll do the rest.
Learn more.
Log In
Close this box