无论如何要避免每次都创建一个JavaScript函数? (关闭的替代方案)

时间:2013-11-12 20:57:30

标签: javascript closures

假设我有以下jQuery代码:

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);
    });
});

目前,由于do_something位于定义link的函数内部,因此它可以访问该变量(闭包)。但是,我想知道是否可以避免为每个链接创建函数。我宁愿做更接近这件事的事情:

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);
    });
});

这样do_something只创建一次。但是,如果我执行此操作,则do_something不再具有link的值。

在这种情况下假设无法更改FooBar的代码,并且它只需要回调并且无法发送任何其他参数。

我想到的唯一选择就是这样,至少只根据需要创建函数:

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));
    });
});

这是最好的选择吗?

5 个答案:

答案 0 :(得分:2)

你走了:

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);
});

如果你有异步和缓慢进行的事情,你可以尝试让它像do_something_maker或bind / $。代理方法一样懒,但每个链接只有一次(在第一次点击时将其添加到$ .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);
});

答案 1 :(得分:0)

为什么不直接将“link”作为参数传递给zizzle函数并将其传递给回调?

喜欢:

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);
});

答案 2 :(得分:0)

只是为了踢:

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);

这给了你什么?

对于您创建的每个FooBar对象,您将减少1个zizzle的实例(因为所有函数都是实际上少了1个的对象)。 对于Util的每个实例,您将减少1个do_something的实例 并且您现在可以更好地决定何时需要Util实例,而不是使用未使用的函数使全局对象混乱。

这是有效的,因为我们在原型上定义了一个函数,而不是让函数在对象文字{}中猖獗或将它们附加到全局命名空间

答案 3 :(得分:0)

您可以使用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));
    });
});

答案 4 :(得分:0)

您应该注册点击您感兴趣的元素的容器。在您的情况下,这将是document.body。然后有一个额外的选择器找出何时采取行动:http://api.jquery.com/on/这是一个例子

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));
});

刚发现一些东西,如果你有任何使用$("a[href]").click$("a[href]").on的解决方案,jQuery将为选择器中的每个找到的元素创建一个闭包。

在Chrome中运行一些测试并执行堆快照会产生以下结果:

没有jQuery,也没有JS代码:

2557次关闭

根本没有代码(只是jQuery和jQuery ui):

5832关闭

使用以下代码

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

5834次关闭(有2个div)

5834个闭包(100个div),注意闭包的数量保持不变

使用以下代码

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

5835次关闭(有2个div)

5933个闭包(100个div),注意有98个闭包更多,所以jQuery为你在选择器中的每个找到的元素添加一个闭包。

那么为什么$("div").click有更多的闭包呢?

jQuery 1.10.2在第4762行未经证实:

 eventHandle = elemData.handle = function( e ) {

当你设置一个断点时,你会看到jQuery为选择器中找到的每个元素添加一个闭包。所以$(“div”)返回了100,$(document.body)有一个,无论有多少div。