2
\$\begingroup\$

I sometimes find myself wanting custom events on a global object with JQuery.

For example:

$(window).trigger('blah', {x: 'y'});

This plugin is designed to provide exactly that, except faster:

$.multicast('blah')({x: 'y'});

Full plugin code (minus test pages, really gotta get some qunit going on):

(function ($) {
    "use strict";
    var namedDelegates = {},
        guid = 0,
        multicastDelegate = function () {
            var targets = {},
                proxy = function () {
                    var args = arguments,
                        useargs = args.length > 0,
                        results = [],
                        site;
                    for (site in targets) {
                        if (targets.hasOwnProperty(site)) {
                            if (useargs) {
                                results.push(targets[site].apply(this, args));
                            } else {
                                results.push(targets[site].call(this));
                            }
                        }
                    }
                    if (proxy.aggregate) {
                        return proxy.aggregate.call(this, results);
                    }
                    return results;
                };

            $.extend(proxy, {
                exists: function () {
                    var site;
                    for (site in targets) {
                        if (targets.hasOwnProperty(site)) {
                            return true;
                        }
                    }
                    return false;
                },
                add: function (func, key) {
                    if (typeof (func) === 'string') {
                        var temp = func;
                        func = key;
                        key = temp;
                    }
                    guid += 1;
                    if (!key) {
                        key = guid;
                    }
                    targets[key] = func;
                },
                remove: function (key) {
                    if (targets.hasOwnProperty(key)) {
                        delete targets[key];
                    }
                },
                callsites: function () {
                    return $.extend({}, targets);
                },
                aggregate: null
            });
            return proxy;
        };

    $.multicast = function (name) {
        if (!namedDelegates[name]) {
            namedDelegates[name] = multicastDelegate();
        }
        return namedDelegates[name];
    };

    $.extend($.multicast, {
        aggregates: {
            any_or_empty: function (args) {
                var r = args.length === 0,
                    i,
                    l = args.length;
                for (i = 0; i < l && !r; ++i) {
                    r = r || args[i];
                }
                return r;
            },
            all_or_empty: function (args) {
                var r = args.length === 0,
                    i,
                    l = args.length;
                for (i = 0; i < l && r; ++i) {
                    r = r && args[i];
                }
                return r;
            },
            atLeastN: function (n) {
                return function (args) {
                    var r = 0,
                        i,
                        l = args.length;
                    if (l < n) { return false; }
                    for (i = 0; i < l && r < n; ++i) {
                        r += !!args[i] ? 1 : 0;
                    }
                    return r === n;
                };
            },
            all: function (args) {
                var r = args.length !== 0,
                    i,
                    l = args.length;
                for (i = 0; i < l && r; ++i) {
                    r = r && args[i];
                }
                return r;
            }
        }
    });
}(jQuery));

jsPerf

Eventually I'll get around to uploading this to github (I am waiting on the new plugin site). Feel free to use this.

License header is and has been since I started using this:

/**
* jQuery.multicast
* Copyright (c) 2008 Bill Barry - after.fallout(at)gmail(dot)com | http://16randombytes.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 13 Dec 2009
*/

I'd prefer if you kept it if you use this code, perhaps with a link here (when I do get this on JQuery plugins, I'll link there from here and update the header).

How can I make this code better?

\$\endgroup\$
  • \$\begingroup\$ How come you don't use $.fn if it's a jQuery plugin? \$\endgroup\$ – Jonny Sooter Feb 28 '13 at 15:25
  • \$\begingroup\$ It doesn't extend the jQuery object. It extends jQuery itself. \$\endgroup\$ – Bill Barry Mar 1 '13 at 20:12
  • \$\begingroup\$ Wouyld be interested to see how this works wqiuth the aggregates any demos? \$\endgroup\$ – JasonDavis Jan 24 '16 at 11:43
2
\$\begingroup\$

Namespacing

There's no reason why this needs to be a jQuery plugin other than that you're using the jQuery $ as the namespace, and that you're using $.extend. You could easily make this an independent library, and use a UMD-style header to load it into whatever namespace is available.

Duplication

There's a lot of duplicate control flow code, especially in the aggregate functions. This is javascript: Use higher order functions! Look at using ES5's Array.some and Array.every, and don't be afraid to make your own. Just make sure that you use common idioms.

Documentation and testing

This should go hand in hand with uploading it to github. Nobody's going to use your code unless you document it, and nobody at a serious business company is going to trust it unless you have tests. There's plenty of javascript event/pubsub libraries to go around.

Minor details

Something like this

var r = args.length === 0,
    i,
    l = args.length;

Should be more like this

var i,
    l = args.length,
    r = l === 0;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.