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.
JSLint 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: JSLint, a JavaScript syntax checker
and validator.
JSLint 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. JSLint 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.
JSLint 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). JSLint 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. JSLint helps you to program in that better language and to avoid most of the slop. JSLint will reject programs that browsers will accept because JSLint is concerned with the quality of your code and browsers are not. You should gladly accept all of JSLint's advice.
JSLint can operate on JavaScript source or JSON text.
Some of ES6’s features are good, so JSLint will recognize the good parts of ES6.
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, it 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 JSLint 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. JSLint recognizes a small but essential subset of the module syntax.
import
namefrom
stringliteral;
import {
name} from
stringliteral;
import(
string).then(
function);
export default Object.freeze(
expression);
export {
name}
;
JSLint provides three directives that may be placed in a
file to manage JSLint’s behavior. Use of these directives is
optional. If they are used, they should be placed in 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 JSLint 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; var report; var jslint;
Using var
in this way allows comparing a global variable to the
undefined
value to determine whether it is has been used in the global context.
/*jslint*/
The /*jslint*/
directive allows for the control of several
options. These options can also be set from the
web 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.
JSLint will give warnings on the bitwise operators
unless this option is selected. |
Assume a browser | browser |
true if the standard browser globals should be
predefined. This option disallows the file form of the
"use strict" pragma. |
Assume Bun | bun |
true if Bun globals should be predefined. |
Assume CouchDB | couch |
true if
Couch DB
globals should be predefined. It adds the same globals as this
directive:
|
Assume Deno | deno |
true if Deno globals should be predefined. |
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 eval |
eval |
true if eval should be allowed. In the
past, the eval function was the most misused
feature of the language. |
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. JSLint will by default start
numbering at zero. Use this option to start the numbering at one
in its reports. |
Tolerate get and set |
getset |
true if accessor properties are allowed in object literals. |
Tolerate globalThis |
global |
true if globalThis should be allowed.
Use globalThis instead of window ,
self , or global .
|
Tolerate long source lines | long |
true if a line can contain more than 80 characters. |
Assume Node.js | node |
true if Node.js globals should be predefined. |
Tolerate null |
null |
true if null should be allowed
anywhere except Object.create(null) . |
Tolerate this |
this |
true if this should be allowed. |
Assume Txiki | tjs |
true if Txiki globals should be predefined. |
Assume web | web |
true if the web globals
should be predefined. |
Tolerate whitespace mess | white |
true if the whitespace rules should be ignored. |
For example:
/*jslint bitwise, 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 JSLint’s naming rules.
JSLint can build the /*property*/
list for you. At
the bottom of its report, JSLint 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 thru the list for misspellings. You
can copy the /*property*/
directive to the top of your
script file. JSLint will check the spelling of all property
names against the list. That way, you can have JSLint look for
misspellings for you.
For example,
/*property
charAt, slice, _$_
*/
_
JSLint introduces a new reserved word: _
. 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
_
.
function handler(_, 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.
JSLint 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
. Declare one name per statement.
= == ===
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.
JSLint 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.
JSLint expects that every statement will be followed by ;
except
for function
, if
, try
, and
while
. JSLint does not expect to see unnecessary semicolons,
the empty statement, or empty blocks.
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
JSLint 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.
JSLint 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.
JSLint expects blocks with function
, if
,
while
, do
, and try
statements and nowhere else.
JSLint expects that if
, while
and do
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.
An expression statement is expected to be an assignment or a function/method call. All other expression statements are considered to be errors.
for
Iterating thru arrays is best achieved using the array methods, such as
forEach
, which handle the induction variable for you. This
eliminates the potential for a whole class of errors.
For all other kinds of iteration, use a tail recursive function, a
while
loop or a do
loop. These alternatives avoid
the for
loop's overly compacted syntax
for (initialization; condition; increment) { // do some work }
yet do exactly the same thing:
initialization; while (condition) { // do some work increment; }
In fact, they are strictly more powerful, because the condition check can be moved anywhere within the block.
The for
in
statement loops thru
the names of all of the properties of an object. Unfortunately, it also
loops thru all of the properties that were inherited from the
prototype chain. This has the bad side effect of serving up method
functions when the interest is in data properties. Use
Object.keys
instead.
JSLint does not expect to see a for
statement.
null
JSLint does not recommend the use of null
, except in
Object.create(null)
. Use undefined
instead.
JavaScript has two bottom values, undefined
and
null
(three if you count NaN
). Using both is a
source of ambiguity. JavaScript itself uses undefined
, making
it indispensable. On the other hand, null
can usually be
eliminated from well-written programs. For example, rather than assigning
null
to a property of an object, you can delete
the property instead.
Warnings about null
can be suppressed with the null
option.
switch
The switch
statement suffers from a fall thru hazard, as well as a
bizarre syntax. Fortunately, there are better alternatives. The
if
statement has the else if
form which is more
regular and more compact than the switch
statement.
if (variable === case_a) { statements; } else if (variable === case_b) { statements; } else { statements; }
Alternatively, an object can be filled with functions representing the different cases.
const cases = { case_a() { statements; }, case_b() { statements; } }; cases[variable]();
If none of the functions are matched, an exception gets raised automatically.
JSLint does not expect to see a switch
statement.
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.
JSLint does not expect to see a with
statement.
JavaScript allows any statement to have a label, and labels have a separate name space. JSLint is more strict.
JSLint expects labels only on statements that interact with
break
: while
and do
. JSLint expects
that labels will be distinct from vars and parameters.
JSLint expects that a return
, break
,
or throw
statement will be followed by a
}
right brace.
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 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 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
. 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
/
slash character. To avoid ambiguity,
JSLint 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 the this
option.
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.
JSLint enforces the convention that constructor functions be
given names with initial uppercase. JSLint does not expect to
see a function invocation with an initial uppercase name unless it has the
new
prefix. JSLint does not expect to see the
new
prefix used with functions whose names do not start with
initial uppercase.
JSLint does not expect to see the wrapper forms
new Number
, new String
, new Boolean
.
JSLint does not expect to see new Object
.
Use Object.create(null)
instead.
JSLint 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 always wrap it in parens.
The ?
question mark and
:
colon each begin a line.
return ( (the_token.id === "(string)" || the_token.id === "(number)") ? String(the_token.value) : the_token.id );
The word function
is always followed with one space.
Clauses (catch
, else
and 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 criterion. 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.
If JSLint 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 JSLint 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 JSLint down or to make it more forgiving of bad practices. You would only be disappointed.
I intend to continue to adapt JSLint based on your comments. Keep watching for improvements.
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.
JSLint is written entirely in JavaScript, so it can run anywhere that JavaScript (or even Java) can run.
JSLint was designed to reject code that some would consider to be perfectly fine. The reason for this is that JSLint's purpose is to help produce programs that are free of error. That is difficult in any language and is especially hard in JavaScript. JSLint attempts to help you increase the visual distance between correct programs and incorrect programs, making the remaining errors more obvious. JSLint 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. JSLint is here to help.
JSLint will hurt your feelings. Side effects may include headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, dry mouth, cleaner code, and a reduced error rate.