Primitive Re-definition

  • Thread starter Michael Haufe (\TNO\)
  • Start date
M

Michael Haufe (\TNO\)

There seems to be a trend among jQuery "Rock Stars" that involves
redefining primitive values for "lookup efficiency". For example, I
saw the following in one of the JQuery Conference power points:

var TRUE = true,
FALSE = false,
undefined = undefined;

I've also seen something similar in the jQuery source code.

In 9 years of JavaScript development, I've never seen such a thing
done. Am I right to think that this pattern is practically pointless
in most if not all cases, or did I miss the boat on this one?
 
R

RobG

There seems to be a trend among jQuery "Rock Stars" that involves
redefining primitive values for "lookup efficiency". For example, I
saw the following in one of the JQuery Conference power points:

var TRUE = true,
FALSE = false,
undefined = undefined;

I've also seen something similar in the jQuery source code.

In 9 years of JavaScript development, I've never seen such a thing
done. Am I right to think that this pattern is practically pointless
in most if not all cases, or did I miss the boat on this one?

I've seen this done in environments using multiple languages as a way
of creating a single variable name for true, false, etc. across all
the languages. I can't see that it's necessary in a single language
environment or where multiple languages are being used and they all
have the same syntax for those values.

I don't think it shouldn't be in a particular library - it should be
left to the programming group to decide what the generic variable
names will be, and then to define them in a manner that won't conflict
with other scripts in use.
 
R

RobG

I've seen this done in environments using multiple languages as a way
of creating a single variable name for true, false, etc. across all
the languages. I can't see that it's necessary in a single language
environment or where multiple languages are being used and they all
have the same syntax for those values.

I don't think it shouldn't be in a particular library

That should have been:

I think it shouldn't be in a particular library...


And it doesn't seem to be used in the current version of jQuery
(1.3.2).
 
T

-TNO-

And it doesn't seem to be used in the current version of jQuery
(1.3.2).

Line 18:
-------------------------------------
14. var
15. // Will speed up references to window, and allows munging
its name.
16. window = this,
17. // Will speed up references to undefined, and allows
munging its name.
18. undefined,
19. // Map over jQuery in case of overwrite
20. _jQuery = window.jQuery,
21. // Map over the $ in case of overwrite
22. _$ = window.$,
------------------------------------------------------------
 
R

RobG

Line 18:
-------------------------------------
14.    var
15.        // Will speed up references to window, and allows munging
its name.
16.        window = this,
17.        // Will speed up references to undefined, and allows
munging its name.
18.        undefined,
19.        // Map over jQuery in case of overwrite
20.        _jQuery = window.jQuery,
21.        // Map over the $ in case of overwrite
22.        _$ = window.$,
------------------------------------------------------------


I searched for TRUE and FALSE. :-(

Any idea whether the "speed up references" claim has any basis in
fact?

Testing 1,000,000 loops gave the following results:

Browser Global Local
IE 235 110
Firefox 3 3

So while it might be measurably faster in IE, I don't think it makes
any practical difference. The string "undefined" only appears 50 times
in jQuery, perhaps it's the "munging" part that is important to the
author. Where does that happen?


Here's my test function:

function testRef(n) {
var i = n, msg, t;

var s = new Date();
while (i--) {
(t == undefined);
}
var f = new Date();

msg = 'Global undefined: ' + (f - s);
i = n;

s = new Date();
var UNDEFINED = undefined;
while (i--) {
(t == UNDEFINED);
}
f = new Date();

msg += '\nLocal undefined: ' + (f - s);

alert(msg);

}


testRef(1000000);
 
T

-TNO-

So while it might be measurably faster in IE, I don't think it makes
any practical difference. The string "undefined" only appears 50 times
in jQuery, perhaps it's the "munging" part that is important to the
author. Where does that happen?

Here are my results to verify your own (I copied your code to a test
page here: http://thenewobjective.com/temp/vartest.html):

Global | Local
FireFox 3.5.3 3ms | 3ms
Chrome 2.0 9ms | 6ms
Safari 4.0.2 16ms | 16ms
Opera 10.00 82ms | 62ms
IE 8 109ms | 53ms
IE 7 195ms | 109ms (IE Tester)
IE 6 192ms | 112ms (IE Tester)
 
L

Lasse Reichstein Nielsen

Michael Haufe (\"TNO\") said:
There seems to be a trend among jQuery "Rock Stars" that involves
redefining primitive values for "lookup efficiency".

I'm glad you quoted that. It makes almost no sense.
For example, I
saw the following in one of the JQuery Conference power points:

var TRUE = true,
FALSE = false,
undefined = undefined;

That's just silly.
I can accept "undefined", because that would be a lookup on the
global object otherwise (but the "= undefined" is unnecessary).
The global object can be slower than most other objects and scopes,
for numerous reasons.

But "true" and "false" are *keywords*! They have no lookup time
at all, and are evaluated directly in the parser. Any aliasing
will be slower (if that matters at all).
I've also seen something similar in the jQuery source code.

Voodoo science.
In 9 years of JavaScript development, I've never seen such a thing
done. Am I right to think that this pattern is practically pointless
in most if not all cases, or did I miss the boat on this one?

I can see a point in avoiding accesses to the global object inside a
tight loop, so "undefined" makes sense. The rest are pretty much
stupid, mostly harmless, but slightly slower than just doing the right
thing.

/L
 
L

Lasse Reichstein Nielsen

-TNO- said:
Line 18:
-------------------------------------
14. var
15. // Will speed up references to window, and allows munging
its name.
16. window = this,
17. // Will speed up references to undefined, and allows
munging its name.
18. undefined,
19. // Map over jQuery in case of overwrite
20. _jQuery = window.jQuery,
21. // Map over the $ in case of overwrite
22. _$ = window.$,
------------------------------------------------------------

These do make some sense. Avoiding lookups on the global object does
make a difference (but as with all optimizations - don't do it unless
you have profiled and found it to make a signficant contribution).

Taking a copy to be safe against overwriting also makes sense,
especially in library code that might be combined with other
libraries.

/L
 
R

RobG

These do make some sense. Avoiding lookups on the global object does
make a difference (but as with all optimizations - don't do it unless
you have profiled and found it to make a signficant contribution).

Your hint about the length of the scope chain got me thinking about
making it longer - so I did. However, avoiding global lookups still
doesn't make a lot of difference. I also made the comparison in the
same scope as the function running the loop so that only the undefined
lookup should use the scope chain.

IE:
Global undefined level 0: 172
Global undefined level 1: 234
Global undefined level 2: 281
Local level 0: 47
Local level 2: 156

Firefox:
Global undefined level 0: 2
Global undefined level 1: 153
Global undefined level 2: 151
Local level 0: 2
Local level 2: 124

Performance seems to be enhanced more by executing statements as
global code than messing with local declarations of global variables.

Here's the new code:

<script type="text/javascript">

function testRef(n) {
var i = n, msg = [], f, s, t;


s = new Date();
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('Global undefined level 0: ' + (f - s));

function level1() {
i = n;
s = new Date();
var t;
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('\nGlobal undefined level 1: ' + (f - s));
}

level1();

function level2() {
(function() {
i = n;
s = new Date();
var t;
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('\nGlobal undefined level 2: ' + (f - s));
})();
}

level2();

i = n;
s = new Date();
var UNDEFINED = undefined;
while (i--) {
(t === UNDEFINED);
}
f = new Date();
msg.push('\nLocal level 0: ' + (f - s));

function local2() {
(function() {
i = n;
s = new Date();
var UNDEFINED = undefined;
var t;
while (i--) {
(t === UNDEFINED);
}
f = new Date();
msg.push('\nLocal level 2: ' + (f - s));
})();
}
local2();

document.getElementById('div0').innerHTML = msg.join('<br>');
}

window.onload = function(){testRef(500000);};

</script>

Taking a copy to be safe against overwriting also makes sense,
especially in library code that might be combined with other
libraries.

I can understand that perspective. However, it woud be better to use:

var UNDEFINED;

so that it is obvious within the code that it has been defined within
the script. Also, using:

var undefined = undefined;

means that if some other script that assigns it an unexpected value
runs first, the "local" undefined will get that unexpected value.
 
G

Garrett Smith

Means the identifier becomes shortened in minification, to something
like |b|.
[snip]
I searched for TRUE and FALSE. :-(

Not there. Look to Cappuccino for that. Plus nil, Nil, NULL, NO, YES,
etc. All globals there, so not mungeable.
Any idea whether the "speed up references" claim has any basis in
fact?

I have a test for scope chain and identifier resolution, as well as
assignment and reading using the identifier from global object or
containing scope. The results vary depending on the browser.

I will post that up in a new thread tomorrow. It is getting late here,
almost 1am.
 
T

Thomas 'PointedEars' Lahn

RobG said:
I searched for TRUE and FALSE. :-(

Any idea whether the "speed up references" claim has any basis in
fact?

Scope chain; see Nicholas Zakas' presentation about it, which is correct in
that regard. However, identifying `window' with `this' (i.e., the reference
to the ECMAScript Global Object here) is jQuery's first bug, of course.


PointedEars
 
G

Gabriel Gilini

On Sep 14, 5:24 am, Thomas 'PointedEars' Lahn <[email protected]>
wrote:
[...]
However, identifying `window' with `this' (i.e., the reference
to the ECMAScript Global Object here) is jQuery's first bug, of course.

I understand that `window' is a property of the Global Object. But
besides semantics, what else could that misconception cause?
 
T

Thomas 'PointedEars' Lahn

Gabriel said:
On Sep 14, 5:24 am, Thomas 'PointedEars' Lahn <[email protected]>
wrote:
[...]
However, identifying `window' with `this' (i.e., the reference
to the ECMAScript Global Object here) is jQuery's first bug, of course.

I understand that `window' is a property of the Global Object. But
besides semantics, what else could that misconception cause?

Semantics *is* the problem. One object is a host object; the other one is
not. One object has built-in properties that have a special meaning; the
other one doesn't have them, and vice-versa.

Your signature is borken, it must be delimited by a line containing only
"-- " (except newline).


PointedEars
 
L

Lasse Reichstein Nielsen

RobG said:
Also, using:

var undefined = undefined;

means that if some other script that assigns it an unexpected value
runs first, the "local" undefined will get that unexpected value.

Actually not. The variable "undefined" refers to the variable defined
in the same scope. Remember that in Javascript, all variables (and
functions) are declared before the body of a function is executed.
The line above "var undefined = undefined" is equivalent to
"var undefined; undefined = undefined;"

/L
 
T

Thomas 'PointedEars' Lahn

kangax said:
I would replace `undefined` on RHS with `void` (also mentioned here by
PE recently):

var undefined = void 0;

or:

(function(undefined){

// do stuff

})();

Here, you get a "safe" `undefined` with faster resolution. The
difference with global `undefined` (for the code that uses it) is
undetectable (well almost), so the whole thing is rather transparent.

Actually, in a *local* execution context,

var undefined;

suffices.


PointedEars
 
G

Garrett Smith

kangax said:
RobG said:
On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <[email protected]>
wrote: [...]
Taking a copy to be safe against overwriting also makes sense,
especially in library code that might be combined with other
libraries.

I can understand that perspective. However, it woud be better to use:

var UNDEFINED;

When extracting a method to another scope, the extracted method might
need to redefine UNDEFINED (if it references that from the scope chain
it is being extracted from).
I would replace `undefined` on RHS with `void` (also mentioned here by
PE recently):

What is wrong with:-

var undefined;

?
 
R

RobG

Actually not. The variable "undefined" refers to the variable defined
in the same scope. Remember that in Javascript, all variables (and
functions) are declared before the body of a function is executed.
The line above "var undefined = undefined" is equivalent to
"var undefined; undefined = undefined;"

Ah yes, hadn't thought about that!
 
G

Gabriel Gilini

Thomas said:
Gabriel said:
On Sep 14, 5:24 am, Thomas 'PointedEars' Lahn <[email protected]>
wrote:
[...]
However, identifying `window' with `this' (i.e., the reference
to the ECMAScript Global Object here) is jQuery's first bug, of course.
I understand that `window' is a property of the Global Object. But
besides semantics, what else could that misconception cause?

Semantics *is* the problem. One object is a host object; the other one is
not. One object has built-in properties that have a special meaning; the
other one doesn't have them, and vice-versa.
Ok.

Your signature is borken, it must be delimited by a line containing only
"-- " (except newline).

It's that stupid Google Groups, it should be fine now.
 
D

David Mark

These do make some sense.

Not in the bigger picture. And if optimization is in order, why isn't
that this.jQuery and this.$?
Avoiding lookups on the global object does
make a difference (but as with all optimizations - don't do it unless
you have profiled and found it to make a signficant contribution).

And there won't be unfortunate side effects, like memory leaks.
Preserving a reference to the mother of all host objects can't be
considered good practice.
Taking a copy to be safe against overwriting also makes sense,
especially in library code that might be combined with other
libraries.

I don't know. I think I'd want to know if some rogue library was
overwriting my methods. ;)
 
R

RobG

On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <[email protected]>
wrote: [...]
These do make some sense. Avoiding lookups on the global object does
make a difference (but as with all optimizations - don't do it unless
you have profiled and found it to make a signficant contribution).

Your hint about the length of the scope chain got me thinking about
making it longer - so I did.  However, avoiding global lookups still
doesn't make a lot of difference. I also made the comparison in the
same scope as the function running the loop so that only the undefined
lookup should use the scope chain.

IE:
Global undefined level 0: 172
Global undefined level 1: 234
Global undefined level 2: 281
Local level 0: 47
Local level 2: 156

Firefox:
Global undefined level 0: 2
Global undefined level 1: 153
Global undefined level 2: 151
Local level 0: 2
Local level 2: 124

Performance seems to be enhanced more by executing statements as
global code than messing with local declarations of global variables.


I looked at the code again and realised the counter i was still being
looked-up along the scope chain, so I created a local alais. Here's
the new results:

IE
Global undefined level 0: 172
Global undefined level 1: 156
Global undefined level 2: 188
Local level 0: 62
Local level 2: 63

Firefox
Global undefined level 0: 1
Global undefined level 1: 2
Global undefined level 2: 1
Local level 0: 1
Local level 2: 1

So it would seem that global properties like undefined may well
already be optimised and that creating a local scope for such
variables has no measurable performance benefit.

What I was measuring was the lookup of a standard variable on the
scope chain, so aliasing those may make a (big) difference.

Updated code:

function testRef(n) {
var i = n, msg = [], f, s, t;


s = new Date();
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('Global undefined level 0: ' + (f - s));

function level1() {
var i = n, t;
s = new Date();
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('\nGlobal undefined level 1: ' + (f - s));
}

level1();

function level2() {
(function() {
var i = n, t;
s = new Date();
while (i--) {
(t === undefined);
}
f = new Date();
msg.push('\nGlobal undefined level 2: ' + (f - s));
})();
}

level2();

i = n;
s = new Date();
var UNDEFINED;
while (i--) {
(t === UNDEFINED);
}
f = new Date();
msg.push('\nLocal level 0: ' + (f - s));

function local2() {
(function() {
var i = n, t, UNDEFINED;
s = new Date();
while (i--) {
(t === UNDEFINED);
}
f = new Date();
msg.push('\nLocal level 2: ' + (f - s));
})();
}
local2();

document.getElementById('div0').innerHTML = msg.join('<br>');
}

window.onload = function(){testRef(500000);};
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top