Jordan Reiter
Jordan Reiter

Reputation: 20992

Anyway to avoid creating a JavaScript function each time? (alternative to closure)

Suppose I have the following jQuery code:

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

$("a[href]").each(function () {
    var link = this;

    // do things with the link, e.g. 
    $(link).data('fiddle', "deedee")

    function do_something() {
        console.log("I would use the link variable here:", link)
        // lots of code here using link
        // blah, blah, blah link
    }

    $(this).click(function () {
        FooBar.zizzle(do_something);
    });
});

Currently, because do_something is inside of the function where link is defined it has access to that variable (closure). However, I'm wondering whether it's possible to avoid creating the function for every link. I'd rather do something closer to this:

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

function do_something() {
    console.log("I would use the link variable here:", link)
    // lots of code here using link
    // blah, blah, blah link
}

$("a[href]").each(function () {
    var link = this;

    // do things with the link, e.g. 
    $(link).data('fiddle', "deedee")

    $(this).click(function () {
        FooBar.zizzle(do_something);
    });
});

So that do_something is only created once. However, if I do this, then do_something no longer has a value for link.

Assume in this case that it's not possible to change the code for FooBar and that it expects just a callback and can't send along any additional parameters.

The only alternative I've thought of is something like this, which at least only creates functions as needed:

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

function do_something_maker(link) {
    return function (x, y, z) {
        console.log("I would use the link variable here:", link)
        // lots of code here using link
        // blah, blah, blah link
    }
}

$("a[href]").each(function () {
    var link = this;

    // do things with the link, e.g. 
    $(link).data('fiddle', "deedee")

    $(this).click(function () {
        FooBar.zizzle(do_something_maker(link));
    });
});

Is that the best option?

Upvotes: 3

Views: 242

Answers (5)

HMR
HMR

Reputation: 39250

You should register click on the container of the elements you're interested in. In your case that would be document.body. Then have an extra selector figure out when to take action: http://api.jquery.com/on/ Here is an example

function create_do_something($link){
  return function(z,y,x){
    do_something(z,y,x);
  };
}
$(document.body).on("click","a[href]",function () {
  var $link =  this;
  FooBar.zizzle(create_do_something($link));
});

Just found out something, if you have any solution using $("a[href]").click or $("a[href]").on jQuery will create a closure for every found element in the selector.

Running some tests in Chrome and doing a heap snapshot has the following result:

No jQuery and no JS code:

2557 closures

No code at all (just jQuery and jQuery ui):

5832 closures

With the following code

$(document.body).on("click","div"
  ,function(){console.log("hi");});

5834 closures(with 2 divs)

5834 closures(with 100 divs), notice that the amount of closures stays the same

With the following code

$("div").click(function(){console.log("hi");});

5835 closures(with 2 divs)

5933 closures(with 100 divs), notice there are 98 closures more so jQuery adds a closure for you on every found element in the selector.

So why does $("div").click have more closures?

jQuery 1.10.2 unminified on line 4762:

 eventHandle = elemData.handle = function( e ) {

When you set a break point there you'll see jQuery add a closure for each element found in the selector. So $("div") returned a hundred where $(document.body) has one no matter how many divs.

Upvotes: 0

alexander farkas
alexander farkas

Reputation: 14134

Here you go:

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

function do_something() {
    console.log("I would use the link variable here:", do_something.link);
    //if no link exists, abort
    if(!do_something.link){return;}
    //code with do_something.link


    //you might want to delete afterwards
    //delete do_something.link;
}

$("a[href]").click(function () {
    do_something.link = this;
    FooBar.zizzle(do_something);
});

In case you have something async and slow ongoing, you can try to make it lazy like do_something_maker or the bind/$.proxy approach, but only once for each link (on the first click and add it to $.data).

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

function do_something() {
    console.log("I would use the link variable here:", this);

}

$("a[href]").click(function () {
    var fn = $.data(this, 'zizzleCB') || $.data(this, 'zizzleCB', do_something.bind(this));
    FooBar.zizzle(fn);
});

Upvotes: 2

katranci
katranci

Reputation: 2571

You can use Function.prototype.bind:

var FooBar = {
    zizzle: function (callback) {
        var x, y, z;
        // … do stuff with x y and z
        callback(z, y, z);
    }
}

function do_something() {
    console.log("'this' refers to link here", this)
    // lots of code here using link
    // blah, blah, blah link
}

$("a[href]").each(function () {
    link = this;
    $(this).click(function () {
        FooBar.zizzle(do_something.bind(link));
    });
});

Upvotes: 0

Henrik Andersson
Henrik Andersson

Reputation: 47172

Just for the kicks:

var Util = function() {};
Util.prototype.do_something = function() {};

var FooBar = function() {
};
FooBar.prototype.zizzle = function(fn) {};

var fooBar = new FooBar();
var util = new Util();
fooBar.zizzle(util.do_something);

What has this given you?

Well for each FooBar object you create you will have 1 less instance of zizzle (since all functions are objects you effectively have 1 less). For each instance of Util, you will have 1 less instance of do_something AND you can now decide better when you want a Util instance and not clutter the global object with an unused function.

This works because we're defining one function on the prototype instead of letting the functions run rampant in a object literal {} or attach them to the global namespace

Upvotes: 0

john Smith
john Smith

Reputation: 17906

Why not just passing "link" as an argument to your zizzle function and pass it to the callback ?

sth like :

var FooBar = {
    zizzle: function (callback,link) {
        var x, y, z;
        // … do stuff with x y and z
        callback(x, y, z, link);
    }
}

function do_something(x, y, z, link) {
    console.log("I would use the link variable here:", link)
    // lots of code here using link
    // blah, blah, blah link
}

$("a[href]").on("click", function () {
    FooBar.zizzle(do_something, this);
});

Upvotes: 0

Related Questions