Java Script Flaws

JavaScript and EcmaScript have their share of design flaws. Some of these continue for backwards compatibility with the early days of web scripting. Others are simply differences of opinion among developers. (shocking that it happens ;-) Here are some examples:


No Built-In Array Iterator

The properties of dictionaries/objects can be iterated using a for/in loop:

var dict = {
one: 1,
two: 2,
three: 3
};
for (var ii in dict) {
// Do Something
}
The order of the iteration is undefined by the standard, because the intent is to iterate over unordered structures like hashes.

This means that some algorithms - that are sensitive to their order of execuation inside the loop - may be impractical or behave differently in different implementations. In particular, iterating over an array can be the source of many bugs. For/in loops will iterates through both the indices and enumerable properties of the array, as the following example shows:

var aList = [2,3,4,5,6,7,8,7,6];
aList.bad = "bad";
aList.stillbad = "more bad";
aList.map = function(each) {
return "see, it iterates properties, still not reliable to use for in";
};

for(var each in aList) alert(aList[each]);
A solution is to write a HigherOrderFunction to iterate over an array and use closure to pass a function containing the code to be iterated:
function forEach(anArray, aProcedure){
            for(var index=0; index<anArray.length; index++) 
                aProcedure(anArray[index]);
};

var scopedVar = 12345; var aList = [1, 2, 3, 4, 5]; forEach(aList, function(anItem){ doStuff(anItem, scopedVar); });
Some people believe that function like the above belong in the core library of the language (even though ECMAScript doesn't technically have library support). Programmers shouldn't have to be ReinventingTheWheel for simple, general-purpose algorithms. Subtle incompatibilities and bugs can crop up, and less code reuse could reduce efficency.

Others believe that functions like this should be written into external libraries. Languages like Lisp and Smalltalk make the case pretty well that it's very flexible and extensible to leave some control structures outside of the language. It doesn't force you into limited constructs offered by the language.

If JavaScript supported RealMacros - such as a LispMacro - then new control structures could be added to the language. For example, the above solution could be rewritten as:

var scopedVar = 12345;
var aList = [1, 2, 3, 4, 5];
forEach (var anItem in aList){
doStuff(anItem, scopedVar);
};
Note that the macro was not shown, because JavaScript doesn't have macro support!

MozillaFirefox 1.5 implements JavaScript 1.6. The Array object has these new methods: every(), some(), forEach(), filter(), and map(). See BlocksInJavaScript and http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6 .

{It needs to have a fricken built-in table engine. --top}

[What's a "fricken table engine"?]


No Libraries

JavaScript can be used to build programs of both small size and also great heft. However, ECMAScript doesn't really define anything like libraries to help wth code reuse. The closest things are scripts running in the same environment and the built-in objects/methods. You have to outside the spec and use multiple source files glued together by the environment (e.g. HTML <script> elements).

As a result, many programmers - especially newcommers to the language - tend to BoldlyGoWhereSomeoneHasGoneBefore with messy, bandwidth obese results. A standard way to include other scripts from a script - as well as a standard library - would do well toward promoting more code reuse and better JavaScriptCodingStyle?.


No good way of controlling for/in access to properties

In JavaScript, some properties are not iterated over by for/in loops. These properties are usually built into the language and return false for the object's propertyIsEnumerable() method. If the programmer overrides one of these properties, it automatically becomes enumerable. There is no way (to my knowledge) of preventing an object's property being iterated by a for/in loop.

One way to overcome this issue is to never mix objects with properties-as-data and properties-as-methods. Sometimes the difference between the data and methods becomes blurred, and this pattern creates more harm than good.


typeOf null == "object"

The typeOf operator tells you which built-in type a variable is. Possible values include:

Primitives boxed as objects are interpreted as objects. Thus note that typeof new Number(8) == "object", while typeof Number(8) == "number".

The problem stems from the fact that typeof null == "object" in JavaScript. This makes life a little harder when you absolutely need to know if a value is null.

A workaround is to test for having a type of an object (although it really isn't an object), then for being null:

function isNull(vv) {
return typeof vv == "object" && vv == null;
}
This function should return true iff vv is null. It had to test that typeof returns "object" first, since an undefined value is also equal to null but returns a typeof "undefined".

Just use vv === null. -- StragerNeds?


Variables are global unless declared local.

In this example:

function f() {
var aa = 10;
bb = 20;
}
Variable aa is local and bb is global. That is, bb is first retrieved from the parent code block's scope. If it isn't found, the parent's parent's scope is searched. And so on... This process is called searching the scope-chain. If bb is first accessed from the top-level code of the script, it becomes a property of the global object. For example, in HTML web browsers:
<script type="text/javascript">
bb = 5;
self.alert(self.bb);
</script>
If one forgets to declare all local variable before using them, subtle gremlins can pop up in a program. For example, one function could modify another's variables. These types of bugs are hard to find and sometimes fall though even extensive testing.

Strict adherence to a good JavaScriptCodingStandard could help prevent some bugs; however as programmers are only human, mistakes are bound to be made. The default should be local, and you should have to work to make a variable global, e.g.

function f() {
aa = 10; // local
var cc = 20; // local
var.parent bb = 20; // parent scope
var.parent[1] dd = 42; // parent's parent scope
var.self ee = 50; // global scope
}
Note that the above is not legal JavaScript. It is just a possible alternative that the creators of JavaScript could have chosen instead (but didn't).


Octal integer literals

and... non-octal integer literals... If a number contains the digits 0-7 and has a leading 0, it is interpreted in base eight and not base ten:

var arr_flight = 0708; // this is decimal
var dep_flight = 0707; // this is octal
ARGH


Weirdness initializing arrays

An array constructor with zero arguments creates an empty array. An array constructor with n arguments where n>=2 creates an array with n elements; those being the arguments to the constructor. Ex:

var ary = new Array(1,2,3,4,5); // ary = [1, 2, 3, 4, 5]
However, if an array constructor with one argument is used, that argument is not interpreted as the single (initial) element of the array; it's the size of the array (the array then gets that many null elements). Ex:
var ary = new Array(5); // contains 5 elements (each undefined)
While an initial-size constructor is often useful, the "new Array(1,2,3,4,5)" form is aberrant. But now it's a legacy issue and so far hasn't been removed.


Offsite Issues

DouglasCrockford has some good articles in praise of and criticizing JavaScript at http://www.crockford.com.


See also: JavaScript, JavaScriptRocks, JavaScriptSucks.


CategoryJavaScript


EditText of this page (last edited July 22, 2010) or FindPage with title or text search