N
nolo contendere
Here?
In this very thread in fact.
Lots, but I don't have time to go through all of them right now.
However, one good illustration is the least you deserve.
I appreciate it.
Take this code form the latest (1.6.0) version of Prototype.js:-
| var Hash = Class.create(Enumerable, (function() {
| if (function() {
| var i = 0, Test = function(value) { this.key = value };
| Test.prototype.key = 'foo';
| for (var property in new Test('bar')) i++;
| return i > 1;
| }()) {
| function each(iterator) {
| var cache = [];
| for (var key in this._object) {
| var value = this._object[key];
| if (cache.include(key)) continue;
| cache.push(key);
| var pair = [key, value];
| pair.key = key;
| pair.value = value;
| iterator(pair);
| }
| }
| } else {
| function each(iterator) {
| for (var key in this._object) {
| var value = this._object[key], pair = [key, value];
| pair.key = key;
| pair.value = value;
| iterator(pair);
| }
| }
| }
| ...
- What this appears to represent is the conditional creation of a
function based upon (rather complex) test. But in ECMAScript terms this
is actually one big syntax error, because inside each of the
BlockStatement branches of the - if-else - there are what appear to be
function declarations, and BlockStatemen may only contain Statements
(not FunctionDeclarations (FunctionDeclarations and Statements being the
two distinct syntactic units from which ECMAScript Programs and function
bodies are constructed)).
Obviously if the 3 or 4 browsers with which Prototype.js 'works'
actually generated a syntax error that fact would have been noticed, so
what is happening? The first part of the answer is that JavaScript(tm)
has a non-standard syntax extension that is a FunctionStatement, which
(being a Statement) may appear inside a BlockStatement and can be
conditionally evaluated. ECMAScript allows for such extensions, though
they usually must be avoided when creating cross-browser code. The
second part of the answer is that JScript (and many other ECMAScript
implementations, including Opera's) are tolerant enough to
unconditionally treat these out of context 'FunctionDeclarations' as
FunctionDeclarations. But because FunctionDeclarations are processed
during variable instantiation, before any actual code execution, in
these environments the effect of the above is the equivalent of:-
var Hash = Class.create(Enumerable, (function() {
function each(iterator) {
for (var key in this._object) {
var value = this._object[key], pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
}
function each(iterator) {
var cache = [];
for (var key in this._object) {
var value = this._object[key];
if (cache.include(key)) continue;
cache.push(key);
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
}
if (function() {
var i = 0, Test = function(value) { this.key = value };
Test.prototype.key = 'foo';
for (var property in new Test('bar')) i++;
return i > 1;
}()) {
} else {
}
...
- Where the first - each - FunctionDeclaration results in the creation
of a function object and the assignment of a reference to that function
to the 'each' property of the variable object, and then the second -
each - FunctionDeclaration - results in the creation of a second
function object which is then assigned to the 'each' property of the
variable object, replacing the first. So in these environments it is the
second FunctionDeclaration that defines 'each', unconditionally, and
the - if-else - statement is effectively empty, making its execution
worthless.
Now if this code 'works' at all it 'works' mostly by coincidence as it
is relying on a non-standard extension wherever the - if-else - is
effective, and where the FunctionDeclaration are unconditionally acted
upon the result must only 'work' due to the order in which the two
declarations appear.
Of course the conditional creation of function objects is trivial in
javascript in a way that fully conforms with the ECMAScript
specification, and so in a way that can be expected to be fully
cross-browser and consistently executed in ECMA 262 3rd Ed. implementing
environments. Such code would be something like:-
var Hash = Class.create(Enumerable, (function() {
var each;
if (function() {
var i = 0, Test = function(value) { this.key = value };
Test.prototype.key = 'foo';
for (var property in new Test('bar')) i++;
return i > 1;
}()) {
each = function (iterator) {
var cache = [];
for (var key in this._object) {
var value = this._object[key];
if (cache.include(key)) continue;
cache.push(key);
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
}
} else {
each = function (iterator) {
for (var key in this._object) {
var value = this._object[key], pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
}
}
...
- replacing the 'syntax error' FunctionDeclaration with Expression
Statements where a Function Expression is assigned to an Identifier. The
result is fully ECMAScript standard and so will 'work' consistently in
all past, present and future ECMA 262 3rd Ed. conforming
implementations.
Why has a library not been written this way? Obviously it's not your
responsibility to do it, or to even know the reason why it hasn't been
done thus far, but if so many people are in error, and it causes you
any distress, it would behoove you to write one, or at least begin
one. Perhaps set up a project? Clearly you care a lot about this
subject, and have invested much time in it. This is just another
avenue by which you can participate in -- and even shape -- the future
of how javascript is used.
It is simply the case that anyone knowing javascript would have written
the original code in this final form. So would not be writing code that
functioned purely by coincidence but instead they would be programming.
The problem with the way this sort of ignorance of javascript results in
code that only works by coincidence is highlighted by browsers like
Opera. Opera currently acts like JScript, and unconditionally acts on
the FunctionDeclarations out of their (otherwise syntax error) context
but while Opera has an ECMA 262 conforming javascript implementation,
they also aim to emulate JavaScript(tm), and so may introduce
JavaScript(tm)'s FunctionStatement extension at any moment. And so code
that works by coincidence because of the order of the
'FunctionDeclarations' may find itself suddenly conditionally evaluating
FunctionStatements with any next version of Opera, and what these
branches do in that versions of Opera may be totally inappropriate.
So this is the browser's problem. They are too permissive. But if you
think about it, if 90% of people do something one way, and that way
doesn't conform to "the standard" (in this case, ECMA), doesn't the
way most people do it BECOME the standard? It may be horrific in its
implementation, syntax, whatever, but so is HTTP. So you either adapt
and use it, or try to make change.
The Prototype.js approach results in code that cannot be expected to
work in any more browsers than those in which it has been demonstrated
to 'work', and so cannot even be expected to 'work' with the future
versions of that those browsers. This cannot be a viable approach to
creating cross-browser code, and it does not.
Again, this may be the browser's fault then. Bear in mind that browser
developers do have to cater to existing web pages, and have all sorts
of testing to try to ensure that nothing breaks in future versions. I
highly doubt that just because a page doesn't conform to ECMA
standards, the browser developers will say "screw that web page, if
they don't follow ECMA they don't get a proper rendering in our
browser." This may work if the percentage of "bad" web pages were
vanishingly small, but hey, this is the real world where people are
stupid and market share is of vital importance to a browser's
continued existence. I'm not saying I'm encouraging bad coding, or
stupidity, but I'm being a realist. Once all market share is captured,
THEN you can begin to gradually enforce whichever standards are the
best. But until then, it's a free-for-all.
That is quite a bit of writing in order to demonstrate that the authors
of Prototype.js don't know javascript. Similar demonstrations are
possible for bad design, ridiculous inefficiencies, avoidable bad
practices and much else besides, but I don't have time right now so you
will have to look them up in the archive.
I truly do appreciate your perspective. I did a little research on
you, and you appear to be a very intelligent individual, although (and
I don't mean to be destructively critical) a bit of an idealist/
purist--which the world does need, but isn't the way the world works.
The code itself is about a quarter as efficient as it could be, but
certain aspects of the design promote its extremely inefficient use, so
examples you will see are often orders of magnitude worse than that.
Fast computers mitigate that problem, but you would find systems created
with Prototype.js become unacceptably sluggish at much lower levels of
complexity.
Maybe, but that doesn't stop my boss continually wanting the client-side
code our web applications to run faster. He maintains that they are
easier to sell when they are fast, and then that they are easier to sell
if they have ever more bells and whistles. Obviously those are
contradictory demands.
At least for as long as you avoid understanding how those effects work,
and so how trivial they are to create for yourself.
Again, if they are so trivial, why wouldn't a "good" library have been
written? Wouldn't evolution cause the "good" librarues to bubble up
and become the de facto standard? Wouldn't "bad libraries" break in
future revs, independent browsers, etc, and the "good libraries" gain
mind- and market-share with each manifestation of the "bad
libararies'" failure?