Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery performance difference without "each"

Tags:

jquery

Update Added jsfiddle - see bottom of post

I currently have a function that reenables all disabled fields on the screen. While it runs pretty quickly (<1ms according to Firebug profiler), I am in the process of tidying up all the Javascript in my screens and figured this particular function seemed to be a little redundant:

function enableDisabledFields() {
    $('[disabled]').each(function(i) {
        $(this).removeAttr('disabled');
    });
}

I was under the impression that those 3 lines could be replaced as follows, and I expected if not better than at least equal performance.

function enableDisabledFields() {
    $('[disabled]').removeAttr('disabled');
}

Apparently I'm wrong. The first performs much better and I don't quite understand why. Even adding additional selectors such as :input make no difference (and in fact make it worse).

Can anyone clear up my confusion? Thanks.

Edit I should add that we're using an old version of jQuery - 1.3.1 I believe.

Edit2 Here are some jsFiddle links. Please keep in mind that I may be misunderstanding Firebug's profiler (which I'm thinking seems to be the case).

Option 1: http://jsfiddle.net/kcut7/

Option 2: http://jsfiddle.net/ZgZpU/

like image 910
Ben J Avatar asked Dec 15 '10 23:12

Ben J


1 Answers

In jQuery 1.3.1 this is implemented slightly differently to the current versions:

v1.4.4

removeAttr: function( name, fn ) {
    return this.each(function(){
        jQuery.attr( this, name, "" );
        if ( this.nodeType === 1 ) {
            this.removeAttribute( name );
        }
    });
},

v1.3.1

jQuery.each({
    removeAttr: function( name ) {
        jQuery.attr( this, name, "" );
        if (this.nodeType == 1)
            this.removeAttribute( name );
    }
}, function(name, fn){
    jQuery.fn[ name ] = function(){
        return this.each( fn, arguments );
    };
});

The functionality is more or less the same, both use .each() in all cases to process the list. The definition of each has not changed fundamentally at all:

// args is for internal usage only
each: function( object, callback, args ) {
    var name, i = 0,
        length = object.length,
        isObj = length === undefined || jQuery.isFunction(object);

    if ( args ) {
        if ( isObj ) {
            for ( name in object ) {
                if ( callback.apply( object[ name ], args ) === false ) {
                    break;
                }
            }
        } else {
            for ( ; i < length; ) {
                if ( callback.apply( object[ i++ ], args ) === false ) {
                    break;
                }
            }
        }

    // A special, fast, case for the most common use of each
    } else {
        if ( isObj ) {
            for ( name in object ) {
                if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                    break;
                }
            }
        } else {
            for ( var value = object[0];
                i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
        }
    }

    return object;
},

So just a loop using .call() a lot.

I think the only explanation as to a speed increase with an additional outer .each() loop would be the data structure size being much reduced to a single element before being passed down through many layers of calls, which is probably creating local data copies at each level. Passing the whole list through to deal with creates that much more overhead perhaps, would need to profile the heap usage to tell.

like image 121
Orbling Avatar answered Sep 19 '22 11:09

Orbling