Il semble que la perfection soit atteinte non quand il n’y a plus rien à ajouter, mais quand il n’y a plus rien à retrancher.
The Principle of the Good Parts is
If a feature is sometimes useful and sometimes dangerous and if there is a better option then always use the better option.
is a JavaScript program that looks for problems in JavaScript programs.
It is a code quality tool.
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 the language matured, the definition of the language was
strengthened to eliminate some insecurities, and compilers got better
at issuing warnings. lint
is no longer needed.
JavaScript is a
young-for-its-age language. It was originally intended to do small tasks in
webpages, tasks for which Java was too heavy and clumsy. But JavaScript is
a surprisingly capable language, and it is now being used in larger
projects. Many of the features that were intended to make the language easy
to use are troublesome when projects become complicated. A lint
for JavaScript is needed: , a JavaScript syntax checker and
validator.
takes a JavaScript source and scans it. If it finds a problem, it
returns a message describing the problem and an approximate location within
the source. The problem is not necessarily a syntax error, although it often
is. looks at some style conventions as well as structural problems.
It does not prove that your program is correct. It just provides another set
of eyes to help spot problems.
defines a professional subset of JavaScript, a stricter language than
that defined by the ECMAScript Programming Language Standard (the
strangely named document that governs JavaScript). will reject most
legal programs. It is a higher standard.
JavaScript is a sloppy language, but hidden deep inside there is an elegant,
better language. helps you to program in that better language
and to avoid most of the slop. will reject programs that browsers
will accept because is concerned with the quality of your code and
browsers are not. You should gladly accept all of 's advice.
can operate on JavaScript source
or JSON text.
A new sixth edition of ECMAScript [ES6] is expected to be published
later this year. Many of the new features are good ones, and will
recognise those. It will take time, perhaps years, for the new standard to
reach ubiquity. Using the new features in enviroments that do not fully
implement the new standard will result in failure. This is why gives
warnings when ES6 features are used. Some of ES6's features are good, so
will recognize the good parts of ES6 with the es6
option. As the new standard becomes more stable and better understood, the
set of features recognized by JSLint may increase. After the transition to
ES6 is complete, the es6
option will be dropped.
Currently, these features are recognized:
...
ellipsis marker in parameter lists
and argument lists, replacing the arguments
object for
variadic functions.let
statement, which is like the var
statement except that it respects block scope.
You may use let
or var
but not both.const
statement is like the let
statement
except that it disallows the use of assignment on the variable, although if the value of the variable is mutable, but can still be mutated.
const
is preferred to let
.
let
, and const
, but not var
or assignment statements, and not deep destructuring or eliding.=>
fart functions.import
and export
.`
Megastring`
literals, but not nested `
megastring`
literals.Map
, Set
,
WeakMap
, and WeakSet
.0b
- and 0o
- number literals.The most important new feature of ES6 is proper tail calls. This has no
new syntax, so doesn't see it. But it makes recursion much more
attractive, which makes loops, particularly for
loops, much
less attractive.
import export
The ES6 module feature will be an important improvement over JavaScript's
global variables as a means of linking separate files together.
recognizes a small but essential subset of the module syntax.
import
namefrom
stringliteral;
The stringliteral identifies a resource. The value (usually a function or object) exported from that resource can be accessed with the name. The stringliteral will be listed in the function report.
export default
value;
The value (usually a function or object) will be exported from this file.
provides three directives that may be inserted into a file to manage
's behavior. Use of these directives is optional. If they are used,
they should be placed into a source file before the first statement. They
are written in the form of a comment, where the directive name is placed
immediately after the opening of the comment before any whitespace. The
three directives are global
, jslint
, and
property
. Directives in a file are stronger than options
selected from the UI or passed with the option object.
/*global*/
The /*global*/
directive is used to specify a set of globals
(usually functions and objects containing functions) that are available to
this file. This was commonly used in browsers to link source files together
before ES6 modules appeared. Use of global variables is strongly discouraged, but unfortunately web browsers require their use. The /*global*/
directive can only be used when the Assume a browser option is selected.
Each of the names listed will indicate a
read-only global variable. The names are separated by ,
comma. This directive should not be used
if import
or export
is used. This directive inhibits
warnings, but it does not declare the names in the execution environment.
For example:
/*global
ADSAFE, report, jslint
*/
instructs to not give warnings about the global variables ADsafe
, report
, and jslint
. However, if any of those names
are expected to be supplied by other files and those other files fail to do so, then execution errors
will result. It is usually better to use top-level variable declarations instead:
var ADSAFE, report, jslint;
Using var
in this way allows comparing a global variable to the undefined
value to determine if it is present.
/*jslint*/
The /*jslint*/
directive allows for the control of several
options. These options can also be set from the
JSLint.com user interface.
Description | option |
Meaning |
---|---|---|
Tolerate bitwise operators | bitwise |
true if bitwise operators should be allowed. The
bitwise operators are rarely used in JavaScript programs, so it
is usually more likely that & is a mistyping of
&& than that it indicates a bitwise and. |
Assume a browser | browser |
true if the standard browser globals should be
predefined. This option will reject the use of import
and export . This option also
disallows the file form of the 'use strict' pragma. It does not
supply self or window ; you will have to request
these aliases of the dreaded global object yourself. It adds the
same globals as this directive:
|
Assume CouchDB | couch |
true if Couch DB
globals should be predefined. It adds the
same globals as this directive:
|
Assume in development | devel |
true if browser globals that are useful in
development should be predefined, and if debugger
statements and TODO comments
should be allowed. It adds the
same globals as this directive:
Be sure to turn this option off before going into production. |
Tolerate the good parts of ES6 | es6 |
true if using the good parts of ECMAScript Sixth
Edition. Some ES6 features are still under probation. Some
ES6 features will never be good parts. It adds the
same globals as this directive:
|
Tolerate eval |
eval |
true if eval should be allowed. In the
past, the eval function was the most misused
feature of the language. |
Tolerate for statement |
for |
true if the for statement should be
allowed. It is almost always better to use the array methods instead. |
Fudge the line and column numbers | fudge |
true if the origin of a text file is line 1 column
1. Programmers know that number systems start at zero.
Unfortunately, most text editors start numbering lines and
columns at one. |
Maximum number of errors | maxerr |
The maximum number of warnings reported. (The default is unlimited) |
Maximum line length | maxlen |
The maximum number of characters in a line. (The default is unlimited) |
Assume Node.js | node |
true if Node.js globals should be predefined. It will
predefine globals that are used in the Node.js environment .
It adds the same globals as this directive:
|
Tolerate this |
this |
true if this should be allowed. |
Tolerate whitespace mess | white |
true if the whitespace rules should be ignored. |
For example:
/*jslint es6, maxerr: 10, node */
/*property*/
The /*property*/
directive is used to declare a list of
property identifiers that are used in the file. Each property name in the
program is looked up in this list. If a name is not found, that indicates an
error, most likely a typing error.
The list can also be used to evade some of 's naming rules.
can build the /*property*/
list for you. At the bottom
of its report, displays a /*property*/
directive. It
contains all of the names that were used with dot
notation, subscript notation, and object literals to name the properties of
objects. You can look through the list for misspellings. You can copy the
/*property*/
directive to the top of your script file.
will check the spelling of all property names against the list. That
way, you can have look for misspellings for you.
For example,
/*property
charAt, slice, _$_
*/
ignore
introduces a new reserved word: ignore
. It is used in
parameter lists and in catch
clauses to indicate a parameter
that will be ignored. Unused warnings
will not be produced for ignore
.
function handler(ignore, value) { return do_something_useful(value); }
var let const
JavaScript provides three statements for declaring variables:
var
, let
, and const
. The
var
statement suffers from a bad practice called hoisting. The
let
statement does not do hoisting and respects block scope.
The const
statement is like let
except that it
marks the variable (but not its contents) as read only, making it an error
to attempt to assign to the variable. When given a choice,
const
is the best, var
is the worst. Use of
let
and const
require the es6
option.
uses the intersection of the var
rules and the
let
rules, and by doing so avoids the errors related to either.
A name should be declared only once in a function. It should be declared
before it is used. It should not be used outside of the block in which it is
declared. A variable should not have the same name as a variable or
parameter in an outer function. Do not mix var
and
let
.
= == ===
Fortran made a terrible
mistake in using the equality operator as its assignment operator. That
mistake has been replicated in most languages since then. C compounded that
mistake by making ==
its equality operator. The visual
similarity is a source of errors. JavaScript compounded this further by
making ==
a type coercing comparison operator that produces
false positive results. This was mitigated by adding the ===
operator, leaving the broken ==
operator in place.
attempts to minimize errors by the following rules:
==
is not allowed. This avoids the false positives, and
increases the visual distance between =
and ===
.
Assignments are not allowed in expression position, and comparisons are not
allowed in statement position. This also reduces confusion.
JavaScript uses a C-like syntax that requires the use of semicolons to delimit certain statements. JavaScript attempts to make those semicolons optional with an automatic semicolon insertion mechanism, but it does not work very well. Automatic semicolon insertion was added to make things easier for beginners. Unfortunately, it sometimes fails. Do not rely on it unless you are a beginner.
expects that every statement be followed by ;
except for
for
, function
, if
,
switch
, try
, and while
. does
not expect to see unnecessary semicolons or the empty statement.
function =>
JavaScript has four syntactic forms for making function objects: function
statements, function expressions, enhanced object literals, and the
=>
fart operator.
The function statement creates a variable and assigns the function object to it. It should be used in a file or function body, but not inside of a block.
function
name(
parameters) {
statements}
The function expression unfortunately looks like the function statement. It may appear anywhere that an expression may appear, but not in statement position and not in a loop. It produces a function object but does not create a variable in which to store it.
function (
parameters) {
statements}
The enhanced object literal provides an ES6 shorthand for creating a property
whose value is a function, saving you from having to type :
colon
and function
.
{
name(
parameters) {
statements}
}
Finally, ES6 provides an even shorter form of function expression that leaves
out the words function
and return
:
(
parameters) =>
expression
requires the parens around the parameters, and forbids a
{
left brace after the
=>
fart to avoid syntactic ambiguity.
The ,
comma operator is unnecessary and can
mask programming errors.
expects to see the comma used as a separator, but not as an operator.
It does not expect to see elided elements in array literals. A comma should
not appear after the last element of an array literal or object literal.
expects blocks with function
, if
,
switch
, while
, for
, do
,
and try
statements and nowhere else.
expects that if
, while
, do
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 expects the
use of a block:
if (condition) { statements; }
Experience shows that this form is more resilient.
An expression statement is expected to be an assignment or a function/method call. All other expression statements are considered to be errors.
for
does not recommend use of the for
statement. Use array methods
like forEach
instead. The for
option will suppress
some warnings. The forms of for
that accepts are
restricted, excluding the new ES6 forms.
for
in
does not recommend use of the for
in
statement. Use
Object.keys
instead.
The for
in
statement allows for looping through
the names of all of the properties of an object. Unfortunately, it also
loops through all of the properties that were inherited through the
prototype chain. This has the bad side effect of serving up method
functions when the interest is in data properties. If a program is written
without awareness of this situation, then it can fail.
The body of every for
in
statement should be
wrapped in an if
statement that does filtering. It can select
for a particular type or range of values, or it can exclude functions,
or it can exclude properties from the prototype. For example,
for (name in object) { if (object.hasOwnProperty(name)) { .... } }
Note that the above code will fail if the object contains a data property
named hasOwnProperty
. Use Object.keys
instead.
switch
A common error in switch
statements is to forget to place a
break
statement after each case, resulting in unintended
fall-through. expects that the statement before the next
case
or default
is one of these:
break
, return
, or throw
.
with
The with
statement was intended to provide a shorthand in
accessing properties in deeply nested objects. Unfortunately, it behaves
very badly when setting new properties. Never use the with
statement.
does not expect to see a with
statement.
JavaScript allows any statement to have a label, and labels have a
separate name space. is more strict.
expects labels only on statements that interact
with break
: switch
, while
,
do
, and for
. expects that labels will be
distinct from vars and parameters.
expects that a return
, break
, or
throw
statement will be followed by a
}
right brace or case
or
default
.
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 parens 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 to viruses and other security
menaces. Also, preincrement/postincrement confusion can produce off-by-one
errors that are extremely difficult to diagnose. Fortunately, they are also
complete unnecessary. There are better ways to add 1 to a variable.
It is best to avoid these operators entirely and rely on +=
and
-=
instead.
void
In most C-like languages, void
is a type. In
JavaScript, void
is a prefix operator that always
returns undefined
. does not expect to
see void
because it is confusing and not very useful.
Regular expressions are written in a terse and cryptic notation.
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
/
slash character. To avoid ambiguity,
expects that the character preceding a regular expression literal is
a (
left paren or
=
equal or
:
colon or
,
comma character.
this
Having this
in the language makes it harder to talk
about the language. It is like pair programming with Abbott and Costello.
Avoid using this
. Warnings about this
can be
suppressed with option.this
.
new
Constructors are functions that are designed to be used with the
new
prefix. The new
prefix creates a new object
based on the function's prototype
, and binds that object to the
function's implied this
parameter. If you neglect to use the
new
prefix, no new object will be made and this
will be bound to the global object.
enforces the convention that constructor functions be given names
with initial uppercase. does not expect to see a function invocation
with an initial uppercase name unless it has the new
prefix.
does not expect to see the new
prefix used with
functions whose names do not start with initial uppercase.
does not expect to see the wrapper forms new Number
,
new String
, new Boolean
.
does not expect to see new Object
.
Use Object.create(null)
instead.
has a specific set of rules about the use of whitespace. Where
possible, these rules are consistent with centuries of good practice with
literary style.
The indentation increases by 4 spaces when the last token on a line is
{
left brace, [
left bracket,
(
left paren. The matching closing
token will be the first token on a line, restoring the previous
indentation.
The ternary operator can be visually confusing, so ?
question mark and :
colon always begin a line and increase the indentation by 4 spaces.
If .
period is the first character on a line, the indentation is increased by 4 spaces.
A var
, let
, or const
statement will
also cause an indentation if it declares two or more variables, and if the
second variable is not on the same line as the start of the statement.
Long lines can be continued on the next line with 8 spaces added to the current indentation. Those 8 spaces do not change the current indentation. It is 8 to avoid confusion with ordinary indentation.
The word function
is always followed with one space.
Clauses (case
, catch
, default
, else
, finally
) are not statements and so should
not be indented like statements.
Spaces are used to make things that are not invocations look less like invocations.
Tabs and spaces should not be mixed. We should pick just one in order to avoid the problems that come from having both. Personal preference is an extremely unreliable criteria. Neither offers a powerful advantage over the other. Fifty years ago, tab had the advantage of consuming less memory, but Moore's Law has eliminated that advantage. Space has one clear advantage over tab: there is no reliable standard for how many spaces a tab represents, but it is universally accepted that a space occupies a space. So use spaces. You can edit with tabs if you must, but make sure it is spaces again before you commit. Maybe someday we will finally get a universal standard for tabs, but until that day comes, the better choice is spaces.
There are characters that are handled inconsistently in some browsers, and so must be escaped when placed in strings.
\u0000-\u001f \u007f-\u009f \u00ad \u0600-\u0604 \u070f \u17b4 \u17b5 \u200c-\u200f \u2028-\u202f \u2060-\u206f \ufeff \ufff0-\uffff
If is able to complete its scan, it generates a function
report. It lists for each function:
«
guess»
the name.catch
clauses of try
statements.The report will also include a list of all of the property names that were used.
Please let me know if is useful for you. Is it too
strict? Is there a check or a report that could help you to improve the
quality of your programs?
douglas@crockford.com.
But please don't ask me to dumb down or to make it more
forgiving of bad practices. You would only be disappointed.
I intend to continue to adapt based on your comments.
Keep watching for improvements. Updates are announced at
https://plus.google.com/communities/104441363299760713736.
Try it. Paste your script into the window and click the button. The analysis is done by a script running on your machine. Your script is not sent over the network. You can set the options used.
is written entirely in JavaScript, so it can run anywhere that
JavaScript (or even Java) can run.
was designed to reject code that some would consider to be
perfectly fine. The reason for this is that 's purpose is to
help produce programs that are free of error. That is difficult in any
language and is especially hard in JavaScript. attempts to help you
increase the visual distance between correct programs and incorrect
programs, making the remaining errors more obvious. will give
warnings about things that are not necessarily wrong in the current
situation, but which have been observed to mask or obscure errors. Avoid
those things when there are better options available.
It is dangerous out
there. is here to help.
will hurt your feelings. Side effects may include headache,
irritability, dizziness, snarkiness, stomach pain, defensiveness, dry mouth,
cleaner code, and a reduced error rate.
Code Conventions for the JavaScript Programming Language.