Posts mit dem Label javascript werden angezeigt. Alle Posts anzeigen
Posts mit dem Label javascript werden angezeigt. Alle Posts anzeigen

Mittwoch, 25. März 2015

Do Your Weekend Projects!

There is a joke among me and my friends that every programmer should try their skill with the following at least once in their life:


  • a parser / parser-generator / programming language
  • an MVC framework
  • a text editor and/or a word processor
  • a raytracer
  • and just because it's a fetish of mine: a genetic algorithm
Last weekend, I was meddling around with Node.js, just to explore new territories, and I inadvertently treaded on the territory of MVC. And suddenly, a truism hit home that I had read a few times before but never quite grokked before:

You do not do a weekend project in order to get anything done.

An MVC, just as all the other examples above, has been done a trillion times, and most probably there are already thousands of excellent frameworks out there. I couldn't create something that's actually better than that. So why bother?


You do a weekend project because you will learn something, and because it's fun.

(If you're a professional coder, I sincerely hope that those two areas overlap for you. Otherwise you might consider switching jobs.)


I guess I had always had productivity and success somewhere in the back of my mind. "It isn't good if it doesn't sell", or at the very least if it doesn't solve some real-world problem.

Not true. I have fond memories of the time when programming was fundamentally fun and games and geek pride galore. Time to let go a little of the professional cynicism acquired in years of professional coding. Sure, at the job you have to, well, get the job done, and that's your top priority. But in your spare time, a little idealism can help you improve as a coder. I understand MVC better now, I have a little more insight into the nature of dynamically typed languages and DSLs, and I can happily put Node.js into my CV. What's not to love?

So, folks, have your weekend projects, hack away with pride, and DFTA!

Mittwoch, 19. März 2014

Performance Penalties In JavaScript: Recursion vs. Iteration / Native DOM Objects vs. jQuery

In one of my own private projects (codename "Tag Bonsai"), a little chrome extension for working with tags, I ran into some performance issues. I figured this was down to recursive creation of DOM tags, so I ran a little test. (Find the full code at the bottom of this posting!)

The results were astounding.

They were not astounding, though, regarding their general direction. Of course iteration is faster than recursion, and of course native JS objects are faster than their jQuery sires.

What was astounding was the sheer factor of performance penalties involved. On my puter, those are the results when creating 10000 spans:

recursive on jq objects: 11567.999999999302
recursive on js objects: 348.99999998742715
recursive on acc'ed html: 82.00000002398156
iterative on jq objects (without penalty): 10442.000000010012
iterative on js objects (without penalty)201.9999999902211
iterative on acc'ed html (without penalty): 41.99999998672865
iterative on jq objects (with penalty): 9248.00000002142
iterative on js objects (with penalty): 425.99999997764826
iterative on acc'ed html (with penalty): 61.000000016065314
 That's a factor of ~ 190 when writing html manually in an iterative loop, compared to recursively creating jQery objects, even if I penalize the iterative by doing a push and pull each time (because under production circumstances, I'd have to manage a stack of objects by hand).

Here are the results again, ordered by time:
iterative on acc'ed html (without penalty): 41.99999998672865
iterative on acc'ed html (with penalty): 61.000000016065314
recursive on acc'ed html: 82.00000002398156
iterative on js objects (without penalty): 201.9999999902211
recursive on js objects: 348.99999998742715
iterative on js objects (with penalty): 425.99999997764826
iterative on jq objects (with penalty): 9248.00000002142
iterative on jq objects (without penalty): 10442.000000010012
recursive on jq objects: 11567.999999999302

Okay, JS objects are obviously a bit slower than pure html text (by a factor of 3 or so), and recursion drops the speed by another 3rd or so; but jQuery is an awful lot slower than that!

According to this simple test, jQuery objects involve a forbidding impact on performance, especially when combined with recursion. (It's interesting that iteration w/ jQuery and recursion w/ JS objects are relatively close together.) So if you have to create lots of DOM objects, and if you know which browser you use, definitely use the native JavaScript methods!

(I'm not quite sure why some penalized versions seem to be faster than their non-penalized counterparts - there might be some bug in my code, or it's just some random imponderability in my machine; but I think the general direction is still correct and as revealing as a skimpy white dress under heavy rain. It's just staggering.)



Here's the code (The StopWatch class is taken from this Stackoverflow posting.):

            var limit = 10000;
         
            function createSpanRecJqObj(i, el) {              
                var div = $('<span>' + i + '</span>').appendTo(el);
                if (i < limit) {
                    createSpanRecJqObj(++i, div);
                }              
            }
            function createSpanRecHtml(i) {              
                var html = '<span>' + i + '</span>';
                if (i < limit) {
                    html += createSpanRecHtml(++i);
                }
                return html;
            }
            function createSpanRecJsObj(i, el) {
                var span = document.createElement('span');
                span.appendChild(document.createTextNode(i));
                el.appendChild(span);
                if (i < limit) {
                    createSpanRecJsObj(++i, span);
                }
            }
         
         
         
            function createSpanIterJqObj(i, el, penalty) {
                var stack = [];
                if (arguments.length === 2) penalty = false;
                var i;
                for (i = 0; i < limit; ++i) {
                    if (penalty) stack.push(i);
                    el = $('<span>' + i + '</span>').appendTo(el);
                    if (penalty) stack.pop();
                }              
            }          
            function createSpanIterHtml(i, penalty) {              
                if (arguments.length === 1) penalty = false;
                var html = '';
                var stack = [];
                for (var i = 0; i < limit; ++i) {
                    if (penalty) stack.push(i);
                    html += '<span>' + i;
                }
                for (var i = 0; i < limit; ++i) {
                    if (penalty) stack.pop();
                    html += '</span>';
                }
                return html;
            }
            function createSpanIterJsObj(i, el, penalty) {
                var stack = [];
                if (arguments.length === 2) penalty = false;
                console.log('penalty', penalty);
                var i;
                for (i = 0; i < limit; ++i) {
                    if (penalty) stack.push(i);
                    var span = document.createElement('span');
                    span.appendChild(document.createTextNode(i));
                    el.appendChild(span);
                    el = span;
                    if (penalty) stack.pop();
                }
            }
         
         
            $(document).ready(function(){
                var sw = new StopWatch();
                sw.start();
                createSpanRecJqObj(0, $('body'));
                var timeRecJqObj = sw.stop();
                $('#time').append('recursive on jq objects: ' + timeRecJqObj + '<br/>');
             
                sw.start();
                createSpanRecJsObj(0, document.getElementsByTagName('body')[0]);
                var timeRecJsObj = sw.stop();
                $('#time').append('recursive on js objects: ' + timeRecJsObj + '<br/>');
             
                sw.start();
                $('body').append($(createSpanRecHtml(0)));
                var timeRecHtml = sw.stop();
                $('#time').append('recursive on acc\'ed html: ' + timeRecHtml + '<br/>');
             
                sw.start();
                createSpanIterJqObj(0, $('body'));
                timeIterJqObj = sw.stop();
                $('#time').append('iterative on jq objects (without penalty): ' + timeIterJqObj + '<br/>');
             
                sw.start();
                createSpanIterJsObj(0, document.getElementsByTagName('body')[0]);
                timeIterJsObj = sw.stop();
                $('#time').append('iterative on js objects (without penalty)' + timeIterJsObj + '<br/>');
             
                sw.start();
                $('body').append($(createSpanIterHtml(0)));
                timeIterHtml = sw.stop();
                $('#time').append('iterative on acc\'ed html (without penalty): ' + timeIterHtml + '<br/>');
             
                sw.start();
                createSpanIterJqObj(0, $('body'), true);
                timeIterJqObj = sw.stop();
                $('#time').append('iterative on jq objects (with penalty): ' + timeIterJqObj + '<br/>');
             
                sw.start();
                createSpanIterJsObj(0, document.getElementsByTagName('body')[0], true);
                timeIterJsObj = sw.stop();
                $('#time').append('iterative on js objects (with penalty)' + timeIterJsObj + '<br/>');
             
                sw.start();
                $('body').append($(createSpanIterHtml(0, true)));
                timeIterHtml = sw.stop();
                $('#time').append('iterative on acc\'ed html (with penalty): ' + timeIterHtml + '<br/>');
             
            });

Mittwoch, 19. Februar 2014

Optional Parameters in Javascript

Want a nice laugh? Here it comes.

For years, I've been hacking in JavaScript, as it were, "on the side". Just as a necessary, sometimes fun, often loathesome part of the webproject experience.

When doing optional parameters, I always did this:

function foo(a) {
    if (arguments.length === 2)
        b = arguments[1];
    else
        b = 'bar';
}

The problem with this is so obvious I won't even spell it out.

Here's the puncher.

What I somehow managed to miss until today, is that you can also do this:

function foo(a, b) {
    if (typeof(b) === 'undefined')
        b = 'bar';
}

Shorter, sweeter, more obvious. If I'd just known this a bit earlier.