
I think it's useful to understand which constructs should be avoided
because they necessarily impose a significant overhead, and what is
slow simply due to poor implementation of current JavaScript
interpreters.
In the global variable example, it is clear that s and i are free
identifiers of testfunction (and we can see statically there there are
no uses of 'with' or 'eval' in that scope), so there should be no need
to walk the scope chain. It's true that the global variable accesses
require a hashtable lookup, but since the hash value can be
precomputed, that should not be significantly more expensive than
the local variable lookup in the common case where no collision
occurs. So the 30% performance difference is a sign that the
implementation is not doing some really straightforward optimizations.
(I'm not arguing for the use of global variables. There are plenty
of more important reasons to avoid them than performance.)
In the 'implicit object conversion' section, you say:
"When you reference a property or method of a string value, the
ECMAScript engine must implicitly create a new string object with
the same value as your string, before running the method."
There's no "must" about it. It is completely trivial for an
implementation to use the same representation for string values as
for objects, so that string values can have methods that are called
like any other method. The program can't tell the difference unless
it modifies String.prototype, e.g. if it does something like:
String.prototype.toString = function() {
if (!String.prototype.isPrototypeOf(this)) {
alert("You tried to optimize and got caught");
}
return this;
}
but it is easy enough to do the string value -> String object
conversion only in that case.
(Actually the distinction between string values and String objects
in ES3 is just plain stupid -- an example of standardizing what a
particular implementation did rather than thinking carefully about
the language design. There are plenty of instances of that in ES3.
My point is that an implementation *can* work around the performance
overhead while still being strictly compliant with the poorly
thought out spec.)
The string concatenation would also be fairly easy to optimize. It's
complicated a little by the fact that + is overloaded to behave
differently for strings vs non-strings, and due to the [[DefaultValue]]
coercions. However, an implementation can still test for the common
case where all arguments (including the LHS when += is used) are
string values, and then only create a single new string value,
regardless of how many consecutive + operations there are. Java
compilers do a similar optimization (admittedly they have more type
information), so it can't be that hard.
--
David-Sarah Hopwood