Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Closure vs Rewrite

6 views
Skip to first unread message

Jorge

unread,
Jan 11, 2009, 4:16:02 AM1/11/09
to
What do you think about this ?

<http://alttag.org/2009/01/11/closure-vs-rewrite/>

--
Jorge.

Conrad Lender

unread,
Jan 11, 2009, 5:08:30 AM1/11/09
to
On 2009-01-11 10:16, Jorge wrote:
> What do you think about this ?
>
> <http://alttag.org/2009/01/11/closure-vs-rewrite/>

Quoting from that page:
| I proposed an alternative to him [Crockford] which looked like this,
| again, minus the alert and validation:
|
| var showNumber_Rewrite=function(i){
| alert('This is called when it is needed, but only once');
| var n=['Zero','One','Two','Three','Four','Five','Six',...];
| showNumber_Rewrite=function(i){
| if(!(i in n))return 'Invalid Number';
| return n[i];
| };
| return showNumber_Rewrite(i);
| }
|
| The difference is this does not execute at all until it is actually
| needed. The first time it is called, the array is created and the
| function itself is rewritten, creating a closure, such that each
| subsequent call just validates and returns the string. Unlike his
| example, if you never need it, it never executes.

This isn't new; see here for more examples:
http://peter.michaux.ca/articles/lazy-function-definition-pattern

| He didn’t like it. He couldn’t give me a real reason.

Sounds like Crockford alright.

It's a useful pattern, especially when you want to return different
versions of a function, but need to wait for the first call before you
decide which version to use. One example where this is useful is when
you load the function in the document head, but need access to
document.body.

I'm not so sure about the mentioned performance benefit; I'd need to see
(or do) benchmarks before I use this just to save "a few cycles".

By the way, the "validation" added by the author uses "i in n" to check
for the existence of array elements, which is a bad idea for obvious
reasons:

showNumber_Rewrite("join");

There are also a few gotchas with this approach. One is the possibility
of memory leaks (see the comments on Peters blog). And if you use
something like the following, it will rewrite the function on every call:

var myAlias = showNumber_Rewrite;
myAlias(2);
myAlias(2);
myAlias(2);

This will show three alerts. You could avoid that by storing the result
of the expensive calculations in arguments.callee.cached, and have the
function return this value if it exists.


- Conrad

Richard Cornford

unread,
Jan 11, 2009, 10:28:54 AM1/11/09
to

Jorge

unread,
Jan 12, 2009, 10:34:15 AM1/12/09
to

Conrad, Richard, thanks.

They are both, in essence, the same pattern. Only that the "final" f()
gets assigned at different points in time, isn't it ?

--
Jorge.

Douglas Crockford

unread,
Jan 12, 2009, 1:34:14 PM1/12/09
to
Conrad Lender wrote:
> On 2009-01-11 10:16, Jorge wrote:
>> What do you think about this ?
>>
>> <http://alttag.org/2009/01/11/closure-vs-rewrite/>
>
> Quoting from that page:
> | I proposed an alternative to him [Crockford] which looked like this,
> | again, minus the alert and validation:
> |
> | var showNumber_Rewrite=function(i){
> | alert('This is called when it is needed, but only once');
> | var n=['Zero','One','Two','Three','Four','Five','Six',...];
> | showNumber_Rewrite=function(i){
> | if(!(i in n))return 'Invalid Number';
> | return n[i];
> | };
> | return showNumber_Rewrite(i);
> | }
> |
> | The difference is this does not execute at all until it is actually
> | needed. The first time it is called, the array is created and the
> | function itself is rewritten, creating a closure, such that each
> | subsequent call just validates and returns the string. Unlike his
> | example, if you never need it, it never executes.
>
> This isn't new; see here for more examples:
> http://peter.michaux.ca/articles/lazy-function-definition-pattern
>
> | He didn’t like it. He couldn’t give me a real reason.
>
> Sounds like Crockford alright.

You are a rude little man.

The real reason is that the performance benefit of this technique is the saving
of one if statement per invocation. I think that is too small a payoff to
justify this level of trickiness.

I prefer keeping code as readable as possible, resorting to trickery only when
there is overwhelming justification. This technique appears to me to be unjustified.

Other than that, it's great!

Conrad Lender

unread,
Jan 13, 2009, 6:33:07 AM1/13/09
to
On 2009-01-12 19:34, Douglas Crockford wrote:

> Conrad Lender wrote:
>> This isn't new; see here for more examples:
>> http://peter.michaux.ca/articles/lazy-function-definition-pattern
>>
>> | He didn’t like it. He couldn’t give me a real reason.
>>
>> Sounds like Crockford alright.
>
> You are a rude little man.

I'm sorry if you felt offended by that remark, that wasn't my intention.
On the JSLint list, I've seen a number of very short replies from you to
questions or feature requests that I thought deserved more of an
explanation. Other messages didn't receive any response at all. JSLint
is such an important tool for JS developers; it can be quite frustrating
when its author appears unresponsive to suggestions and criticism.
I apologize for my previous remark, but you may want to think about what
prompted me to write it, instead of calling me "a rude little man".

> The real reason is that the performance benefit of this technique is
> the saving of one if statement per invocation. I think that is too
> small a payoff to justify this level of trickiness.
>
> I prefer keeping code as readable as possible, resorting to trickery
> only when there is overwhelming justification. This technique appears
> to me to be unjustified.

It's overkill in the showNumber example, I agree. There are other
situations where it could actually be useful; for example measuring the
browser's viewport area. Such a function could be called a number of
times in short succession if it's used in an onresize handler. Rewriting
the function, or (better) storing the appropriate sub-function as a
property of arguments.callee, could lead to a measurable performance
difference there. Other examples are listed in Peter's blog entry and
its comments.


- Conrad

Thomas 'PointedEars' Lahn

unread,
Jan 13, 2009, 6:49:06 AM1/13/09
to

Please forgive my ignorance: How?


PointedEars

Conrad Lender

unread,
Jan 13, 2009, 8:09:40 AM1/13/09
to
On 2009-01-13 12:49, Thomas 'PointedEars' Lahn wrote:

> Conrad Lender wrote:
>> It's overkill in the showNumber example, I agree. There are other
>> situations where it could actually be useful; for example measuring the
>> browser's viewport area. Such a function could be called a number of
>> times in short succession if it's used in an onresize handler. Rewriting
>> the function, or (better) storing the appropriate sub-function as a
>> property of arguments.callee, could lead to a measurable performance
>> difference there.
>
> Please forgive my ignorance: How?

This strategy is used in Garrett Smith's APE, for example:
http://dhtmlkitchen.com/ape/build/dom/viewport-f.js

function getViewportDimensions(win) {
win = win || window;
var node = win.document, d = node, propPrefix = "client",
wName, hName;
// Safari 2 uses document.clientWidth (default).
if(typeof d.clientWidth == "number");
// Opera < 9.5, or IE in quirks mode.
else if(IS_BODY_ACTING_ROOT || isDocumentElementHeightOff(win)) {
node = d.body;
// Modern Webkit, Firefox, IE.
// Might be undefined. 0 in older mozilla.
} else if(d.documentElement.clientHeight > 0){
node = d.documentElement;
// For older versions of Mozilla.
} else if(typeof innerHeight == "number") {
node = win;
propPrefix = "inner";
}
wName = propPrefix + "Width";
hName = propPrefix + "Height";
return (this.getViewportDimensions = function() {
return{width: node[wName], height: node[hName]};
})();
// Used to feature test Opera returning wrong values
// for documentElement.clientHeight.
function isDocumentElementHeightOff(win){
var d = win.document,
div = d.createElement('div');
div.style.height = "2500px";
d.body.insertBefore(div, d.body.firstChild);
var r = d.documentElement.clientHeight > 2400;
d.body.removeChild(div);
return r;
}
}

Regardless of whether this is or isn't the best way to measure the
viewport dimensions, the example shows how function rewriting can be
used to improve performance. The tests are only performed the first time
the function is called. Without caching, isDocumentElementHeightOff
would be called every time, including the two expensive insertBefore and
removeChild calls.

Of course, this is just one way to do it, and there are many other ways
to cache the results of calculations.


- Conrad

Peter Michaux

unread,
Jan 13, 2009, 10:37:04 AM1/13/09
to
On Jan 12, 10:34 am, Douglas Crockford <nos...@sbcglobal.net> wrote:

> I prefer keeping code as readable as possible,

In that case, consider writing automatically evaluated functions with
an extra set of parens.

http://peter.michaux.ca/articles/an-important-pair-of-parens

Peter

Lasse Reichstein Nielsen

unread,
Jan 13, 2009, 5:58:22 PM1/13/09
to
Conrad Lender <crle...@yahoo.com> writes:

> It's overkill in the showNumber example, I agree. There are other
> situations where it could actually be useful; for example measuring the
> browser's viewport area. Such a function could be called a number of
> times in short succession if it's used in an onresize handler. Rewriting
> the function, or (better) storing the appropriate sub-function as a
> property of arguments.callee, could lead to a measurable performance
> difference there.

If you are going for performance, I wouldn't use arguments.callee (or
a global variable for that matter) to access the function.
Using arguments.callee is likely to trigger creation of the arguments
object, which an optimized JS implementation would otherwise avoid if
the "arguments" variable isn't used. It will cause a slowdown on each
call to the function.

Reading a property of the global object might trigger security checks
that are also costly. An enclosing scope that is not the global scope
seems like a better location.
But there is no need for a lazy computation in this case. It's simple
to detect the correct version and install it, before registering the
resize handler. Whatever you save if the user never resizes the page
is a small one-time cost.

/L
--
Lasse Reichstein Holst Nielsen
'Javascript frameworks is a disruptive technology'

Thomas 'PointedEars' Lahn

unread,
Jan 13, 2009, 6:30:55 PM1/13/09
to
Lasse Reichstein Nielsen wrote:
> If you are going for performance, I wouldn't use arguments.callee (or
> a global variable for that matter) to access the function.
> Using arguments.callee is likely to trigger creation of the arguments
> object, which an optimized JS implementation would otherwise avoid if
> the "arguments" variable isn't used. It will cause a slowdown on each
> call to the function.

Such an optimized "JS" implementation would not be conforming as the
ECMAScript Language Specification, Edition 3 Final, section 10.1.8, says
that "the `arguments' object" "is [to be] created and initialised" "when
control enters an execution context for function code", and _not_ when it
is first used. Your recommendation is not valid.


PointedEars

Conrad Lender

unread,
Jan 13, 2009, 7:20:17 PM1/13/09
to
On 2009-01-13 23:58, Lasse Reichstein Nielsen wrote:
> If you are going for performance, I wouldn't use arguments.callee (or
> a global variable for that matter) to access the function.
> Using arguments.callee is likely to trigger creation of the arguments
> object, which an optimized JS implementation would otherwise avoid if
> the "arguments" variable isn't used. It will cause a slowdown on each
> call to the function.

Interesting. I've just tested this in FF2 and Opera 9.63, and I can
confirm that using the arguments object is noticably slower (about 30%
of the maximum in Opera, and 1-2 orders of magnitude in FF2). Unless I
made a mistake in my benchmark (see below).

> But there is no need for a lazy computation in this case. It's simple
> to detect the correct version and install it, before registering the
> resize handler.

That would either mean an additional step for the user of the function,
or you couldn't place the script in the <head>, because the detection
requires access to document.body.


- Conrad


// simple benchmark using JSLitmus:
function expensive () {
return document.createElement("div");
}
function func_args () {
var ac = arguments.callee,
x = ac.x;
if (x) {
return x;
}
return (ac.x = expensive());
}
var func_noargs = (function () {
var xarguments = {
callee: {}
};
return function () {
var ac = xarguments.callee,
x = ac.x;
if (x) {
return x;
}
return (ac.x = expensive());
};
})();
JSLitmus.test('with arguments', function (count) {
while (count--) {
func_args();
}
});
JSLitmus.test('no arguments', function (count) {
while (count--) {
func_noargs();
}
});

Richard Cornford

unread,
Jan 13, 2009, 7:28:03 PM1/13/09
to

The problem with that is that ECMAScript implementations are only
required to 'behave as if ... ', and while the simplest way for an
implementation to behave as if it did precisely what the spec says it
should do (though I am not sure that "should do" is the right way of
stating that) is to be written so that it does do it that is not
required.

If a function makes no reference to an - arguments - object the
behaviour of the code will not be influenced at all if no - arguments -
object gets created for that execution context.

It is worth remembering that - arguments - objects have a dynamic
linkage with the Variable object's 'formal parameter' properties
(theoretically open-ended, but never implemented as such) and
facilitating that means a bit more work than simply creating a new
native ECMAScript object.

It also appears, from discussions on the ES 3.1 discussion mailing lists
between some of the people who have written some of the implementations
currently in use, that current implementations frequently don't create
arguments objects for execution contexts in which they are not
referenced.

> Your recommendation is not valid.

I also would not use - arguments.callee - for this type of thing, mostly
for personal aspheric reasons rather than any direct perception of
related 'efficiency'. I am not convinced that the 'problem' that doing
so attempts to address needs to be introduced into any system.

Richard Cornford.

Richard Cornford

unread,
Jan 13, 2009, 8:00:35 PM1/13/09
to
Conrad Lender wrote:
<snip>

> On the JSLint list, I've seen a number of very short replies
> from you to questions or feature requests that I thought
> deserved more of an explanation. Other messages didn't receive
> any response at all. JSLint is such an important tool for JS
> developers; it can be quite frustrating when its author appears
> unresponsive to suggestions and criticism.
<snip>

Manny things deserve more/better explanations than they get, but you
should consider the position of the people (or person) being asked to
provide those explanations. Explaining the same thing a few times can be
useful. Doing so focuses your thinking and each successive explanation
can improve on the previous (in one way or another). But get to the
point of being asked for the same expiation of the 40th time, and
particularly with all the previous expiations in the public record, and
it is quite easy to see how a certain reluctance to respond may come
into play.

I recall that years ago (5 or 6) I argued with Douglas Crockford on this
group about whether JSLint should be objecting to 'fall through' in -
switch - structures. I was in favour of allowing it, JSLint had other
ideas, and no amount of talk of 'issues' caused by it was going to
convince me otherwise.

Given that I can entirely understand an unwillingness on Douglas' part
to carry on that discussion. He may well have thought 'he wants to learn
the hard way, so let him', and left it at that. I know that I often end
up thinking that when people disregard (what I think of as)
well-reasoned argument in favour of carrying on doing what they were
going to do anyway.

In that case I did learn the hard way (which is by falling over the
'issues' for myself). Experience (particularly on large-scale projects)
has convinced me that 'fall through' is dangerous and probably is best
subject to a blanket injunction. The thing is that I still could not
present a good explanation of the 'issues' beyond vague talk of
'maintenance headaches' and 'hard to trace bugs'. That is, precisely the
same talk that failed to convince me half a decade ago. Some of these
things are apparently not as easy to put into words as some may prefer.

Richard.

Thomas 'PointedEars' Lahn

unread,
Jan 13, 2009, 9:11:52 PM1/13/09
to
Richard Cornford wrote:
> It is worth remembering that - arguments - objects have a dynamic
> linkage with the Variable object's 'formal parameter' properties
> (theoretically open-ended, but never implemented as such) and
> facilitating that means a bit more work than simply creating a new
> native ECMAScript object.

All the more reason why it does not make sense to create the object only
when referenced. If created only when execution reached the relevant
statement, that would slow down execution. The alternative to that is to
scan the entire function body for `arguments' when control enters the
execution context, to see if the reference is there, which also is not very
efficient. Better create it always, as the Spec says.

> It also appears, from discussions on the ES 3.1 discussion mailing lists
> between some of the people who have written some of the implementations
> currently in use, that current implementations frequently don't create
> arguments objects for execution contexts in which they are not
> referenced.

Apparently those people have not thought through what that would mean in a
production environment.


PointedEars

Peter Michaux

unread,
Jan 13, 2009, 11:43:55 PM1/13/09
to
On Jan 13, 5:00 pm, "Richard Cornford" <Rich...@litotes.demon.co.uk>
wrote:

> I recall that years ago (5 or 6) I argued with Douglas Crockford on this
> group about whether JSLint should be objecting to 'fall through' in -
> switch - structures. I was in favour of allowing it, JSLint had other
> ideas, and no amount of talk of 'issues' caused by it was going to
> convince me otherwise.

It is unfortunate JSLint is not programmed in a modular way so that an
abstract syntax tree is first produced and then various rules can
analyze that tree. I believe it would make it a more flexible tool for
more people's points of view.

> Given that I can entirely understand an unwillingness on Douglas' part
> to carry on that discussion. He may well have thought 'he wants to learn
> the hard way, so let him', and left it at that.

I remember reading that Douglas learned this the hard way himself.
Something about discussing the issue with an opponent to fall through
and then Douglas himself having a bug due to fall through the next
day.

Peter

Lasse Reichstein Nielsen

unread,
Jan 14, 2009, 12:55:18 AM1/14/09
to

The specification defines semantics, not implementation.
Whether you actually create the arguments object when the function is
entered, or when the arguments object is first used, is indistinguishable
and all javascript programs will behave the same in the two cases.
I.e., the semantics are identical.

Anyway, I was not saying that the arguments object was created later,
but that it is created at all. If the "arguments" variable is
definitly not used inside a function, then an implementation can
entirely avoid creating it and save time and space in doing so - still
without changing the semantics of any program.

/L 'If tree doesn't fall in the forest, and nobody is looking, does
it still take time and space?'

Lasse Reichstein Nielsen

unread,
Jan 14, 2009, 1:02:54 AM1/14/09
to
Thomas 'PointedEars' Lahn <Point...@web.de> writes:

> Richard Cornford wrote:

> All the more reason why it does not make sense to create the object only
> when referenced. If created only when execution reached the relevant
> statement, that would slow down execution. The alternative to that is to
> scan the entire function body for `arguments' when control enters the
> execution context, to see if the reference is there, which also is not very
> efficient. Better create it always, as the Spec says.

You already scan the function when you parse it, so you might as well
detect features like the use of "arguments", "with" and "eval" at that
point, before further processing the parsed function body.
Also, you only parse and compile a function once, but you might
execute a function a lot of times. That is why it makes sense to avoid
creating the arguments object, even at a slightly higher initial cost.

>> It also appears, from discussions on the ES 3.1 discussion mailing lists
>> between some of the people who have written some of the implementations
>> currently in use, that current implementations frequently don't create
>> arguments objects for execution contexts in which they are not
>> referenced.
>
> Apparently those people have not thought through what that would mean in a
> production environment.

Do tell. What /would/ it mean in a production environment?

Again: It's an optimization that does not change the semantics of any
javascript program, but which does improve the running speed of almost
all programs.

Jorge

unread,
Jan 14, 2009, 3:57:09 AM1/14/09
to
On Jan 14, 1:20 am, Conrad Lender <crlen...@yahoo.com> wrote:
> On 2009-01-13 23:58, Lasse Reichstein Nielsen wrote:
>
> > If you are going for performance, I wouldn't use arguments.callee (or
> > a global variable for that matter) to access the function.
> > Using arguments.callee is likely to trigger creation of the arguments
> > object, which an optimized JS implementation would otherwise avoid if
> > the "arguments" variable isn't used. It will cause a slowdown on each
> > call to the function.
>
> Interesting. I've just tested this in FF2 and Opera 9.63, and I can
> confirm that using the arguments object is noticably slower (about 30%
> of the maximum in Opera, and 1-2 orders of magnitude in FF2). Unless I
> made a mistake in my benchmark (see below).
>

I've put it online: <http://jorgechamorro.com/cljs/030/>

Wow, yes, arguments.callee is expensive !

--
Jorge.

Jorge

unread,
Jan 14, 2009, 4:56:17 AM1/14/09
to
On Jan 14, 1:20 am, Conrad Lender <crlen...@yahoo.com> wrote:
> On 2009-01-13 23:58, Lasse Reichstein Nielsen wrote:
>
> > If you are going for performance, I wouldn't use arguments.callee (or
> > a global variable for that matter) to access the function.
> > Using arguments.callee is likely to trigger creation of the arguments
> > object, which an optimized JS implementation would otherwise avoid if
> > the "arguments" variable isn't used. It will cause a slowdown on each
> > call to the function.
>
> Interesting. I've just tested this in FF2 and Opera 9.63, and I can
> confirm that using the arguments object is noticably slower (about 30%
> of the maximum in Opera, and 1-2 orders of magnitude in FF2). Unless I
> made a mistake in my benchmark (see below).
>
> > But there is no need for a lazy computation in this case. It's simple
> > to detect the correct version and install it, before registering the
> > resize handler.
>
> That would either mean an additional step for the user of the function,
> or you couldn't place the script in the <head>, because the detection
> requires access to document.body.
>

My 2 cents:

var func_yetAnother= function f () {
if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
};

Allows returning falsy values, runs almost as fast -if not faster- and
no closures are created:

<http://jorgechamorro.com/cljs/030/>

--
Jorge.

Message has been deleted

Jorge

unread,
Jan 14, 2009, 5:19:24 AM1/14/09
to

BTW, "Closure vs. Rewrite" isn't the correct term, as both patterns
create
closures and both "rewrite" the original f(). Well, rewrite the var,
really.
--
Jorge.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 6:10:55 AM1/14/09
to
Lasse Reichstein Nielsen wrote:

> Thomas 'PointedEars' Lahn <Point...@web.de> writes:
>> Lasse Reichstein Nielsen wrote:
>>> If you are going for performance, I wouldn't use arguments.callee (or
>>> a global variable for that matter) to access the function.
>>> Using arguments.callee is likely to trigger creation of the arguments
>>> object, which an optimized JS implementation would otherwise avoid if
>>> the "arguments" variable isn't used. It will cause a slowdown on each
>>> call to the function.
>>
>> Such an optimized "JS" implementation would not be conforming as the
>> ECMAScript Language Specification, Edition 3 Final, section 10.1.8, says
>> that "the `arguments' object" "is [to be] created and initialised" "when
>> control enters an execution context for function code", and _not_ when it
>> is first used. Your recommendation is not valid.
>
> The specification defines semantics, not implementation.
> Whether you actually create the arguments object when the function is
> entered, or when the arguments object is first used, is indistinguishable
> and all javascript programs will behave the same in the two cases.

Most definitely they will not.

> I.e., the semantics are identical.

They are not. It takes time to create the object, and creating it only when
execution reaches the point when it is first used, would slow down
execution.


> Anyway, I was not saying that the arguments object was created later,
> but that it is created at all. If the "arguments" variable is
> definitly not used inside a function, then an implementation can
> entirely avoid creating it and save time and space in doing so - still
> without changing the semantics of any program.

Fair enough, but how can you be sure that the run time required for
additonal parsing does not compensate for the run time that might be gained
by not creating the `arguments' object when not being referred to?



> /L 'If tree doesn't fall in the forest, and nobody is looking, does
> it still take time and space?'

The issue here is that somebody (the script engine) always would need to
have a look if it falls (if it occurs), though. The optimization no doubt
requires more parsing than without it.


PointedEars

Conrad Lender

unread,
Jan 14, 2009, 6:18:12 AM1/14/09
to
On 2009-01-14 02:00, Richard Cornford wrote:

> Conrad Lender wrote:
> I recall that years ago (5 or 6) I argued with Douglas Crockford on this
> group about whether JSLint should be objecting to 'fall through' in -
> switch - structures. I was in favour of allowing it, JSLint had other
> ideas, and no amount of talk of 'issues' caused by it was going to
> convince me otherwise.
...

> In that case I did learn the hard way (which is by falling over the
> 'issues' for myself). Experience (particularly on large-scale projects)
> has convinced me that 'fall through' is dangerous and probably is best
> subject to a blanket injunction. The thing is that I still could not
> present a good explanation of the 'issues' beyond vague talk of
> 'maintenance headaches' and 'hard to trace bugs'. That is, precisely the
> same talk that failed to convince me half a decade ago. Some of these
> things are apparently not as easy to put into words as some may prefer.

Yes, fall-through is one of those things that _will_ bite you sooner or
later, just like "=" vs "==" in a condition. I've been writing software
for most of my life, and I'll readily admit that I've shot myself in the
foot with both of them at least once, so I'm very glad that lint tools
will issue warnings for them.

They're a common source of errors, but IMO they can still be useful and
make for more compact code if they're used explicitly. That's why you
can use parens around an assignment in a condition to indicate that yes,
you really want to do an assignment here (JSLint adopted this very
common convention from other languages and lints). I wish there was a
way to do the same for fall-through, for example

case 66:
x++;
/*jslint: fallthrough*/
case 68:
x *= 2;
break;
...

This would double as in-line documementation for humans reading the
code, and the chance of doing something like this unintentionally would
be practically nil.

My main problem with JSLint is that its rules reflect what Crockford
defined for himself as the "good parts" of the language. I've read his
book and thought it was very interesting, insightful even, and I agree
with most of what he says, but everybody has their own ideas about which
JavaScript features fall into the good/bad/ugly categories. Having a
lint checker that's built on someone else's opinions is bound to create
false positives and unwanted warnings. Some of this can be configured
with options, but most of it is hard-coded.

For example, JSLint will force you to escape characters in regex
character classes that don't need to be escaped ([^{}]), and it
categorically disallows the use of "new Array(x)", forcing you to write
"var y = []; y.length = x". I disagree with these and a few other
warnings, and suggested a way to tell JSLint to suppress warnings on a
certain line, or (for example) from /*jslint:off*/ to /*jslint:on*/.
Crockford's reply was "I recommend that you fix your code instead of
documenting that it is intentionally defective". It's this kind of
statement that I find frustrating, and that caused my previous
(admittedly unnecessary) remark.

On the other hand, JSLint will not detect other problems that could very
well be found through static code analysis. For example, it allows
statements in a function body after an (unconditional) return, and it
won't warn if local identifiers are declared but not used. For this
reason, I'm using YUICompressor with the -v (verbose) option in addition
to JSLint, which will reveal some of those other problems. I have also
written a wrapper around JSLint that implements things like /*nolint*/
and automatically filters other unwanted warnings.


- Conrad

Jorge

unread,
Jan 14, 2009, 7:31:50 AM1/14/09
to
On Jan 14, 12:10 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>

wrote:
> Lasse Reichstein Nielsen wrote:
>
> > I.e., the semantics are identical.
>
> They are not. It takes time to create the object, and creating it only when
> execution reaches the point when it is first used, would slow down
> execution.

The time to create it is === the time to create it, no matter *when*.
What matters is that if it isn't needed, why bother creating it ?

> > Anyway, I was not saying that the arguments object was created later,
> > but that it is created at all. If the "arguments" variable is
> > definitly not used inside a function, then an implementation can
> > entirely avoid creating it and save time and space in doing so - still
> > without changing the semantics of any program.
>
> Fair enough, but how can you be sure that the run time required for
> additonal parsing does not compensate for the run time that might be gained
> by not creating the `arguments' object when not being referred to?

How do you know that it takes additional "parsing" time ?
How could you possibly detect while parsing a, for example, args= eval
('arg'+'uments'); ?
What if it's a call to its getter what triggers its creation
"lazily" ?
Have you ever heard about the "lazy-function-definition-pattern" ? :-)

--
Jorge.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 8:57:02 AM1/14/09
to
Jorge wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Lasse Reichstein Nielsen wrote:
>> > I.e., the semantics are identical.
>>
>> They are not. It takes time to create the object, and creating it only
>> when execution reaches the point when it is first used, would slow down
>> execution.
>
> The time to create it is === the time to create it,

Correct.

> no matter *when*.

Nonsense, see above.

> What matters is that if it isn't needed, why bother creating it ?

What matters is that it takes extra effort to recognize if it is needed.
And that effort takes time. So it matters very much when creation takes
place, and under which conditions.

>> > Anyway, I was not saying that the arguments object was created later,
>> > but that it is created at all. If the "arguments" variable is
>> > definitly not used inside a function, then an implementation can
>> > entirely avoid creating it and save time and space in doing so - still
>> > without changing the semantics of any program.
>>
>> Fair enough, but how can you be sure that the run time required for
>> additonal parsing does not compensate for the run time that might be
>> gained by not creating the `arguments' object when not being referred to?
>
> How do you know that it takes additional "parsing" time ?

As the reference needs to be searched for, that much is self-evident.
You provide another example:

> How could you possibly detect while parsing a, for example, args= eval
> ('arg'+'uments'); ?

That's in fact the beginning of a really good argument against conditional
creation of `arguments'.

> What if it's a call to its getter what triggers its creation
> "lazily" ?

That would be an impossible implementation.

> Have you ever heard about the "lazy-function-definition-pattern" ? :-)

You are not making any sense.


PointedEars

kangax

unread,
Jan 14, 2009, 11:40:14 AM1/14/09
to
Jorge wrote:

[...]

> My 2 cents:
>
> var func_yetAnother= function f () {
> if (f.flag) { return f.x; }
> f.flag= true;
> return (f.x= expensive());
> };
>
> Allows returning falsy values, runs almost as fast -if not faster- and
> no closures are created:
>
> <http://jorgechamorro.com/cljs/030/>

I don't see a need for "flag" here.

function fn() {
if ('cached' in fn) {
return fn.cached;
}
return (fn.cached = expensive());
};

or even slightly more concise (or cryptic):

function fn() {
return ('cached' in fn)
? fn.cached
: (fn.cached = expensive());
};

It's also a good idea to understand that assigning to a property of a
function has a potential to "leak" information that should not be
exposed. Closure provides such "privacy" (except in environments like
SpiderMonkey, which allow `eval` to be called in an arbitrary scope -
something that's considered a bug by Mozilla)

--
kangax

Message has been deleted

Jorge

unread,
Jan 14, 2009, 12:48:22 PM1/14/09
to
On Jan 14, 2:57 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> Jorge wrote:
> > How could you possibly detect while parsing a, for example, args= eval
> > ('argu'+'ments'); ?

> That's in fact the beginning of a really good argument against conditional
> creation of `arguments'.


No. That's in fact a really good argument against your theory that
arguments' use is detected while parsing.

> > What if it's a call to its getter what triggers its creation
> > "lazily" ?
> That would be an impossible implementation.

Because... what ?

--
Jorge

Jorge

unread,
Jan 14, 2009, 12:56:44 PM1/14/09
to
On Jan 14, 5:40 pm, kangax <kan...@gmail.com> wrote:
> Jorge wrote:
>
> [...]
>
> > My 2 cents:
>
> > var func_yetAnother= function f () {
> >   if (f.flag) { return f.x; }
> >   f.flag= true;
> >   return (f.x= expensive());
> >   };
>
> > Allows returning falsy values, runs almost as fast -if not faster- and
> > no closures are created:
>
> > <http://jorgechamorro.com/cljs/030/>
>
> I don't see a need for "flag" here.
>
> function fn() {
>    if ('cached' in fn) {
>      return fn.cached;
>    }
>    return (fn.cached = expensive());
>
> };
>
> or even slightly more concise (or cryptic):
>
> function fn() {
>    return ('cached' in fn)
>      ? fn.cached
>      : (fn.cached = expensive());
>
> };

Nice. Just one property. But a bit slower: it seems that 'if ("cached"
in f)' is a bit (a lot in Safari/Mac) slower than an 'if (f.flag)':
<http://jorgechamorro.com/cljs/030/>

> It's also a good idea to understand that assigning to a property of a
> function has a potential to "leak" information that should not be
> exposed. Closure provides such "privacy" (except in environments like
> SpiderMonkey, which allow `eval` to be called in an arbitrary scope -
> something that's considered a bug by Mozilla)
>

Yes: <http://javascript.crockford.com/private.html>

--
Jorge.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 12:57:44 PM1/14/09
to
Mano wrote:
^^^^[1]

> Thomas 'PointedEars' Lahn wrote:


>> Jorge wrote:
>> > How could you possibly detect while parsing a, for example, args= eval
>> > ('arg'+'uments'); ?
>>
>> That's in fact the beginning of a really good argument against
>> conditional creation of `arguments'.
>

> No. That's in fact a really good argument against your theory that
> arguments' use is detected while parsing.

That is _not_ my theory, you misunderstood. I said instead that the
possibility that your example demonstrates, points out how impractical it
in fact is that the `arguments' object is created only if it is being
referred to in the source code. For there can be, obviously, any number of
references (including yours) that are _not_ obvious from parsing the source
code.

But, on the other hand, if you do _not_ parse the source code when control
enters the local execution context, you have to wait for the reference to
be resolved, and that would slow down execution as it is not supposed to be
resolved before execution reaches the corresponding statement. That is, if
constructing the `arguments' object really was as expensive as was assumed
by those who use that assumption as an argument in favor of conditional
creation of the object.



>> > What if it's a call to its getter what triggers its creation
>> > "lazily" ?
>>
>> That would be an impossible implementation.
>

> Because... ?

An object cannot have a getter before it was created. (That was easy!)

> Jorge

Who? [^1]


PointedEars

Jorge

unread,
Jan 14, 2009, 1:10:43 PM1/14/09
to
On Jan 14, 6:57 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:
>

> >> That would be an impossible implementation.
>
> > Because... ?
>
> An object cannot have a getter before it was created.  (That was easy!)
>

Of course. But it's cheaper (read: faster) to create a (dummy, but not
non-existent) arguments object that only initializes itself (read: the
expensive operation) "lazily": if/only when its getter is called.

>
> Who? [^1]

(Manuel === Mano)-Jorge.

--
Jorge.

kangax

unread,
Jan 14, 2009, 1:22:29 PM1/14/09
to
Jorge wrote:

[...]

>> function fn() {
>> return ('cached' in fn)
>> ? fn.cached
>> : (fn.cached = expensive());
>>
>> };
>
> Nice. Just one property. But a bit slower: it seems that 'if ("cached"
> in f)' is a bit (a lot in Safari/Mac) slower than an 'if (f.flag)':
> <http://jorgechamorro.com/cljs/030/>

Interesting.

Then I guess we're talking "memory consumption" (i.e. an extra property
for every "cached" function) vs. "execution speed" (i.e. `in` operator
being slower than `flag` property lookup) : )

Also, using `in` is obviously not the best solution when working with
host methods (as it might not be a good idea to augment them with
"custom" properties).

[...]

--
kangax

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 1:32:39 PM1/14/09
to
Jorge wrote:

> Thomas 'PointedEars' Lahn wrote:
>> >> That would be an impossible implementation.
>> > Because... ?
>>
>> An object cannot have a getter before it was created.  (That was easy!)
>
> Of course. But it's cheaper (read: faster) to create a (dummy, but not
> non-existent) arguments object that only initializes itself (read: the
> expensive operation) "lazily": if/only when its getter is called.

Apparently you forget that this would require additional code to check if
the object was already initialized. Looks like it gets less efficient with
every new idea you make up to make it appear more efficient.

Nice try, though.


PointedEars

Lasse Reichstein Nielsen

unread,
Jan 14, 2009, 4:26:38 PM1/14/09
to
Mano <jorgec...@mac.com> writes:

> No. That's in fact a really good argument against your theory that
> arguments' use is detected while parsing.

Uhm, no.

While parsing, you can detect usage of an "arguments" variable in a
function's scope. If you are certain that it isn't used, you can
optimize the function call. You don't have to be certain that it is
used (which is indeed undecidable in general), since it's just an
optimization. You just have to have sufficient cases where you know
for certain that you can optimize.

Reasons to not be certain include the function using the eval function
(or using it unaliased, for implementations that don't allow aliased
eval to change the local scope).

Luckily, almost no functions use "arguments" or "eval", and all calls
to all these functions can be optimized.

/L

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 4:34:01 PM1/14/09
to
Lasse Reichstein Nielsen wrote:

> Luckily, almost no functions use "arguments" or "eval", and all calls
> to all these functions can be optimized.

You seem to continue missing that the "optimization" requires *additional*
code which makes the whole thing more expensive, not less.


PointedEars

Lasse Reichstein Nielsen

unread,
Jan 14, 2009, 4:33:52 PM1/14/09
to
kangax <kan...@gmail.com> writes:

> Jorge wrote:
>
> [...]
>
>>> function fn() {
>>> return ('cached' in fn)
>>> ? fn.cached
>>> : (fn.cached = expensive());
>>>
>>> };

...


> Also, using `in` is obviously not the best solution when working with
> host methods (as it might not be a good idea to augment them with
> "custom" properties).

If we are doing this, and mind you - I don't necessarily think it's
worth doing, then I would do something like:

var fn = (function() { var cache;
return function () {
return cache || (cache = expensive());
;} })();

Jorge

unread,
Jan 14, 2009, 4:44:10 PM1/14/09
to
On Jan 14, 10:33 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com>
wrote:

>
> If we are doing this, and mind you - I don't necessarily think it's
> worth doing, then I would do something like:
>
> var fn = (function() { var cache;
>                        return function () {
>                          return cache || (cache = expensive());
>                        ;} })();
>

This can't return falsy (cached) values...

--
Jorge.

Jorge

unread,
Jan 14, 2009, 5:02:05 PM1/14/09
to
On Jan 14, 10:26 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com>
wrote:

> Mano <jorgechamo...@mac.com> writes:
> > No. That's in fact a really good argument against your theory that
> > arguments' use is detected while parsing.
>
> Uhm, no.
>
> While parsing, you can detect usage of an "arguments" variable in a
> function's scope. If you are certain that it isn't used, you can
> optimize the function call. You don't have to be certain that it is
> used (which is indeed undecidable in general), since it's just an
> optimization. You just have to have sufficient cases where you know
> for certain that you can optimize.
>
> Reasons to not be certain include the function using the eval function
> (or using it unaliased, for implementations that don't allow aliased
> eval to change the local scope).
>
> Luckily, almost no functions use "arguments" or "eval", and all calls
> to all these functions can be optimized.
>

Yes, you seem to be right: the simple presence of an 'eval' or an
'arguments' in the source slows it down:

var func_yetAnother = function f () {


if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());
};

var func_yetAnotherDummyEval = function f (p) {


if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());

if (0) { eval(p); }
};

var func_yetAnotherDummyArgs = function f () {


if (f.flag) { return f.x; }
f.flag= true;
return (f.x= expensive());

if (0) { arguments; }
};

<http://jorgechamorro.com/cljs/030/>

--
Jorge.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 5:03:14 PM1/14/09
to
Jorge wrote:

> Lasse Reichstein Nielsen wrote:
>> If we are doing this, and mind you - I don't necessarily
>> think it's worth doing, then I would do something like:
>>
>> var fn = (function() { var cache;
>> return function () {
>> return cache || (cache = expensive());
>> ;} })();

The first `;' seems misplaced (although syntactically valid); should be

};


})();

> This can't return falsy (cached) values...

return (typeof cache != "undefined") ? cache : (cache = expensive());

can, if we assume that expensive() never returns `undefined'.


PointedEars

kangax

unread,
Jan 14, 2009, 5:10:36 PM1/14/09
to
Thomas 'PointedEars' Lahn wrote:

[...]

>> This can't return falsy (cached) values...
>
> return (typeof cache != "undefined") ? cache : (cache = expensive());
>
> can, if we assume that expensive() never returns `undefined'.

What about using unique "key"?

var fn = (function() {
var cache = KEY = { };
return function () {
return (cache != KEY)


? cache
: (cache = expensive());
}

})();


--
kangax

Lasse Reichstein Nielsen

unread,
Jan 14, 2009, 5:11:21 PM1/14/09
to
Thomas 'PointedEars' Lahn <Point...@web.de> writes:

And you are missing that you can save creating an object, with
properties linked to the parameter variables of the function, for 95%+
of all function calls. That is a significant save, for a fairly simple
and cheap static analysis.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 5:14:47 PM1/14/09
to
kangax wrote:

Creates another closure, but I like it nevertheless.


PointedEars

Richard Cornford

unread,
Jan 14, 2009, 5:46:23 PM1/14/09
to
Peter Michaux wrote:
> On Jan 13, 5:00 pm, Richard Cornford wrote:
<snip>
> It is unfortunate JSLint is not programmed in a modular way so
> that an abstract syntax tree is first produced and then various
> rules can analyze that tree. I believe it would make it a more
> flexible tool for more people's points of view.
<snip>

Yes, but with programming, in the event that others will not create the
things that you want, there is always the option of creating them for
yourself.

Richard.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 5:46:26 PM1/14/09
to
Lasse Reichstein Nielsen wrote:

> Thomas 'PointedEars' Lahn <Point...@web.de> writes:
>> Lasse Reichstein Nielsen wrote:
>>> Luckily, almost no functions use "arguments" or "eval", and all calls
>>> to all these functions can be optimized.
>>
>> You seem to continue missing that the "optimization" requires
>> *additional* code which makes the whole thing more expensive, not less.
>
> And you are missing that you can save creating an object, with
> properties linked to the parameter variables of the function, for 95%+
> of all function calls. That is a significant save, for a fairly simple

^^^^^^^^^^^^^^^
> and cheap static analysis.
^^^^^^^^^^^^^^^^^^^^^^^^^

I have to see that before I can believe it.


PointedEars

Jorge

unread,
Jan 14, 2009, 6:01:41 PM1/14/09
to

Nice. Yet another closure, but nice. Lets see how it compares to
"yetAnother2" (yours is "kangaxThree") :

var func_kangaxThree = (function() {


var cache = KEY = { };
return function () {
return (cache != KEY)
? cache
: (cache = expensive());
}
})();

var func_yetAnother2 = (function() {
var cache, key;
return function () {
if (key) { return cache; }
key= true;
cache = expensive();
}
})();

<http://jorgechamorro.com/cljs/030/>

And the winner is... yetAnother2 ! (even faster than yetAnother !)

Thanks,
--
Jorge.

Thomas 'PointedEars' Lahn

unread,
Jan 14, 2009, 6:18:24 PM1/14/09
to
Jorge wrote:

> [...] Lets see how it compares to "yetAnother2" (yours is "kangaxThree") :


>
> var func_kangaxThree = (function() {
> var cache = KEY = { };
> return function () {
> return (cache != KEY)
> ? cache
> : (cache = expensive());
> }
> })();
>
> var func_yetAnother2 = (function() {
> var cache, key;
> return function () {
> if (key) { return cache; }
> key= true;
> cache = expensive();
> }
> })();
>
> <http://jorgechamorro.com/cljs/030/>
>
> And the winner is... yetAnother2 ! (even faster than yetAnother !)

*Maybe* (JSLitmus looks rather dubious), it wins in run time. But what
about memory? The target of the exercise was to *reduce* the number of
bound variables, wasn't it?


PointedEars

kangax

unread,
Jan 14, 2009, 9:39:12 PM1/14/09
to
Thomas 'PointedEars' Lahn wrote:

[...]

> *Maybe* (JSLitmus looks rather dubious), it wins in run time. But what


> about memory? The target of the exercise was to *reduce* the number of
> bound variables, wasn't it?

Well, considering loose nature of `undefined` value in Javascript, the
only way (I see) to eliminate additional references is to use `in` (or
perhaps `hasOwnProperty`, although latter one might be an overkill).
Both would allow to know whether an operation has been actually
performed and both don't depend on the returned value.

I don't think having one more reference to a `cache` object (i.e. `KEY`
identifier in my previous example) is a big deal. I would use it as an
alternative to `in` when function augmentation is not safe/desirable.

It's also worth mentioning that `expensive` function can mess things up
by simply returning `KEY` (which would lead to enclosing function
getting into an `expensive` block again). The `KEY` example, therefore,
is not reliable in cases when `expensive` can not be trusted (as it can
brake program integrity).

[...]

--
kangax

Thomas 'PointedEars' Lahn

unread,
Jan 15, 2009, 6:20:18 AM1/15/09
to
kangax wrote:

> Thomas 'PointedEars' Lahn wrote:
> [...]
>> *Maybe* (JSLitmus looks rather dubious), it wins in run time. But what
>> about memory? The target of the exercise was to *reduce* the number of
>> bound variables, wasn't it?
>
> Well, considering loose nature of `undefined` value in Javascript, the
> only way (I see) to eliminate additional references is to use `in` (or
> perhaps `hasOwnProperty`, although latter one might be an overkill).

Also, the standalone `in' operation requires at least JavaScript 1.5,
JScript 5.0, ECMAScript Ed. 3, and Object.prototype.hasOwnProperty()
requires at least JavaScript 1.5, JScript 5.5, ECMAScript Ed. 3.

> Both would allow to know whether an operation has been actually
> performed and both don't depend on the returned value.
>
> I don't think having one more reference to a `cache` object (i.e. `KEY`
> identifier in my previous example) is a big deal.

ACK. I was only to point out that runtime efficiency should not be a single
goal; it always has to be balanced against memory efficiency, and
vice-versa.

> I would use it as an alternative to `in` when function augmentation is
> not safe/desirable.

When is function augmentation ever not safe/desirable?

> It's also worth mentioning that `expensive` function can mess things up
> by simply returning `KEY` (which would lead to enclosing function
> getting into an `expensive` block again). The `KEY` example, therefore,
> is not reliable in cases when `expensive` can not be trusted (as it can
> brake program integrity).

Fortunately, you are mistaken here. The scope of local variables is not
automatically extended to called subroutines:

function expensive()
{
return KEY;
}

function x()
{
var KEY = {};
return expensive();
}

// ReferenceError: KEY is not defined
x();

Probably you have confused this with closures:

function x()
{
var KEY = {};
return (function() {
return KEY;
})();
}

However, such a closure must be considered trusted code, as you wrote it
yourself.


PointedEars

kangax

unread,
Jan 15, 2009, 8:52:58 AM1/15/09
to
Thomas 'PointedEars' Lahn wrote:

[...]

>> Well, considering loose nature of `undefined` value in Javascript, the


>> only way (I see) to eliminate additional references is to use `in` (or
>> perhaps `hasOwnProperty`, although latter one might be an overkill).
>
> Also, the standalone `in' operation requires at least JavaScript 1.5,
> JScript 5.0, ECMAScript Ed. 3, and Object.prototype.hasOwnProperty()
> requires at least JavaScript 1.5, JScript 5.5, ECMAScript Ed. 3.

Good to know.
IIRC, `hasOwnProperty` is also not implemented in Safari <=2.0.2

[...]

>> I would use it as an alternative to `in` when function augmentation is
>> not safe/desirable.
>
> When is function augmentation ever not safe/desirable?

I suppose when a function is a host method, although, now that I think
about it, this should never be a concern - it is us who create an
enclosing function (the one that might get augmented with `cache`) : )

>
>> It's also worth mentioning that `expensive` function can mess things up
>> by simply returning `KEY` (which would lead to enclosing function
>> getting into an `expensive` block again). The `KEY` example, therefore,
>> is not reliable in cases when `expensive` can not be trusted (as it can
>> brake program integrity).
>
> Fortunately, you are mistaken here. The scope of local variables is not
> automatically extended to called subroutines:
>
> function expensive()
> {
> return KEY;
> }
>
> function x()
> {
> var KEY = {};
> return expensive();
> }
>
> // ReferenceError: KEY is not defined
> x();
>
> Probably you have confused this with closures:
>
> function x()
> {
> var KEY = {};
> return (function() {
> return KEY;
> })();
> }
>
> However, such a closure must be considered trusted code, as you wrote it
> yourself.

You're absolutely right, but what exactly is going on in this example?

// untrusted function `expensive`
// (declared somewhere outside of `fn`)
function expensive() {
console.log('exp');
return 0;
}

// caching facility
var fn = (function(){


var cache = KEY = { };
return function() {

return (cache == KEY)
? (cache = expensive())
: (function(){
console.log('cache');
return cache;
})();
}
})();

fn(); fn(); fn();

When written like this, it works as expected, and I get the following in
a log:

exp
cache
cache

Now, when changing `expensive` to return `KEY` -

...
function expensive() {
console.log('exp');
return KEY;
}
...

- the output becomes:

exp
exp
exp


--
kangax

Conrad Lender

unread,
Jan 15, 2009, 8:58:53 AM1/15/09
to
On 2009-01-15 14:52, kangax wrote:
> You're absolutely right, but what exactly is going on in this example?
...

> // caching facility
> var fn = (function(){
> var cache = KEY = { };

This doesn't do what you think: KEY is not created as a local variable.
It's global, and can be changed by expensive(). This should work better:

var cache = {}, KEY = cache;


- Conrad

Thomas 'PointedEars' Lahn

unread,
Jan 15, 2009, 9:34:24 AM1/15/09
to
kangax wrote:

> Thomas 'PointedEars' Lahn wrote:
> [...]
>>> Well, considering loose nature of `undefined` value in Javascript, the
>>> only way (I see) to eliminate additional references is to use `in` (or
>>> perhaps `hasOwnProperty`, although latter one might be an overkill).
>>
>> Also, the standalone `in' operation requires at least JavaScript 1.5,
>> JScript 5.0, ECMAScript Ed. 3, and Object.prototype.hasOwnProperty()
>> requires at least JavaScript 1.5, JScript 5.5, ECMAScript Ed. 3.
>
> Good to know.
> IIRC, `hasOwnProperty` is also not implemented in Safari <=2.0.2

Thanks, the Matrix has you ;-)


Regards,

PointedEars

kangax

unread,
Jan 15, 2009, 11:00:22 AM1/15/09
to

Well, that's... enlightening : )

Thanks.

--
kangax

0 new messages