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?
<t...@thenewobjective.com> wrote: > 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:
> 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.
> <t...@thenewobjective.com> wrote: > > 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:
> > 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
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).
On Sep 13, 7:51 pm, RobG <robg...@gmail.com> wrote:
> 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.$, ------------------------------------------------------------
> On Sep 13, 7:51 pm, RobG <robg...@gmail.com> wrote:
> > 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.$, > ------------------------------------------------------------
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();
On Sep 13, 10:14 pm, RobG <robg...@gmail.com> wrote:
> 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?
Michael Haufe ("TNO") wrote: > 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:
> 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 wouldn't call it "primitive re-definition". All that's happening here is creation of local variables to speed-up identifier resolution. Zakas recently had a presentation about this exact subject (http://www.youtube.com/watch?v=mHtdZgou0qU) concluding that it is indeed faster *in some cases*. Ditto for property resolution, of course.
What's strange about your particular snippet is aliasing of `true` and `false` values. While `undefined` case somewhat makes sense (it's a plain variable and needs to be resolved against a scope chain all the way up to the global scope) I would be very surprised to see implementations performing better with local variables rather than plain boolean literals. I would assume that literals can be somehow *optimized statically*, whereas variables can only be determined at runtime.
As far as munging, well yes, aliasing something like `undefined` to a local variable will allow compressor to munge those variables:
if (x == undefined || y == undefined || z == undefined) {}
will compress into:
if(x==undefined||y==undefined||z==undefined){}
whereas aliased version:
var UNDEFINED = undefined; if (x == UNDEFINED || y == UNDEFINED || z == UNDEFINED) {}
will compress into a smaller:
var A=undefined;if(x==A||y==A||z==A){}
The difference grows with the amount of occurrences of aliased variables of course.
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 -- Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
>> 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.$, > ------------------------------------------------------------
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 -- Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
> >> 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.$, > > ------------------------------------------------------------
> 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();
RobG wrote: > On Sep 14, 11:00 am, -TNO- <t...@thenewobjective.com> wrote: >> On Sep 13, 7:51 pm, RobG <robg...@gmail.com> wrote:
>>> 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.
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. -- Garrett comp.lang.javascript FAQ: http://jibbering.com/faq/
RobG wrote: > On Sep 14, 11:00 am, -TNO- <t...@thenewobjective.com> wrote: >> On Sep 13, 7:51 pm, RobG <robg...@gmail.com> wrote: >>> 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.$, >> ------------------------------------------------------------
> 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 -- Use any version of Microsoft Frontpage to create your site. (This won't prevent people from viewing your source, but no one will want to steal it.) -- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
Gabriel Gilini wrote: > On Sep 14, 5:24 am, Thomas 'PointedEars' Lahn <PointedE...@web.de> > 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 -- Prototype.js was written by people who don't know javascript for people who don't know javascript. People who don't know javascript are not the best source of advice on designing systems that use javascript. -- Richard Cornford, cljs, <f806at$ail$1$8300d...@news.demon.co.uk>
RobG wrote: > On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com> > 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;
> so that it is obvious within the code that it has been defined within > the script. Also, using:
> var undefined = undefined;
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.
> 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 -- Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
kangax wrote: > RobG wrote: >> var undefined = undefined;
> 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 -- Prototype.js was written by people who don't know javascript for people who don't know javascript. People who don't know javascript are not the best source of advice on designing systems that use javascript. -- Richard Cornford, cljs, <f806at$ail$1$8300d...@news.demon.co.uk>
kangax wrote: > RobG wrote: >> On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com> >> 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).
>> so that it is obvious within the code that it has been defined within >> the script. Also, using:
>> var undefined = undefined;
> I would replace `undefined` on RHS with `void` (also mentioned here by > PE recently):
Garrett Smith wrote: > kangax wrote: >> RobG wrote: >>> On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com> >>> wrote: [...] >>> so that it is obvious within the code that it has been defined within >>> the script. Also, using:
>>> var undefined = undefined;
>> I would replace `undefined` on RHS with `void` (also mentioned here by >> PE recently):
> > 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;"
Thomas 'PointedEars' Lahn wrote: > Gabriel Gilini wrote: >> On Sep 14, 5:24 am, Thomas 'PointedEars' Lahn <PointedE...@web.de> >> 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.
> >> 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.$, > > ------------------------------------------------------------
> 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. ;)
> On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com> > 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();
RobG wrote: > On Sep 14, 5:12 pm, RobG <robg...@gmail.com> wrote: >> On Sep 14, 3:17 pm, Lasse Reichstein Nielsen <lrn.unr...@gmail.com> >> 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.
Let me guess, was it FF3.5.x you were testing with? :)
The thing is that newer engines (especially FF3.5 which, afaik, should have JIT on) are rather indifferent to these optimizations.
It is older ones that show somewhat substantial difference. Here are FF results on my machine (Mac OS X 10.6.1):
FF 3.5.2:
Global undefined level 0: 1 Global undefined level 1: 1 Global undefined level 2: 2 Local level 0: 1 Local level 2: 1
FF 3.0.13:
Global undefined level 0: 82 Global undefined level 1: 83 Global undefined level 2: 83 Local level 0: 24 Local level 2: 24
FF 2.0.20:
Global undefined level 0: 600 Global undefined level 1: 627 Global undefined level 2: 1116 Local level 0: 17 Local level 2: 17
FF 1.5.0.7
Global undefined level 0: 689 Global undefined level 1: 698 Global undefined level 2: 1397 Local level 0: 30 Local level 2: 29
> 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?
The original versions of Javascript did not have an 'undefined' constant, though they did have an undefined value.
For a while, it was common to include the line: var undefined;