JavaScript Language
JavaScript has become the third of the three languages considered essential for web programming. Unfortunately, to say that JavaScript is a well designed language would be far from the truth. It is a complicated mixture of good, bad, and ugly features.
(This document exists because of the unignorable badness of the language. With a more sensible language, it wouldn't be necessary.)
The good news is that there is a usable subset of the language that includes all of the good features and few of the bad.
Author Douglas Crockford has written an O'Reilly book on the subject, JavaScript: The Good Parts
, which teaches one to program in JavaScript while avoiding its many problems.
You should read that book. But even if you don't, you can benefit from his table of contents listing of two of the appendices:
Awful Parts (You can't totally avoid them.)
- Global Variables
- Scope
- Semicolon Insertion
- Reserved Words
- Unicode
- typeof
- parseInt
- +
- Floating Point
- NaN
- Phony Arrays
- Falsy Values
- hasOwnProperty
- Object
Bad Parts (You can avoid them.)
- ==
- with Statement
- eval
- [... personal coding style (not JS-specific) ...]
- Bitwise Operators
- The function Statement Versus the function Expression
- Typed Wrappers
- new
- void
If you find yourself using any of those features, be aware that they won't behave the way you expect them to, and that you might end up writing (and debugging) some obscurly broken code.
Variables
- variables
- are data identifiers.
In JavaScript they are typeless; it's the data they refer to that has type.
Strings and numbers are assigned directly to variables.
Anything else is an object, and the variables only point to the data; they don't represent the data itself.
(e.g. if we assign
where B identifies an object, bothA=B
andA
represent the same object. Changing a part of the object using eitherB
orA
will make the change visible through the other.) JavaScript provides ways of creating new objects when you don't want two variables to point to the same one. All of that is good. However:B
- JavaScript is so friendly that if you forget to explicitly define variables, it will silently assume that you did define them. As a result, you can define new variables as easily as by misspelling their names.
- JavaScript is so friendly that if you define the same identifier in two places, it will silently assume that they are the same variable. If you inadvertently define the same identifier as a variable in a completely different part of the program, your code, or that other section of code, or both, may be broken and very difficult to debug.
- global variables
- may be referenced from anywhere regardless of where they are defined.
- JavaScript is not like most languages; it encourages the use of globals. It is best programming practice to avoid the use of global variables; they encourage bugs and make debugging more difficult. Most other languages make this easy to do.
- Global variables should be defined using a
statement, which should be placed at the outermost level and as soon as possible in the code so that they are easier to find and don't look like block-local variables. If you don't do that, JavaScript will process the code just as if you had anyway, so there really isn't any point in making your code look like something it isn't.var
- block-local variables
- are in scope only within the block where they are defined. They of course don't exist in JavaScript.
- functional variables
- may be referenced only from within the function in which they are defined. This is the closest thing that JavaScript has to actually providing variable scope rules.
- static variables
- are like functional variables, but retain their values from one function call to another. JavaScript does not explicitly provide static variables
It's not totally hopeless.
Unintentionally undeclared variables
In response to the number of bugs caused by having JavaScript silently correct typos, a new (ugly) kluge was introduced.
If a segment of code begins with the discardable literal string
, versions of JavaScript that know about this convention will complain about undeclared variables and a few other common causes of bugs.
You should always use this.
"use strict"
Block-local Variables
JavaScript doesn't provide local variables, but they can be faked using anonymous self-executing functions.
Use
in place of the block's opening brace, and ;(function(){
in place of the closing brace.
This is an ugly way of telling JavaScript to respect the braces, but unfortunately necessary.})()
Static Variables
JavaScript doesn't provide static variables, but their equivalent can easily be produced knowing that functions are objects, and therefore can have associated variables.
The counter in test0()
is a global, so it has static behaviour.
Unfortunately, since it is a global, any code anywhere else that uses the same
identifier will be using the same global variable as this code.
It works, but it's a disaster waiting to happen.
count
The counter in test1()
is not static, and so it always returns
.
It's safe, but it doesn't work.
1
The counter in test2()
is stored as a property of the function, not as part of the function code itself, so it behaves like a static variable.
The counter in test3()
is a variable in an anonymous self-executing function that generates the real function.
The generated function refers to that variable, now otherwise inaccessible, so it behaves like a static variable.
The last case is perhaps a little harder to understand, but is the better method. (E.g. the use of the dot in naming test2's variable means that misspellings will be silently ignored.)
Variable Scope
Earlier, we mentioned that Global Variables were considered awful. The above example is a good illustration of why that is so.
First, the
declaration looks as if the variable is local to the if-block; it isn't.
JavaScript simply does not behave that way.
It treats the var i
just the same as if it had been declared where i
was declared.
Similarly, the var anchors
variable is automatically defined and it looks like that is only for the duration of the a
block, but it isn't. It's the same for (a ...)
that appears in every other script in the page, including packages of JavaScript code that are included by a
in the head section.
<link ...>
For that reason, it is considered bad coding style to declare any variable in any context other than the obvious global one, or restricted to within a function.
JavaScript has only two scopes, global and functional.
So, if you want what any other reasonable language would provide when you define variables local to a block, instead of simply using
to delimit that block, you have to use { ... }
instead.
;(function(){ ... })()
It's not that JavaScript isn't capable of doing the right thing, it's that JavaScript makes it very ugly and awkward for you to get it to do so.
Misplaced var
This is an example of an even less obvious bug caused by a misplaced var.
Function
begins by incrementing the global f()
, and eventually returns that value.
value
Function
appears to begin the same way, by incrementing the global g()
, but it actually doesn't.
The later use of value
, possibly hundreds of lines further away within the function, causes this var value
to update that local variable, not the global one.
++value
Note that not only did function g()
return the wrong value, it failed to increment the global value, which after four function calls was 2, not 4.
It's almost impossible to notice this bug, created when someone introduced that additional block of code, not realizing that the variable name was already in use elsewhere. Most other languages would either do the right thing or issue a warning that something is wrong.
The best cure is prevention.
Never declare a variable anywhere other than at the top of a section of code (e.g. immediately after
) or at the beginning of a function block.
That's the right thing to do, because that's exactly what JavaScript does already.
When it encounters the <script> "use safe"
, JavaScript silently moves the var value = 0
var value
declaration to the beginning of the function.
Functions and this
Functions invoked as methods, have a this
variable referring to the instance of the object the method was applied to.
Within functions defined within such a function however, this
refers to something completely different.
A common workaround is to assign the value of this
to another variable in the outer function, and to always use that instead.
Semicolons
Almost no one understands JavaScript's rules for semicolons.
Most statments requre a semicolon at the end, while others (e.g.
and if
must not be followed by a semicolon.
try
Since this is so confusing to most users, JavaScript again tries to be friendly: it treats extra semicolons as empty statements, and missing semicolons at the end of a line as if they were present. This does little to alleviate the confusion.
There are three schools of thought on the issue:
- Use semicolons exactly as defined in the language.
- Always use semicolons.
- Never use semicolons.
The first is admirable, but for most people impossible to achieve. The second used to be the most common approach, while the last is now becoming more popular.
JavaScript is so good at filling in missing semicolons and ignoring extra ones, that there are only two cases where it makes any difference:
- If a
keyword is the last token on a line, JavaScript will assume you forgot the semicolon and returnreturn
. Any value following on the next line will be silently ignored.undefined
- If you begin a line with an opening parenthesis, not preceded by a semicolon, JavaScript will complain about the missing semicolon, but will not supply it itself.
Given that neither approach (always or never use semicolons) solves the broken return problem, always using semicolons doesn't seem worth the effort, especially when JavaScript will complain in the one single case where a semicolon is actually required.
Never using semicolons seems to be the best way; the code is less noisy and the rules are far simpler to remember:
- Begin each return value on the same line as the
keyword.return
- Put a semicolon before each line that begins with an opening parenthesis.
The Evil Twins
: ==
and !=
==
!=
JavaScript provides two slightly different equality operators,
(two equal signs) and ==
(three equal signs), which almost always produce identical results.
===
For the double test, JavaScript tries to be helpful again (by silently converting values in order to compare objects of different types), and again makes a mess of it. In those few circumstances where the results are different, you almost invariably want and expect the result provided by the triple equal operator, not the more familiar double.
The best practice is simply to pretend that
and ==
don't exist.
It's unfortunate (but not surprising) that the comparison operators you should use, !=
and ===
, are the uglier choice, and not the choice that looks the same as in almost all other languages.
!==
Unicode
Unicode is now the standard character set for web pages. Using variable length encoding, it can provide every character that will ever be needed.
Deep in its heart, just as Microsoft Windows believes that 640K ought to be enough memory for anyone, JavaScript believes that 65 thousand different characters ought to be enough for anyone.
Unfortunately, just as computer memory is now a million times larger than Microsoft's prediction, Unicode has long since passed JavaScript's 65 thousand character limit. In particular, if you want to write code that can handle emoji characters, forget about using JavaScript.
Unexpected properties
Objects store their values as properties of that object. For arrays, each numerical subscript, converted to a string, is a property. But objects, including arrays, have other properties in addition to the ones you assign to them.
In this example, arrays have a
property.
length
When iterating the properties of an object (e.g. with
), JavaScript will also present other properties that you might not be aware of.
You can avoid them by testing the object (e.g. for (i in a) ...
), although this will also exclude properties that you do want to inherit from the object's prototype.
if (a.hasOwnProperty(i))
Addition (+
)
When it comes to arithmetic, once again JavaScript tries to be helpful and, as you might expect, gets it wrong. When performing arithmetic operations, if one or both of the operands are a string, JavaScript silently converts that string into a numeric value before applying the operator. It could be argued that that is a good thing, but it's undeniably the wrong thing to do when for one specific operator it does something different. If the operator is the plus sign, JavaScript does the opposite of what it would normally do and converts the numbers into strings. The result is totally inconsistent with the other arithmetic operators.
JavaScript overloads the plus sign, giving it multiple meanings that depend upon its context.
In a typed language, that works well, as anyone reading the code can tell what the operator will do based on the types of variables it is used with. But in JavaScript, the types depend not on the variables themselves, but on their current content.
The plus operator will do different things depending upon the content type of its operands. Whenever adding numbers, be sure that you know that all variables contain numerical values and not strings, even strings representing a number.
Ignore It
The with
Statement
with
The
statement enables lazy shorthand.
The main result is code that is difficult to understand, and a geat source of bugs.
Don't use it.
with
If you know what you actually want, then you should make it explicit. If you don't know, you shouldn't be writing code.
The new
Operator
new
The
operator doesn't always do what you might expect.
In particular, if you use it inappropriately, the return value of a function will be silently ignored, and if you forget to use it your code might end up unknowingly making changes to other objects.
Don't use it.
new
Instead simply use the creation that happens autmatically when you assign a literal value
(E.g.
, or var arr = []
), or create a new object using a prototype (e.g. Object.create(prototype_object)).
var obj = {}
In most other languages, one would use
to create a new object of the same type as x = new y; initFunc(x)
and then initialize its content.
JavaScript wants you to say y
instead.
When it sees the x = new initFunc(y)
rather than passing new
to the function, it creates a new object and passes that instead.
The problem is, the function doesn't know whether it was given an existing y
or a new object, and if you get it wrong, bad things happen.
y
The void
Operator
void
Unlike in other languages,
is a JavaScript operator, not a value.
It is also a useless operator, (though not a dangerous and useless one like void
).
new
Some people use
, which produces an undefined value, in place of the void 0
keyword, just in case some other section of code happens to assign a value to undefined
.
(Yes, JavaScript allows undefined
.)
JavaScript provides far too many other ways in which code can break that it's really not worth worrying about this one specific case that just happens to have a way of avoiding it.
Don't use it.
undefined=86
The parseInt()
Function
parseInt()
The
function can silently produce unexpect results if you don't know that the input has already been correctly formatted.
parseInt()
It doesn't validate the input or indicate where it stopped scanning, so it isn't useful enough to bother with.
undefined
Is Not undefined
undefined
Javascript has (what in any other language would be) a keyword that describes a variable that has not been assigned a value yet.
For instance, you could test if a static or global variable has been set yet with
.
if (x === undefined)
This is good, but other aspects of JavaScript can make it dangerous and confusing.
In particular the
operator, which returns a string indicating the basic type of an object, returns the string typeof
if the object doesn't exist rather than the standard undefined
value.
undefined
Be extremely careful about whether or not to quote this keyword.
Non-obvious Code
A line that has
looks like it is assigning the function to the variable x=function() { ...
, and usually it is.
But one has to look at the closing brace of the function definition to know for sure.
And that closing brace could be hundreds of lines away.
x
If it ends with
rather than a simple closing brace, the anonymous function is invoked immediately and the result assigned to the variable.
This isn't an unusual construct to use.
}()
To help avoid confusion, whenver using anonymous self-executing functions, you should enclose the function call in parentheses to make it obvious that it is an expression, not a definition.
does not look like a function is being assigned to var z = (function() {
.
You should already be used to this style of coding from using anonymous self-executing functions to create local variable blocks.
z
???
...???
...Summary
- Begin all script segments with this line:
<script> "use strict"
- Avoid global variables.
- Define all variables at the top of their lexical scope.
- Group definitions together as high as possible in the code.
- Use function creating functions to hide static variables.
- Use a function to define a function and its statics:
var FNAME=(function() { var VARNAME return function(){ ... VARNAME ... } })()
- Within functions, don't use
this
directly. - The
this
value can change within a function, so instead: function (...) { self=this
...- Wrap blocks with local variables, not with simple braces, but with:
;(function(){
...})()
- Never use semicolons.
- But do use one as shown in the previous point.
- Never use
or==
.!=
- Use
and===
instead.!==
- Never use the
operator directly.new
- Use literal initiation, use a constructor method within the object, or use
Object.create()
instead. - Never use the
statement.with
- It's for lazy, bug-promoting code.
- There is no need for the
operator.void
- Don't use strings as numbers.
- It works for all arithmetic operators but not for addition.
- Remember that JavaScript doesn't handle Unicode characters correctly.
- In particular, newer characters such as emoji cannot be processed.