Douglas
Crockford
www.crockford.com
2007-06-21
The ECMAScript Programming Language (aka JavaScript) went from prototype to world wide acceptance in a remarkably short time. This led to premature standardization, locking some mistakes and inconveniences into the language specification.
Despite these flaws, the language has become one of the world's most popular programming languages. It has been embedded in a large class of applications, and its role as the language of the browser has expanded significantly with the advent of Ajax.
In the earlier years of JavaScript's development, one of its worst attributes was its instability due to frequent redefinition. One of modern JavaScript's best features is its stability. It hasn't changed in seven years. This is what allowed Ajax to happen. Ajax was impossible when the language was in flux. The browser is unlike most programming environments in that the execution environment is not dictated by the supplier of a program; it is determined by every individual who runs the program. The requirement to run in a multitude of browsers, with widely varying levels of quality and standards compliance, which can each have multiple revision levels still in the field, makes programming for the browser quite difficult.
We want to correct some of the errors in the design and specification of the language but without introducing instability. The changes considered here are corrections, deprecations, and some new features. New features must be of high value, and introduce no new syntax. This makes it possible for Ajax libraries running in older browsers (such as IE 6 and IE 7) to fill in features that will be present in more futuristic browsers.
Most of the new features are implemented as new members of existing objects. This makes it possible for a program to look for the presence of a feature without triggering a syntax error.
ECMAScript has powerful reflection capabilities, but there is a blind spot with respect to functions. It is not possible to determine the name of a function or the names of its arguments without parsing the function's source text.
Functions should have a name property that returns the name of the function as a string. The name of anonymous functions is the empty string.
Functions should also have an arguments array, which contains the names of the arguments. So, given the example
function foo(bar, bas, bat) {...}
foo.name is 'foo' and foo.arguments is ['bar', 'bas', 'bat'].
typeOf
The typeof
prefix operator returns a string based on the
type of its parameter. Unfortunately, it provides the wrong result if
the operand is null
or an array.
The new typeOf
global function is intended to replace the
defective typeof
operator. It produces the same result as
typeof
, except that it returns 'null'
for null
and 'array'
for arrays.
It can be implemented in JavaScript:
function typeOf(value) { var s = typeof value; if (s === 'object') { if (value) { if (typeof value.length === 'number' && !(value.propertyIsEnumerable('length')) && typeof value.splice === 'function') { s = 'array'; } } else { s = 'null'; } } return s; }
There should be a way of adding members to objects, particularly prototype objects, without them being enumerated by the for...in statement.
object.dontEnum(memberName)
This is a one-way operation: It cannot be undone.
The keys method produces an array containing all of the enumerable keys of the object, in no particular order.
object.arrayOfKeys()
The values
method produces an array containing the values
from an object. By default, the array is filled with the enumerable values
of the object. If a keys array parameter is supplied, then
it provides the names of the members whose values should be included in
the result, in the order determined by the keys.
object.arrayOfValues(keys)
object.toJSONString(memberName)
The begetObject method returns a new empty object whose [[Prototype]] is the original object.
object.begetObject()
It can be implemented as
Object.prototype.begetObject = function () { function F() {} F.prototype = this; return new F(); };
The isEmpty
method returns true
if the object
(ignoring its prototype chain) has no properties.
It can be implemented as
Object.prototype.isEmpty = function () { var i; if (typeof this === 'object' || typeof this === 'function') { for (i in this) { if (this.hasOwnProperty(i)) { return false; } } } return true; };
Most of these methods come from Mozilla's JavaScript 1.6.
array.toJSONString()
array.indexOf(value)
array.lastIndexOf(value)
array.every(func, thisObject)
array.filter(func, thisObject)
array.forEach(func, thisObject)
array.map(func, thisObject)
array.some(func, thisObject)
string.parseJSON()
string.toJSONString()
string.trim()
String.prototype.trim = function () { return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); };string.eval(context)
The
eval
method replaces the currenteval
global function. The string is compiled as an expression and executed in its own environment, completely isolated from the current environment except that an optional context object can be supplied which provides global variable names and their values.Example:
result = "a + b * c".eval({a: 1, b: 2, c: 3}); // result is 7
date.toJSONString()
date.toISOString()
The ECMAScript Specification is unnecessarily restrictive in the limitations on the use of reserved words. These restrictions can be significantly relaxed.
The ECMAScript Specification does not allow the use of reserved words as identifiers in the key/value pairs in the literal object notation. There is no reason to have this restriction.
The following statement is currently illegal, producing syntax errors. It should be legal.
var object = {function: false, goto: "jail", var: 27};
The ECMAScript Specification does not allow the use of reserved words in the dot notation. There is no reason to have this restriction.
The following statement is currently illegal, producing syntax errors. It should be legal.
if (object.function) { object.goto = object.var; }
Currently, the reserved word list is much too long. It not only includes ECMAScript's keywords, it also includes all of Java's reserved words, even including the words that Java does not use.
The list should be trimmed way back to include only the words that need to be reserved to ensure reliable source parsing.
The use of commas should be made more regular.
Allow a comma between the last value and the closing brace.
Allow a comma between the last value and the closing bracket without increasing the length.
Make the arguments array a true array.
The this variable is used to point at the container object of a function, providing a way for the function to access the object's members. The ECMAScript Specification requires that, for inner functions, this be set to point to the global object. This is bad for a couple of reasons.
First, it is often useful for an inner function to act on the same object that the outer method does. But because this is not retained when calling the inner function, this must be passed in as an explicit parameter. Second, accidents can easily occur in which global vars are unintentionally modified.
I recommend that when calling a function that the value of this be retained from the outer context except when new or a method invocation determine the value of this.
The !! prefix operator is the smallest way of converting a value to a boolean It is already in the language. By recognizing it, it can be optimized.
If a function returns the result of recursively calling itself, then that call will be reformed as a loop. This optimization can significantly reduce the rate of memory consumption of recursive algorithms.
The following features should be depreciated. The Standard should offer advice that use of these features is not recommended, and that they may become illegal in a later revision of the standard.
Primitive type wrappers: new Object(value), new String(), new Number(), new Boolean().
The with statement.
Semicolon insertion.
arguments.callee.
typeof.
The
eval()
function.