2

Why jquery code (https://github.com/jquery/jquery/blob/master/src/callbacks.js) does not use prototype for methods?

I am asking this because experts suggest to use prototype for methods for performance reasons( As functions only gets created once).

Code snippet from jquery code,

self = {
            // Add a callback or a collection of callbacks to the list
            add: function() {
                if ( list ) {
                    // First, we save the current length
                    var start = list.length;
                    (function add( args ) {
                        jQuery.each( args, function( _, arg ) {
                            var type = jQuery.type( arg );
                            if ( type === "function" ) {
                                if ( !options.unique || !self.has( arg ) ) {
                                    list.push( arg );
                                }
                            } else if ( arg && arg.length && type !== "string" ) {
                                // Inspect recursively
                                add( arg );
                            }
                        });
                    })( arguments );
                    // Do we need to add the callbacks to the
                    // current firing batch?
                    if ( firing ) {
                        firingLength = list.length;
                    // With memory, if we're not firing then
                    // we should call right away
                    } else if ( memory ) {
                        firingStart = start;
                        fire( memory );
                    }
                }
                return this;
            },
            // Remove a callback from the list
            remove: function() {
                if ( list ) {
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            list.splice( index, 1 );
                            // Handle firing indexes
                            if ( firing ) {
                                if ( index <= firingLength ) {
                                    firingLength--;
                                }
                                if ( index <= firingIndex ) {
                                    firingIndex--;
                                }
                            }
                        }
                    });
                }
                return this;
            },

My question is why all methods are not part of a separate object which becomes prototype for self object?

Community
  • 1
  • 1
SunnyShah
  • 28,934
  • 30
  • 90
  • 137
  • performance is not always the most important factor. If you really need performance, then by all means follow advice that promotes it.... but if you really need performance above all else, you probably aren't using jQuery. – SDC Jan 11 '13 at 14:28
  • 1
    That's one of those questions where you might find better help on some jQuery dev mailing list or forum. I don't know how many jQuery developers are on SO who worked on this part of the code. – Felix Kling Jan 11 '13 at 14:29
  • @SDC it's not necessarily about performance (Do you ever need to create Callbacks instances inside a tight loop?) but about memory and in my opionion, harmony inside the project. The rest of the project is using prototypal inheritance for this kind of things (See Event, Tween, jQuery itself) – Esailija Jan 11 '13 at 14:37
  • Another expert, Douglas Crockford, recommends this approach, arguing that it avoids the requirement of using the new operator and therefore the risk of forgetting to use the new operator. See his book _JavaScript: The Good Parts_ for more. Incidentally, you might get a more authoritative response to the question on the jQuery formum http://forum.jquery.com/developing-jquery-core. – dgvid Jan 11 '13 at 14:39
  • 2
    @dgvid, Well I dont think that advice is relevant any more. "use strict" creates a global variable this and initialize it with undefined. http://stackoverflow.com/questions/9822561/why-is-this-in-an-anonymous-function-undefined-when-using-strict – SunnyShah Jan 11 '13 at 14:41
  • 1
    @dgvid avoiding new operator can be done with prototypal inheritance easily. (Or do you call `new $(".selector)`?) – Esailija Jan 11 '13 at 14:43
  • Oh, I *personally* don't agree with Crockford on that point. Also, jQuery creator John Resig doesn't agree on that point and offers a simple constructor pattern for side-stepping the "forgotten new" in his _Pro JavaScript Techniques_. – dgvid Jan 11 '13 at 14:46
  • @SDC, I am not using jQuery, I believe that jQuery is good piece of open source code to understand the best practices. I use AngularJS. – SunnyShah Jan 12 '13 at 06:33
  • @SunnyShah: jQuery contains a lot of nice stuff and some best practises, but some is just quirks legacy code. – Bergi Jan 14 '13 at 04:16
  • @Bergi, Thanks. Understood. Other experts also have similar opinion. https://twitter.com/jwmcpeak/status/289981862141652992 – SunnyShah Jan 14 '13 at 04:32

2 Answers2

3

Using those specific functions here allows the embedding of specific variables (for example list) in the closure while keeping them private.

Using in a standard fashion a prototype based object would have made those properties public.

A thumb rule, when choosing, might be the necessity of the coherency of the variables. When a direct variable change may bring the object to an invalid state, it might seem pertinent to be more protective. Here, for example, reducing the list length might make an index invalid.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 2
    He is asking why doesn't jQuery use prototypal inheritance for Callbacks class. It already uses prototypal inheritance for the `Event` class and the `Tween` class (Not to mention the jQuery class itself), so this is hard to explain. If this is his question, then your answer is not relevant. – Esailija Jan 11 '13 at 14:27
  • 1
    @Esailija Thanks. As github wasn't answering I just had read the question. I'll delete my answer now that the link makes it a very different question. – Denys Séguret Jan 11 '13 at 14:32
  • Agree, I know that too. But most of the experts advise to make private/protected variable start with _ ( example _list ) and not use closure to hide it. Why jQuery is not following using it? I mean are there any specific logical reasons/advantages? – SunnyShah Jan 11 '13 at 14:40
  • jQuery never cared about this (enforced privacy), anything outside the documentation is always considered internal and a lot of internal stuff is publicly accessible already. Also the latest addition (Tween class) is using prototypal inheritance again. – Esailija Jan 11 '13 at 14:40
  • 1
    Hard to be definitive on this choice. I personally prefer to use the same naming convention but not enough to criticize somebody's else additional protection, especially when it doesn't make the code less readable. Any javascript code creates so many closures that one more doesn't change a lot... – Denys Séguret Jan 11 '13 at 14:42
  • 1
    This is just very subjective, implementing classes using prototypal inheritance vs closures is a holy war. – Esailija Jan 11 '13 at 14:58
  • @Esailija This **is** subjective but maybe the interesting point here is that the internal variables aren't orthogonal : they might be made incoherent by an external change. – Denys Séguret Jan 11 '13 at 15:00
  • @dystroy like I said before, plenty of internal stuff in jQuery is accessible and can be used to put jQuery to invalid state so that cannot be the reason jQuery implemented this class using closures. – Esailija Jan 11 '13 at 15:01
  • 1
    @Esailija, Agree with you, I should better ask a creator of that file. – SunnyShah Jan 11 '13 at 15:13
1

When using prototype you have access only to the privileged/public members of the object.

As I see from the code, jQuery does not set any options as public/privileged members to the Callback objects, they use a closure for accessing the options inside the Callback instances. If they initialize the methods in the prototype they wont have access to the options object.

Here an alternative example:

Using prototype

function Callback(opt) {
    var options = opt;
}

Callback.prototype.execute = function () {
   console.log(typeof options);   //undefined
}

Without using prototype

function Callback(opt) {
    var options = opt;
    this.execute = function () {
       console.log(typeof options);   //object
    }
}

I cant be sure about the considerations of jQuery but here are few assumptions:

  1. In my opinion they don't want to provide a public access to everything which is used inside by the callback object.

  2. The use of new. I haven't seen jQuery to require from the client to use new for initialization an object provided by the API.

The new can be avoided by:

function Callbacks() {
    if (this === window) {
        return new Callbacks();
    }
}

Callbacks.prototype.method = function () {
    //implementation
}

var c = Callbacks(); 

But there are few performance overheads:

  • Here is an overhead because of the recursive call return new Callbacks().
  • Another one is because of the use of new. In jsperf you can see that usually the object literal is faster then new.
  • And the last one is from the traversal of the prototype chain:

    If I use: c.method(); first the JS interpreter will look at the properties of c for invoking method. Of course it won't find it there so it has to look also in c's prototype.

Minko Gechev
  • 25,304
  • 9
  • 61
  • 68
  • Agree, I know that too. But most of the experts advise to make private/protected variable start with _ ( example _list ) and not use closure to hide it. – SunnyShah Jan 11 '13 at 14:38
  • jQuery never cared about this (enforced privacy), anything outside the documentation is always considered internal and a lot of internal stuff is publicly accessible already. Also the latest addition (Tween class) is using prototypal inheritance again. – Esailija Jan 11 '13 at 14:38
  • @SunnyShah yes, thats right. It makes the unit testing easier and you don't have this problem with the access from the prototype. Probably they just prefer to use the module pattern in this case. – Minko Gechev Jan 11 '13 at 14:44
  • @SunnyShah probably there're performance considerations also, I've edited the answer. – Minko Gechev Jan 11 '13 at 15:03
  • @MinkoGechev, Any data to support your point no 2? – SunnyShah Jan 11 '13 at 15:06
  • At last I've edited it and it's in readable form :-) – Minko Gechev Jan 11 '13 at 15:32
  • 1
    The 3. doesn't really happen in modern interpreters, if you use prototypal inheritance correctly without any magic it will work like classes work in compiled language. It is only when you start messing around with dynamic features that your objects get demoted to hash tables. – Esailija Jan 11 '13 at 15:36
  • 1
    @MinkoGechev, I respect you for your effort and for your knowledge. IMHO, all your three points are not valid in context of this question. – SunnyShah Jan 11 '13 at 15:39
  • OK, so we may exclude 2.3 but there are still at least 3 reasons. – Minko Gechev Jan 11 '13 at 15:39
  • @SunnyShah I'm not exactly sure that they are not. jQuery makes a lot of performance optimizations. In my opinion it's possible the design decision to be based on performance considerations. – Minko Gechev Jan 11 '13 at 18:32