3

当我突然被一个问题击中时,我正在循环。这应该是“正确的方式”:

    // code.. 
    $('tr','#my_table').each(function(){
        $(this).click(function(){
            var show_id = $(this).attr('data-show-id');
            // code..
            // code..
        });
    });
    // code.. 

或者

    // code.. 
    var show_id = 0; /* HERE */ 
    $('tr','#my_table').each(function(){
        $(this).click(function(){
            show_id = $(this).attr('data-show-id');
            // code..
            // code..
        });
    });

在第一个示例中,我为每个tr新变量创建了一个show_id。在第二个中,我对表中show_id的所有tr's 使用相同的全局,并在每个 tr 上重新填充它。

所以问题将是:不管编程语言 - 在每个循环上创建一个新变量,当函数退出或“捕获”整个运行的内存并避免每次都重新创建一个变量时,这是更好的做法吗?

4

5 回答 5

10

该变量不是在循环期间创建的。它仅在事件处理程序中需要,它将自行执行。将变量(仅在内部需要时)移到外部存在三个问题:

  • 访问更高范围的变量有点慢
  • 垃圾收集不收集值(不节省内存)
  • 当事件处理程序中的异步行为访问变量但处理程序被多次调用时可能出现问题

第二点也是你问题的答案:

更好的做法是在每个循环上创建一个新变量,当函数退出或“捕获”整个运行的内存并避免每次重新创建一个变量时释放该变量?

一直浪费内存几乎没有意义,创建函数范围很快。将声明从变量的使用中移开可能被认为是不好的做法,将其移至不同的范围甚至会引入错误。

如果您在具有大内存的机器上运行,并且创建/删除对象(对于所有处理程序保持不变)很慢,那么预分配内存将是有意义的。

于 2013-07-30T12:38:28.097 回答
1

实际上,这完全取决于您需要该变量存在的位置。JS 会将变量提升到当前范围的顶部(声明的范围),或者如果未声明 var,则创建一个(邪恶的)全局变量。

是否在循环中声明 var 并不重要:

for (var i=0;i<10;i++)
{
    var j = 1*2;
}

被翻译成:

var i, j;
for (i=0;i<10;i++)
{
    j = 1*2;
}

不过,有几个“经验法则”

  • 如果您使用的是全局变量,那么您可能犯了一个错误
  • 如果您在循环中声明一个变量,那么您可能无论如何都不需要该变量
  • 如果您没有声明变量,请尝试使用您的脚本执行此操作:

(function()
{
    'use strict';
    //your code here
}());

并调试它,直到你隐含全局自由。

在您的示例中,您“提供”了show_id在全局范围内声明(好吧,$(document).ready(callback无论如何都是范围)或在click处理程序内声明的选择。也没有什么可以阻止您在each回调中声明变量,在这种情况下不会有太大区别,但这是另一回事。

在 JS 中,函数都有自己的作用域,你在你需要的作用域中声明一个变量。

话虽如此,只要注意到你的性能标签,你的代码在效率方面就和它一样糟糕。而不是循环遍历行,并绑定事件处理程序:委托事件:

$('#my_table').on('click','tr',function(e)
{
    var show_id = e.target.attr('data-show-id');
});

在此代码中,show_id将在单击处理程序返回后进行 GC,如果您希望show_id保留它,例如,检查下一行被单击的时间:

$('#my_table').on('click','tr',(function (show_id)
{
    return function(e)
    {
        console.log('show_id was: ' + show_id);
        show_id = e.target.attr('data-show-id');
        console.log('and is now: ' + show_id);
    };
}()));
console.log(show_id);//undefined

变量保持在作用域内(包裹在的函数return function是声明的作用域show_id,但它的返回值引用该变量,所以它不是 GC'ed。在返回的函数之外,我们处于更高的作用域,所以我们根本无法访问变量。我们可以做的是暴露它的值:

var someVar = {};
$('#my_table').on('click','tr',(function (show_id)
{
    return function(e)
    {
        show_id = e.target.attr('data-show-id');
        someVar.myTableClicks = show_id;
        console.log(someVar.myTableClicks);//value of show_id
    };
}()));
console.log(someVar.myTableClicks);//undefined
//after a couple of clicks
console.log(someVar.myTableClicks);//some value

现在,我们可以访问show_id任何我们可以访问的值someVar

就个人而言,我更喜欢保留需要一些变量可用的组代码:

var access = (function(timer)
{
    var speed = 100,
    begin = function(callback, interval)
    {
        speed = +(interval) || speed;
        timer = setInterval(callback, speed);
    },
    stop = function()
    {
        clearInterval(timer);
    };
    return {start: start, stop: stop};
}());

在此示例中,startstop函数都需要访问timervar,但我不希望外部的任何代码与timer' 值混淆,所以我只公开这些函数。AFAIK,回调参数函数可以包含timerspeed引用,但它们不会引用我试图屏蔽的speed和变量,因为根据定义,回调函数将在另一个范围内声明,因此它无法访问这个范围。即便如此,如果你想绝对确定,你总是可以这样做:timer

var access = (function(timer)
{
    var speed = 100,
    begin = function(callback, interval)
    {
        speed = +(interval) || speed;
        timer = (function(timer, speed)
        {//masks timer & speed from the higher scope
            return setInterval(callback, speed);
        }('abc', 'def'));//assign dummy values
        timer = setInterval(callback, speed);
    },
    stop = function()
    {
        clearInterval(timer);
    };
    return {start: start, stop: stop};
}());
于 2013-07-30T12:37:31.813 回答
0

取决于你的要求。

第一个例子:

$('tr','#my_table').each(function(){
        $(this).click(function(){
            var show_id = $(this).attr('data-show-id');
            // code..
        });
    });

变量是本地变量,可以在使用 .each 或 click 绑定的匿名函数中使用。

第二个例子:

var show_id = 0; /* HERE */ 
    $('tr','#my_table').each(function(){
        $(this).click(function(){
            show_id = $(this).attr('data-show-id');
            // code..
            // code..
        });
    });

这里变量是全局的,因此它的范围在整个脚本中都是有效的!!!

于 2013-07-30T12:40:35.040 回答
0

我个人会使用:

    $("tr","#my_table").each(function(){
        $(this).on("click", function(){
             var $this = $(this);                
             var show_id = $this.data("show-id");
            // code..
            // code..
        });
    });
  1. 使用 on("click", function() {...}); 而不是 .click()
  2. 建议使用 var $this = $(this) 以防您在代码中进一步使用它,否则您可以忽略它
  3. 在这种情况下,show_id 将无法在此范围之外访问。
  4. 要访问 data-* 属性,您应该使用 data("- ") 而不是 attr("- ")
于 2013-07-30T12:42:21.223 回答
0

所有变量的内存都将在方法的入口处分配,因此两个示例是等价的。第二个例子更好,因为变量的范围有限(不会意外地在循环外使用变量),它会更安全。

于 2013-07-30T12:44:09.387 回答