JavaScript Problems

Draft - still under construction

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 A=B where B identifies an object, both A and B represent the same object. Changing a part of the object using either A or B 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:
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 var 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.
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

<script> "use strict"
...
</script>
				
Begin every file or section of script like this.

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 "use strict", 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.

Block-local Variables

;(function() {
	var ...
	...
})()
				
Fooling JavaScript into providing local variables.

JavaScript doesn't provide local variables, but they can be faked using anonymous self-executing functions. Use ;(function(){ in place of the block's opening brace, and })() in place of the closing brace. This is an ugly way of telling JavaScript to respect the braces, but unfortunately necessary.

Static Variables

var count = 0    // global (bad)
var test0 = function () {
  return ++count
}

var test1 = function () {
  var count = 0   // local (broken)
  return ++count
}

var test2 = function () {
  // (better, but susceptible to typos)
  if (test2.count === undefined)
    test2.count = 0
  return ++test2.count
}

var test3 = (function() {
  var count = 0   // (best)
  return function(){return ++count}
})()

alert(test0()+" "+test1()+" "+test2()+" "+test3())
alert(test0()+" "+test1()+" "+test2()+" "+test3())
alert(test0()+" "+test1()+" "+test2()+" "+test3())
				
Results are: 1 1 1 1, 2 1 2 2, and 3 1 3 3.
Initializing 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 count identifier will be using the same global variable as this code. It works, but it's a disaster waiting to happen.

The counter in test1() is not static, and so it always returns 1. It's safe, but it doesn't work.

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

<script>
var anchors = document.getElementsByTagName("a")
for (var a in anchors) {
  if ("undefined" !== typeof anchors[a].href) {
    var i = document.createElement("img")
    i.setAttribute("alt", "")
    i.setAttribute("src", "image.png")
    anchors[a].insertBefore(i, anchors[a].childNodes[0])
  }
}
</script>
			
Code that would be good in any other language, but bad in JavaScript.

Earlier, we mentioned that Global Variables were considered awful. The above example is a good illustration of why that is so.

First, the var i 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 i just the same as if it had been declared where var anchors was declared. Similarly, the a variable is automatically defined and it looks like that is only for the duration of the for (a ...) block, but it isn't. It's the same a that appears in every other script in the page, including packages of JavaScript code that are included by <link ...> in the head section.

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.

<script>
// These are global variables.
// They are the same variables
// as other unrelated identifiers
// with the same name in other scripts
// used by this web page.
   var anchors = document.getElementsByTagName("a")
   var a
   var i
for (a in anchors) {
  if ("undefined" !== typeof anchors[a].href) {
    i = document.createElement("img")
    i.setAttribute("alt", "")
    i.setAttribute("src", "image.png")
    anchors[a].insertBefore(i, anchors[a].childNodes[0])
  }
}
</script>
			
Code that reflects JavaScript's natural order.

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 ;(function(){ ... })() instead.

<script> "use strict"
// All variables are local to this section of code.
// They cannot be affected by other code.
// Nor can other code affect them.
;(function() {
  var anchors = document.getElementsByTagName("a")
  ;(function() {
    var a
    for (a in anchors) {
      if ("undefined" !== typeof anchors[a].href) {
        ;(function() {
          var i = document.createElement("img")
          i.setAttribute("alt", "")
          i.setAttribute("src", "image.png")
          anchors[a].insertBefore(i, anchors[a].childNodes[0])
        })()
      }
    }
  })()
})()
</script>
			
The ugly way (and only way) to have local variables.

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

var value = 0
var f = function () {
  ++value   // global
  ...
  return value
}
var g = function () {
  ++value   // global ?
  ...
  {
    var value = 0
    ...
  }
  ...
  return value
}
var a = f()
var b = g()
var c = f()
var d = g()
alert("value="+value  +",a="+a  +",b="+b  +",c="+c  +",d="+d)
			
value=2,a=1,b=0,c=2,d=0
Use var as soon as possible.

This is an example of an even less obvious bug caused by a misplaced var.

Function f() begins by incrementing the global value, and eventually returns that value.

Function g() appears to begin the same way, by incrementing the global value, but it actually doesn't. The later use of var value, possibly hundreds of lines further away within the function, causes this ++value to update that local variable, not the global one.

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 <script> "use safe") 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 var value = 0, JavaScript silently moves the var value declaration to the beginning of the function.

Functions and this

obj.meth=function() { var self=this
   ...
   ... self ...  // "this" would have worked too.
   ...
   var subfunc=function() {
      ...
      ... self ...  // "this" would *not* have worked.
      ...
   }
}
			
Getting around broken this in methods.

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. if and try must not be followed by a semicolon.

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:

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:

return
   a_long_expression
   + b + c
			
JavaScript supplies the ; that you forgot at the end of the first line.
The computed value is then silently ignored.
The function will return undefined.

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:

The Evil Twins: == and !=

The logic of transitivity says that this test could never be true:
(A==B) && (B==C) && (A!=C)
			
But in JavaScript, this test is true:
(''==0) && (0=='0') && (''!='0')
			
JavaScript's == is not transitive.

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

:

  a = [ "length", "width", "height" ]
  a[7] = "duration"
			
All are true (valid subscript):
  (0 in a)
  (1 in a)
  (2 in a)
  ("7" in a)
			
These are false:
  ("007" in a)
  (3 in a)
  ("duration" in a)
  ("width" in a)
			
But this is true, not false:
  ("length" in a)
			
a["length"] has the value 8 (one more than the largest numerical index, not the number of elements in the array).
Be aware of inherent 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 length property.

When iterating the properties of an object (e.g. with for (i in a) ...), JavaScript will also present other properties that you might not be aware of. You can avoid them by testing the object (e.g. if (a.hasOwnProperty(i))), although this will also exclude properties that you do want to inherit from the object's prototype.

Addition (+)

Most operators have reasonable results:

 46  - 23    --> 23
"46" / 23    --> 2
46 % "23"    --> 0
"46" * "23"  --> 1058
[4, 6] - 23  --> NaN
			

Plus has unpredictable results:

 46  + 23    --> 69
"46" + 23    --> "4623"
"46" + "23"  --> "4623"
[4, 6] + 23  --> "4,623"

			

Using Number() can improve the results:

Number("46") + 23    --> 69
Number([4, 2]) + 23  --> NaN
			

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

with (thing) {
   ...
   x = y
   ...
}
				
could, depending on context, mean any of:
   thing.x = thing.y
   thing.x = y
   x = thing.y
   x = y
				
Don't use with.

The with Statement

The with statement enables lazy shorthand. The main result is code that is difficult to understand, and a geat source of bugs. Don't use it.

If you know what you actually want, then you should make it explicit. If you don't know, you shouldn't be writing code.

var thing = {
   name: "unknown",
   value: 86
}
var child = Object.create(thing)
child.name = "Fred"
thing.value = 42
				
child.value isn't explicitly defined, so it will inherit its parent's value, 42.
Don't use new.

The new Operator

The new 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.

Instead simply use the creation that happens autmatically when you assign a literal value (E.g. var arr = [], or var obj = {}), or create a new object using a prototype (e.g. Object.create(prototype_object)).

In most other languages, one would use x = new y; initFunc(x) to create a new object of the same type as y and then initialize its content. JavaScript wants you to say x = new initFunc(y) instead. When it sees the new rather than passing y 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.

if (undefined !== void 0)
   alert("Give up. Go home.")
				
Don't use void.

The void Operator

Unlike in other languages, void is a JavaScript operator, not a value. It is also a useless operator, (though not a dangerous and useless one like new).

Some people use void 0, which produces an undefined value, in place of the undefined keyword, just in case some other section of code happens to assign a value to undefined. (Yes, JavaScript allows undefined=86.) 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.

parseInt(42+15)      --> 57
parseInt("42+15")    --> 42
parseInt("two")      --> NaN
parseInt("2feet")    --> 2
parseInt("24inches") --> 24
parseInt(" 23")      --> 23
parseInt("023")      --> 19
				
Avoid parseInt().

The parseInt() Function

The parseInt() function can silently produce unexpect results if you don't know that the input has already been correctly formatted.

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

if (x === "undefined)
if (x === undefined)
if (typeof x === "undefined")
if (typeof x === undefined)
			
The first two will behave differently, and depending upon context either or both could be correct. Only the fourth line is obviously bad code (always false).
undefined = 86
			
This will break the world. Only JavaScript would accept such a statement.
undefined is easily broken.

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 typeof operator, which returns a string indicating the basic type of an object, returns the string undefined if the object doesn't exist rather than the standard undefined value.

Be extremely careful about whether or not to quote this keyword.

Non-obvious Code

var x = function () {
  ...
  return 17
}

var y = function () {
  ...
  return 17
}()

var z = ( function() {
  ...
  return 17
}() )
			
x is assigned a function that returns 17.
y is assigned 17 (less obvious).
z is assigned 17 (more obvious).
Make anonymous self-executing functions more obvious.

A line that has x=function() { ... looks like it is assigning the function to the variable x, 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.

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. var z = (function() { does not look like a function is being assigned to z. You should already be used to this style of coding from using anonymous self-executing functions to create local variable blocks.

???

...

???

...

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 new operator directly.
Use literal initiation, use a constructor method within the object, or use Object.create() instead.
Never use the with statement.
It's for lazy, bug-promoting code.
There is no need for the void operator.
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.
...