Efficient JavaScript

Forums » Dev.Opera » Archived Article Discussions

This topic has been closed. No new entries allowed.

Reason: You can now post comments on articles on Dev Opera

Forum rules and guidelines

You need to be logged in to post in the forums. If you do not have an account, please sign up first.

Go to last post

2. November 2006, 16:19:29

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Efficient JavaScript

As Web pages become more like applications, the performance of scripts is having a bigger effect. Learn how to make your Web applications more efficient through a series of simple changes.

( Read the article )

Core QA
Opera Software ASA

2. November 2006, 18:11:04

robodesign

{%title%}

Posts: 309

Great article. It's precisely what I was interested into reading.

I have several questions/suggestions.

In "<a href="http://dev.opera.com/articles/view/48/?page=2#avoidwith">Avoid using with</a>":
var testObject = test.information.settings;
testObject.files.primary = 'names';

Could be rewritten as:
var testObject = test.information.settings.files;
testObject.primary = 'names';
...

If I am not mistaken smile.

Also, regarding loops. I optimize my scripts using:
var i, n = arr.length;
for( i = 0; i < n; i++ ) {
  ...
}

instead of:
for( var i = 0; i < arr.length; i++ ) {
  ...
}

Is this true optimization or not? Checking for the length might be more time consuming.
<a href="http://www.robodesign.ro">ROBO Design - We bring you the future</a>

2. November 2006, 18:34:37

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by robodesign:

In "Avoid using with":
var testObject = test.information.settings;
testObject.files.primary = 'names';
Could be rewritten as:
var testObject = test.information.settings.files;
testObject.primary = 'names';



Yep. I was supposed to change that earlier, but got confused by a typo wink
Thanks for the reminder. Fixed now.

Originally posted by robodesign:

I optimize my scripts using:
var i, n = arr.length;
for( i = 0; i < n; i++ ) {
...
}
instead of:
for( var i = 0; i < arr.length; i++ ) {
...
}
Is this true optimization or not?



This depends very much on what you do inside the loop.
In a basic loop, there is no real difference, since the engine should be able to cache the length for you anyway.

If you change the array inside the loop, it will make a difference, since the optimisations may not be possible - especially if you change the length. How much of a difference depends on what you are doing. Changing the contents of existing cells has a very small influence only, your optimised loop is perhaps just a few percent faster at most (2% in my test in Opera). Changing the length will make a much bigger difference, but your caching will fail to work with the updated length anyway, so it would probably not be a useful optimisation in that case.

If the array is not a real array, such as a DOM NodeList, and you modify the DOM inside the loop, it makes a very big difference, and your optimised loop is much better. This point is covered in the DOM chapter:
http://dev.opera.com/articles/view/48/?page=3#traversemodify
http://dev.opera.com/articles/view/48/?page=3#traversemodify
Core QA
Opera Software ASA

2. November 2006, 18:38:44

robodesign

{%title%}

Posts: 309

Thanks for the reply. The DOM NodeList is the most important one, as I expected.
<a href="http://www.robodesign.ro">ROBO Design - We bring you the future</a>

2. November 2006, 18:49:58

Kildor

Posts: 153

http://dev.opera.com/articles/view/48/?page=3#stylechanges
Why do not use
posElem.style='rules'

It work in all current browsers, or not?
Again Open standarts versus real world?
All what you want, and blade of the rose indeed...

2. November 2006, 20:54:26

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by Kildor:

Why do not use
posElem.style='rules'



To my knowledge there is no requirement for a browser to allow this. The DOM 2 CSS spec specifically says it should be read-only; 'readonly attribute CSSStyleDeclaration style;' - although this may change in future versions.

Opera and Safari/Konqueror allow you to write to it anyway. Firefox and iCab do not (Firefox throws an error).

I prefer to use the standard approach wherever possible. In addition, this part of the code is used if the browser does not support cssText - which iCab does not. Since I aim to get maximum compatibility, I will use the standard approach, as that does work in iCab, while the one you suggested does not. (NetFront and ICE are the same.)
Core QA
Opera Software ASA

4. November 2006, 03:22:50

mr7clay

Posts: 38

Re: implicit object conversion, this is a real eye-opener. I guess the same is true for array and object literals like [] and {}... If you're going to call length or an Array method even once, you might as well get it over with and define the variable with "new".

It's nice to see the focus on generally vendor-neutral performance wins. I think too many go into topics like loop unrolling, trying to outsmart the compiler and I wonder if these end up <em>preventing</em> better native optimizations.

4. November 2006, 03:37:04

johnnysaucepn

In a maze of twisty little messages, all alike

Posts: 7853

Originally posted by mr7clay:

I wonder if these end up preventing better native optimizations.


They probably prevent future native optimisations as well.

4. November 2006, 13:16:38

lth

Posts: 3

A gentle correction to Tarquin's post: consider the loop

for ( i=0 ; i < a.length ; i++ )
...

the compiler or execution engine can't really precompute and save the value of a.length to avoid recomputation at each iteration. It can only do so when the contents of the body can't affect the value of that expression. With the current language there are limited opportunities for that; with getters and setters in the language the opportunities will be even fewer.

And as for what mr7clay writes, the expressions [...] and {...} are not array and object literals, but array and object initializers. The expression [1,2,3] is for all practical purposes the same as

tmp = new Array();
tmp[0]=1;
tmp[1]=2;
tmp[2]=3;
tmp

The same for object initializers.

--lars, primary ECMAScript engine maintainer at Opera

4. November 2006, 13:44:08

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by mr7clay:

Re: implicit object conversion, this is a real eye-opener. I guess the same is true for array and object literals like [] and {}



No. With arrays and objects, the short form and the object constructor are equivalent, since an array and object do not have a primitive value. They are not literals, and are always an object. I am not so sure about what category RegExp fall into, but I think they are also always objects - their performance seems to agree.

You can try it this way:

alert( typeof( 'foo' ) ); // -> string
alert( typeof( new String('foo') ) ); // -> object

alert( typeof( [] ) ); // -> object
alert( typeof( new Array() ) ); // -> object
Core QA
Opera Software ASA

4. November 2006, 13:48:22

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by lth:

A gentle correction to Tarquin's post: consider the loop



heh, yes, sorry, I was trying to correct myself, but I am unable to edit posts here - teething troubles with the forum it seems smile

My rewording would have been: -

Imagine that you change the array inside the loop. It will make a difference, since the engine will not be able to cache the length for you. In fact, since it is difficult to know in advance if your code will change the length (since you may be referencing the array via any number of different ways), caching the length may not be easily possible anyway. So your optimisation will make a difference.

How much of a difference depends on what you are doing. In a basic loop, it has a very small influence only, your optimised loop is perhaps just a few percent faster at most (2% in my test in Opera). Changing the length inside the loop may make a bigger difference in some browsers, but your caching will fail to work with the updated length anyway, so it would probably not be a useful optimisation in that case.

-

I also got the second link in that comment wrong - should have been:
http://dev.opera.com/articles/view/48/?page=3#cachevalues
Core QA
Opera Software ASA

6. November 2006, 06:47:18

MarkSchenk

~ The Magus ~

Posts: 650

Thanks for the article Tarquinwj, I'll be taking these aspects into account in my future coding projects.

About measuring JavaScript performance: do you happen to have any experience with (free) JavaScript profilers, that might help to isolate performance bottlenecks?

I'm working on a javascript project that's slow as molasses in Firefox, but flies in Opera, and before I completely attribute this to Opera's faster engine, I want to see if there is something I could optimize. I tried downloading Venkman, but my bright and shiny FF2 refuses to connect to the addon servers... so much for extensibility...

6. November 2006, 09:17:52

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by MarkSchenk:

About measuring JavaScript performance: do you happen to have any experience with (free) JavaScript profilers, that might help to isolate performance bottlenecks?



Yes. There is a fairly simplistic free one. It's called 'new Date()'. It's resolution is a little low, typically 10ms is your minimum. But if you have something running exceptionally slow, then it may just be enough for you to see it.

<pre><code>var recordTimes = [];
function noteTime(oName) {
recordTimes[recordTimes.length] = [new Date(),oName];
}
function showTimings() {
if( !recordTimes.length ) { return; }
var lastDate = recordTimes[0][0];
for( var n = 0, s = ''; n < recordTimes.length; n++ ) {
s += '<tr><td>'+recordTimes[n][1]+'<\/td><td>'+(recordTimes[n][0].getTime()-lastDate.getTime())+'<\/td><\/tr>';
lastDate = recordTimes[n][0];
}
var foo = window.open('','_blank');
foo.document.write('<table>'+s+'<\/table>');
foo.document.close();
}</code></pre>

Then wherever you want to log the time, use:

noteTime('some name');

And to see the result, use this bookmarklet:

javascript:showTimings();

It will show you the intervals between times that you took notes. If you see some that are sufficiently long, then that will probably be the cause of the slowdown.

Originally posted by MarkSchenk:

I tried downloading Venkman, but my bright and shiny FF2 refuses to connect to the addon servers.



Managed to download it without any problem (searched for it on my favourite search engine, and downloaded it from the project site). Don't know how to use it, though - it just gave me a load of useless information about the scripts in its own chrome (even though the option to ignore them was ticked), and nothing about my script smile I am sure this is just because I have not used it before though.

But if you still have trouble, I am afraid I have to point you to the owners of the project, since I do not have enough experience with it.
Core QA
Opera Software ASA

6. November 2006, 14:31:00

MarkSchenk

~ The Magus ~

Posts: 650

Originally posted by tarquinwj:


Yes. There is a fairly simplistic free one. It's called 'new Date()'.



:-)

Thanks for the code. I should have thought of this... it's like the tic/toc functions in Matlab. With some adapting it might actually be able to do what I want; the problem most likely lies in a function called during a loop, so I'll have to sum all the time spent in that called function.

Originally posted by tarquinwj:

Managed to download it without any problem (searched for it on my favourite search engine, and downloaded it from the project site). Don't know how to use it, though - it just gave me a load of useless information about the scripts in its own chrome (even though the option to ignore them was ticked), and nothing about my script smile



Same here, I downloaded the extension in my favourite browser, but couldn't get the extension to do anything useful; just debug problems for its chrome.

6. November 2006, 19:15:13

robodesign

{%title%}

Posts: 309

Originally posted by MarkSchenk:

Same here, I downloaded the extension in my favourite browser, but couldn't get the extension to do anything useful; just debug problems for its chrome.

Last time I used Venkman, it was "OK". It helped me. However, that's a LONG time ago (and it was with the Mozilla Suite, not with Firefox). I never had Venkman in Firefox. I remember I didn't like it very much for being overly complex.

I do recommend you to try <a href="http://joehewitt.com/software/firebug/">Firebug</a>. It's a Firefox extension which provides many very good features, including a working debugger that I found easy and nice to use.
<a href="http://www.robodesign.ro">ROBO Design - We bring you the future</a>

6. November 2006, 19:38:27

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by robodesign:

I do recommend you to try Firebug. It's a Firefox extension which provides many very good features, including a working debugger that I found easy and nice to use.



It does look a lot more comfortable than the other one, but no profiling that I could see. So I doubt this would be useful given the problem that Mark was trying to debug, unfortunately. Or did I miss it?
Core QA
Opera Software ASA

6. November 2006, 20:28:11

robodesign

{%title%}

Posts: 309

Originally posted by tarquinwj:

It does look a lot more comfortable than the other one, but no profiling that I could see. So I doubt this would be useful given the problem that Mark was trying to debug, unfortunately. Or did I miss it?

Yes, no profiling (AFAIK). Firebug is a good replacement for Venkman, as a JS debugger.
<a href="http://www.robodesign.ro">ROBO Design - We bring you the future</a>

7. November 2006, 10:17:51

krang

Posts: 1

Hi tarquinwj, thanks for a great article!

Just wanted to know, if on...

http://dev.opera.com/articles/view/48/?page=3#traversemodify

Would it have been possible to do...

----------------------
var allPara = document.getElementsByTagName('p');
for (var i = (allPara.length - 1); i >= 0 ; i--) {
allPara[ i ].appendChild(document.createTextNode(i));
}
----------------------

This does take some head scratching as the array is processed in reverse, but it means that the "length" is only used once.

However there are times when this does not work... in which case a "var allParaLen = allPara.length" before the "for" loop can help.

Not sure if I have missed the point though... other than that slight change, a great article!

Thanks again,
Craig Francis

8. November 2006, 10:24:17

crisp

Posts: 19

This article should come with a notice "beware of premature optimization and browser-specific implementations" wink

I was fascinated by the 'implicit object conversion' item and decided to do some tests with it in various browsers. For one I noticed that there was not a notable difference in performance in Opera itself, but there was a +/- 10% difference in other browsers (IE7 and Firefox 2.0) where the 'improved' example was actually slower. A far better optimization would be to rewrite the for-loop since that's where most of the time is spent - evaluating the length property with every iteration. This is a many times faster in Firefox:

var s = '0123456789';
for( var i = 0, l = s.length; i < l; i++ )
{
s.charAt(i);
}

but then again a.m. example is actually slower in Opera - it all boils down to the implementation and my advice would be to test each and every optimization in various browsers to find out what actually gives the best result in all of them.

9. November 2006, 10:52:11

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by krang:

Would it have been possible to do...
for (var i = (allPara.length - 1); i >= 0 ; i--) {



Yes.
Core QA
Opera Software ASA

14. November 2006, 03:48:18

xErath

javascript guru

Posts: 6588

There's an error in section Making several style changes at once
if( typeof( posElem.style.cssText != 'undefined' ) ) {

Should be
if( typeof( posElem.style.cssText ) != 'undefined' ) {


For a collection of user scripts visit
http://my.opera.com/xErath/blog/

14. November 2006, 15:01:16

Opera Software

tarquinwj

Taking DHTML to level 5

Posts: 546

Originally posted by xErath:

There's an error in section Making several style changes at once



Thankyou. Fixed.

I also added a note about implicit object conversion being (unfortunately) opera-centric.
Core QA
Opera Software ASA

16. November 2006, 12:21:01

Here are three little tricks I often use if they are inside loops:

------------------------------------------

I find that this is a good optimisation for charAt() inside heavy loops:

Original:
var s = new String('0123456789');
for( var i = 0, len = s.length; i < len; i++ ) {
  s.charAt(i);
}


Optimised:
var s = new String('0123456789');
var arr = s.split('');
for( var i = 0, len = arr.length; i < len; i++ ) {
  s[i];
}


This is avoids separate function calls for each letter by making a temporary array.

------------------------------------------

Instead of using Math.round(Math.random()*10), you can use (Math.random()*10)>>0. A bitwise shift of zero rounds down and avoids the extra function call.

------------------------------------------

There is only really one proper way of swapping two variables but if the variables being swapped are numbers then there are many ways:

Temporary variable:
var temp = a;
a = b;
b = temp;


Additions and subtractions:
a += b;
b = a - b;
a -= b;


Bitwise:
a ^= b;
b ^= a;
a ^= b;


However this next one I have found to by far the fastest in all major browsers, it takes advantage of the order of operations in javascript:
a = b + 0*(b=a);


------------------------------------------

These, as I said are only necessary if they are looped through many times.

17. November 2006, 10:34:10

Moderator

jax

Posts: 7094

Originally posted by crisp:

my advice would be to test each and every optimization in various browsers to find out what actually gives the best result in all of them.



The goal of the Efficient series is to highlight good practices that should, at least in theory, be fairly browser independent.

While testing the result in different browsers is necessary, the result won't only vary from browser to browser, but from browser version to browser version. If one method is much slower in one browser than another, there are good chances that it will be better optimised in the next version. However, if one method is consistently much slower among all browsers it is likely that that method is inherently more inefficient (or that nobody has figured out a way to make it efficient yet).
This sig <a href="http://my.opera.com/community/forums/topic.dml?id=1132152">intentionally broken</a> by My Opera devs...

18. November 2006, 19:50:20

Netegrof

Posts: 380

How I can add that JavaScript to work in Opera???.

19. November 2006, 21:20:13

crisp

Posts: 19

Originally posted by jax:

The goal of the Efficient series is to highlight good practices that should, at least in theory, be fairly browser independent.


And I may add that you are doing a better job at that than Microsoft with their performance recommendations wink

I still think that a warning for premature optimization would be good. That doesn't count for all tips here, most of them can indeed be labelled 'good practice', but some could degrade readability and maintenability of your code and such optimizations should only be used as a last resort and when it is absolutely necessary. Most of the time however you may find that performance issues are due to general bad design of your code or the overly use of abstrations that in itself slow things down ($() anyone?).

16. December 2006, 21:19:16 (edited)

liorean

Posts: 3

About building strings: I've done some performance checking on the building of (massive) strings in various browsers. My tip here depends on browser relevance - Internet Explorer/JScript is several orders of magnitude slower than the other browsers and consumes extreme amounts of memory doing it if you build strings using the + operator, the += operator or even the String.prototype.concat method. For JScript, the ONLY efficient way of building a large string from many small strings is using the Array.prototype.join method.

Other browsers?

If all the small strings are compile time constants, Mozilla-Seamonkey-Firefox-Camino-Gecko/SpiderMonkey seems to be the only browser doing compile time string constant folding. This means that the browser will add the strings together when parsing instead of when executing and thus you don't have lots of intermediaries, the string building doesn't consume processing time, there's no risk of triggering GC or making the browser generally unresponsive while doing it, etc.
However, most of the time, the string isn't made from lots of compile time constants, it's built from runtime data. So, this optimisation, while nice to experience, doesn't really help in the majority of cases. In more common usage, SpiderMonkey has best performance using String.prototype.concat, and worst performance using Array.prototype.join, with += and + operators falling somewhere in the middle.

For Opera, the performance is best using String.prototype.concat, but with just a small difference for the other methods. Also, Opera is blazingly fast compared to SpiderMonkey.

For Safari-OmniWeb-Shiira-Swift/WebKit browsers, String.prototype.concat has best performance, followed by Array.prototype.join. Here it can be noted however that performance of the + operator or += operator is really bad. WebKit using String.prototype.concat has a performance similar to Opera using the same method, but using the + operator takes on the order of ten times more time in WebKit compared to Opera.

CONCLUSION: The performance loss of using Array.prototype.join for SpiderMonkey (or the much more modest performance loss in WebKit and Opera for that matter) is more than compensated for by the fact it's the ONLY method of string building that has reasonable performance in JScript. However, if JScript performance is not of relevance, String.prototype.concat has consistently best performance.


Some data: <a href="http://testsuite.liorean.net/stringconcatenationperformance.html">a very artificial and contrived test</a> - a test which builds a single string from ten thousand random small strings in hundred iterations (With the exception of JScript only doing a single iteration. Why? Because that single iteration takes about as much time as a hundred iterations does in the others.)

________________ff2.0_____op9.10_____swift0.2_________ie6w__
String concat   1 063ms      500ms        468ms      1 094ms
Array join      3 657ms      610ms        547ms         47ms*
+ op/vars       1 078ms    1 140ms      8 078ms      1 453ms
+ op/lits           0ms      938ms     10 782ms      1 125ms
+= op           1 484ms    1 110ms      5 984ms      4 625ms

NOTE: ie6w timings are doing ONE iteration,
other browser timings are doing ONE HUNDRED iterations.

* This one I tried with 100 iterations as well. The result then was 515ms.

19. December 2006, 22:19:36

MarkSchenk

~ The Magus ~

Posts: 650

A follow-up to my earlier question about profiling javascript. I downloaded and installed the Firebug extension for Firefox today, to sort out some performance problems. It *does* include a profiler, and some handy JS debugging stuff:

http://getfirebug.com/js.html

All in all it's one hell of an extension! The DOM/CSS editor is very well done, and Opera should definitely take a cue from this extension, for the implementation of their own webdevelopment tools.

A pity this great extension only serves to show FF's performance current JS problems, compared to Opera :-)

29. December 2006, 21:55:56

OperaTheBest2

Posts: 1

My code is efficient?
http://agoohttp.atspace.com/AproLang/manual.html

(I use only Opera on Linux)

Where I can post my open source code, on dev.opera ?
Thanks

Bye

11. May 2007, 23:06:04

lovedada

Posts: 11

Nice article. I'm particularly interested in http://dev.opera.com/articles/view/efficient-javascript/?page=2#forin about avoiding for (<key> in <object>). Specifically, "The for-in loop requires the script engine to build a list of all the enumerable properties, and check for duplicates in that list, before it can start the enumeration". Why would iterating throug an associative array entail removing duplicates ? Wouldn't any reasonable implementation remove duplicates on entry to the array ?

I filed a bug (261749) a few weeks ago about how large associative arrays in Opera perform very poorly compared to other browsers but have not heard a thing about it so far.

9. August 2007, 04:20:13

echagaray

Posts: 1

Hi. Just new to this forum site, I just want to share something.

I found this guy's script, after searching for a more simple javascript rollover script. It comes from a guy on the internet named Jehiah Czebotar. Just want to see what you guys think. It also preloads the images without extra effort.

In the head or <a href="http://www.aftermarketperformanceparts.com/headers.html">headers</a> it goes like this:

<script type="text/javascript">
function SimpleSwap(el,which){
el.src=el.getAttribute(which ¦¦ "origsrc");
}
function SimpleSwapSetup(){
var x = document.getElementsByTagName("img");
for (var i=0;i<x.length;i++){
var oversrc = x.getAttribute("oversrc");
if (!oversrc) continue;

// preload image
// comment the next two lines to disable image pre-loading
x.oversrc_img = new Image();
x.oversrc_img.src=oversrc;
// set event handlers
x.onmouseover = new Function("SimpleSwap(this,'oversrc');");
x.onmouseout = new Function("SimpleSwap(this);");
// save original src
x.setAttribute("origsrc",x.src);
}
}
var PreSimpleSwapOnload =(window.onload)? window.onload : function(){};
window.onload = function(){PreSimpleSwapOnload(); SimpleSwapSetup();}
</script>

In the body it goes like this:

<img src="image1.gif" oversrc="image2.gif">

9. August 2007, 09:13:39

@echagaray:
For hover effects you are better off avoiding javascript and using CSS:

<style type="text/css">
#myPic {
  display: block;
  width: 200px;
  height: 100px;
  background-image: url(image1.gif);
}
#myPic:hover {
  background-image: url(image2.gif);
}
</style>


<a href="# id="myPic"></a>

21. September 2007, 17:52:00

misha3d

Posts: 14

Excellent article.

Though I noticed that changing a number of elements is about 20% faster than cloning the parent node, doing the canges to the cloned tree fragment and then exchanging the nodes.


Example:
I have 150 div's inside a node. I'm moving all 150 div's 25 times per second by iterating through them.
This doesn't cause any reflow to the document, so it is solely a repaint.
The example can be found at: http://playground.3d-m.de/js_starfield_alpha.html

Modifying the nodes directly occupies my MacBook Pro 2.16GHz 43%.

Cloning the parent and doing the changes to the cloned nodes keeps the system loaded at 53%.

The difference is even more obvious with Mozilla based browsers.

19. January 2008, 01:30:51

justinbmeyer

Posts: 1

On using document.write to load script tags ...

Is there a way to have Opera not load JavaScript immediately. IE, FF, and Safari load after the current 'thread' of execution terminates. For example:

I load a file (first.js) that calls alert(1), and then call alert(2)

<script language="javascript" type="text/javascript">
document.write("<script type='text/javascript' src="first.js"><\/script>");
alert(2)
</script>

In other browsers, I see 1 then 2. In Opera, 2 then 1. Although Opera's way makes more sense. I'm trying to load scripts in a consistent order. I've written a script to help with this. Unfortunately, I can't get it to work with Opera.

Eventually, I want to make an automatic compressor. Developers will be able to include any JS file they want. My compressor will parse the JS files, and collect all the included JS.

But, I need to get this working in Opera first.

28. February 2008, 09:09:32

gregersrygg

Posts: 2

Originally posted by justinbmeyer:

Eventually, I want to make an automatic compressor. Developers will be able to include any JS file they want. My compressor will parse the JS files, and collect all the included JS.



You know there are several tools for compressin and including javascript already...?
Just to name a few:

Yahoo! YUI Compressor:
http://developer.yahoo.com/yui/compressor/ (jar based on Rhino)
http://www.julienlecomte.net/blog/2007/09/16/ The author of the Yahoo compressor has a blog entry how to use it in your ant build.
I personally believe this is the best tool for compressing JS safely.

Include:
http://javascriptmvc.com/learningcenter/include/index.html
Recently announced on Ajaxian: http://ajaxian.com/archives/include-pack-your-javascript-with-ease

JSLoader:
http://vps.jsloader.com/
On Ajaxian a while ago (lots of good comments as well) http://ajaxian.com/archives/jsloader-on-demand-javascript-libraries

JSAN (CPAN for JS)
http://openjsan.org/

Maybe none of them is what you're looking for, but they might help you solve your problem ;)

28. February 2008, 09:37:23

gregersrygg

Posts: 2

Oh, I almost forgot. Here is a browser-independent profiler from Yahoo:
http://developer.yahoo.com/yui/profiler/

You have to specify what functions / objects / classes you want to profile, but it helps a lot to find bottlenecks without Firebug.

23. May 2008, 15:08:26 (edited)

lilthea

Posts: 3

Hello, I have JavaScript in a website that I built a few years ago and it does not work in Opera. I have added a work around that lets it work in Netscape and early IE, is there a similar work around for Opera? My problem is that I do not know a whole lot about JavaScript and I don't understand the article well enough to fix the script myself. Here is the code:

ns3up = (browserName == "Netscape" && browserVer >= 3);
ie4up = (browserName.indexOf("Microsoft") >= 0 && browserVer >= 4);

function doPic(imgName) {
if (ns3up || ie4up) {
imgOn = ("" + imgName);
document.mainpic.src = imgOn;
}
}

This part doesn't show up, so I'm removing the <>
a href="javascript:doPic('furniture/furniture5.jpg');" <img src="furniture/furniture5.jpg" width="100" height="100" border="0" alt="Modern Tansu" /></a>

Thank you in advance for any help you can give me.

27. May 2008, 10:19:41

motorfest

Posts: 13

script type="text/javascript">
function SimpleSwap(el,which){
el.src=el.getAttribute(which ¦¦ "origsrc");
}
function SimpleSwapSetup(){
var x = document.getElementsByTagName("img");
for (var i=0;i<x.length;i++){
var oversrc = x.getAttribute("oversrc");
if (!oversrc) continue;

x.oversrc_img = new Image();
x.oversrc_img.src=oversrc;
x.onmouseover = new Function("SimpleSwap(this,'oversrc');");
x.onmouseout = new Function("SimpleSwap(this);");
x.setAttribute("origsrc",x.src);
}
}
var PreSimpleSwapOnload =(window.onload)? window.onload : function(){};
window.onload = function(){PreSimpleSwapOnload(); SimpleSwapSetup();}
</script>

I so understand this code for the load of image at simple level. for me for some reason in a body does not want to represent pictures. This code taken for optimization of load of pages?

<img src="image1.gif" oversrc="image2.gif">

30. May 2008, 11:20:50

evilopera

Posts: 10

Good afternoon. good article. Never heard about YUI compressor. Very necessary thing.

I noticed that my js had become in 2 times quick!


thank you all

JavaScript - - - - Efficient

2. August 2008, 21:16:26

davidsarah

Posts: 1

I think it's useful to understand which constructs should be avoided
because they necessarily impose a significant overhead, and what is
slow simply due to poor implementation of current JavaScript
interpreters.

In the global variable example, it is clear that s and i are free
identifiers of testfunction (and we can see statically there there are
no uses of 'with' or 'eval' in that scope), so there should be no need
to walk the scope chain. It's true that the global variable accesses
require a hashtable lookup, but since the hash value can be
precomputed, that should not be significantly more expensive than
the local variable lookup in the common case where no collision
occurs. So the 30% performance difference is a sign that the
implementation is not doing some really straightforward optimizations.

(I'm not arguing for the use of global variables. There are plenty
of more important reasons to avoid them than performance.)

In the 'implicit object conversion' section, you say:

"When you reference a property or method of a string value, the
ECMAScript engine must implicitly create a new string object with
the same value as your string, before running the method."

There's no "must" about it. It is completely trivial for an
implementation to use the same representation for string values as
for objects, so that string values can have methods that are called
like any other method. The program can't tell the difference unless
it modifies String.prototype, e.g. if it does something like:

String.prototype.toString = function() {
if (!String.prototype.isPrototypeOf(this)) {
alert("You tried to optimize and got caught");
}
return this;
}

but it is easy enough to do the string value -> String object
conversion only in that case.

(Actually the distinction between string values and String objects
in ES3 is just plain stupid -- an example of standardizing what a
particular implementation did rather than thinking carefully about
the language design. There are plenty of instances of that in ES3.
My point is that an implementation *can* work around the performance
overhead while still being strictly compliant with the poorly
thought out spec.)

The string concatenation would also be fairly easy to optimize. It's
complicated a little by the fact that + is overloaded to behave
differently for strings vs non-strings, and due to the [[DefaultValue]]
coercions. However, an implementation can still test for the common
case where all arguments (including the LHS when += is used) are
string values, and then only create a single new string value,
regardless of how many consecutive + operations there are. Java
compilers do a similar optimization (admittedly they have more type
information), so it can't be that hard.

--
David-Sarah Hopwood

20. August 2008, 05:28:26

jmneter

Posts: 4

Originally posted by tarquinwj:

Originally posted by robodesign:

I optimize my scripts using:
var i, n = arr.length;
for( i = 0; i < n; i++ ) {
...
}
instead of:
for( var i = 0; i < arr.length; i++ ) {
...
}
Is this true optimization or not?



This depends very much on what you do inside the loop.
In a basic loop, there is no real difference, since the engine should be able to cache the length for you anyway.

If you change the array inside the loop, it will make a difference, since the optimisations may not be possible - especially if you change the length. How much of a difference depends on what you are doing. Changing the contents of existing cells has a very small influence only, your optimised loop is perhaps just a few percent faster at most (2% in my test in Opera). Changing the length will make a much bigger difference, but your caching will fail to work with the updated length anyway, so it would probably not be a useful optimisation in that case.

If the array is not a real array, such as a DOM NodeList, and you modify the DOM inside the loop, it makes a very big difference, and your optimised loop is much better. This point is covered in the DOM chapter:
http://dev.opera.com/articles/view/48/?page=3#traversemodify
http://dev.opera.com/articles/view/48/?page=3#traversemodify




This is very useful for me, thank you, guys!

20. August 2008, 06:11:31

jmneter

Posts: 4

I have to save this to my favorite, there are too many informations and I like them all!

22. June 2010, 12:18:12

bensihem

Posts: 1

Originally posted by crisp:


var s = '0123456789';
for( var i = 0, l = s.length; i < l; i++ )
{
s.charAt(i);
}


Like others (crisp,...), I made some performance-tests concerning implicit and explicit object-conversion, but I got exactly the opposite results in all browsers !

So either my performance-test-code or the advise of the author of this article ("Efficient JavaScript") must be wrong.

Here's my, I must say, very simple code :

/* -------------------------------------------------- */

var time1 = new Date().getTime();

var txt = "Loooooong String..."; // the string I used, was about 10000 characters.

//var txt = new String("Loooooong String...");

// Obviously, I was testing one OR the other txt variable.

for( var i = 0, l = txt.length; i < l; i++ ) {
txt.charAt(i);
}

var time2 = new Date().getTime();

document.getElementById("test").innerHTML = time2 - time1 ;

/* -------------------------------------------------- */

Result : It took longer with new String... than with a simple string litteral !
So, were's the truth ?

Does someone have serious links about this topic ?

Thanks.

P.s.: Internet becomes more and more a vast copy/paste playground and the dream of "Universal Access to Knowledge" turns out to be a nightmare. The idea of "transclusion" should become a internet-law...



16. January 2011, 12:12:32

Nailz

Posts: 754

Dead links?

Getting "500 Internal Server Error" when I try and load page 2, 3 or 4.

17. January 2011, 12:36:32

Opera Software

chrismills

Posts: 379

Originally posted by Nailz:

Getting "500 Internal Server Error" when I try and load page 2, 3 or 4.



My apologies about this - we already know about this and are currently trying to get it fixed asap.
Chris Mills
Developer Relations Manager
Editor, dev.opera.com and labs.opera.com

Forums » Dev.Opera » Archived Article Discussions