gpt4 book ai didi

javascript - 循环中的jQuery变量捕获

转载 作者:行者123 更新时间:2023-12-01 01:58:27 25 4
gpt4 key购买 nike

我对JS和jQuery的理解非常有限,而且我来自C#。但是我确实知道什么是变量捕获,而且我知道,如果我在声明为循环外的循环中捕获一个变量,则每次委托(delegate)运行时,我都会获取所捕获变量的最后一个值,而不是捕获时间。
但是,这显然不是这里的问题,但是我仍然收到最后一个值:

for (var i = 0; i < dialogs.length; i++) {

var dialog_button = dialogs[i];

var ix_parts = $(dialog_button).attr("id").split("_");
var index_tag = ix_parts[1];
var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
$(dialog_button).click(function (event) {
$(dialog_panel).dialog('open');
return false;
});

}

由于dialog_button是在循环范围内声明的,因此我希望我会在单击处理程序中收到正确的值。
JS做的事情有所不同吗?

最佳答案

JavaScript没有块范围,只有函数范围(以及全局范围)。闭包收到对该变量的实时引用,因此您的事件处理函数(这是一个闭包)将始终看到分配给dialog_button的最后一个值。

在您描述的特定情况下,最好的选择是使用jQuery的 $.each 函数而不是for循环(谢谢@Esailija,请注意Tadeck suggested $.each before I did,值得赞扬,我在@Esailija的建议中添加了它,因为到目前为止,它是更好的解决方案这种特定情况):

$.each(dialogs, function(index, dialog_button) {

var ix_parts = $(dialog_button).attr("id").split("_");
var index_tag = ix_parts[1];
var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
$(dialog_button).click(function (event) {
$(dialog_panel).dialog('open');
return false;
});
});

...因为现在,传递给 $.each的函数的每个调用都具有其自己唯一的 dialog_button参数,因此每个生成的函数(闭包)都将关闭其自己的副本,并且不会遇到将新值分配给该函数的问题变量。

我建议使用jQuery函数,因为您已经在使用jQuery,因此可以肯定地使用它。从ECMAScript5开始,有一个本机的 Array#forEach 函数可完成几乎相同的功能,但并非所有引擎都具备此功能。

在上述情况不符合要求的情况下,这是另一种方法。它还包括有关发生的事情,原因以及如何对其进行控制的相当深入的讨论:

最好的选择是使用创建事件处理程序的函数,就像这样(我假设所有这些都在函数内):
for (var i = 0; i < dialogs.length; i++) {

var dialog_button = dialogs[i];

var ix_parts = $(dialog_button).attr("id").split("_");
var index_tag = ix_parts[1];
var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
$(dialog_button).click(createHandler(dialog_button));
}

function createHandler(dlg) {
return function (event) {
$(dlg).dialog('open');
return false;
};
}

在那里,循环调用 createHandler,这创建了处理程序函数,作为对 createHandler调用上下文的闭包,因此处理程序引用了 dlg。每次对 createHandler的调用都将获得其自己的唯一上下文,从而获得其自己的唯一 dlg参数。因此,闭包指的是期望值。

您可以根据需要将 createHandler放置在整个函数中(确保它不在任何分支内,必须在函数的顶层),如下所示:
function createDialogs() {
for (var i = 0; i < dialogs.length; i++) {

var dialog_button = dialogs[i];

var ix_parts = $(dialog_button).attr("id").split("_");
var index_tag = ix_parts[1];
var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
$(dialog_button).click(createHandler(dialog_button));
}

function createHandler(dlg) {
return function (event) {
$(dlg).dialog('open');
return false;
};
}
}

...或者如果您需要在其他地方执行相同的操作,则可以将其移出一个级别:
function createDialogs() {
for (var i = 0; i < dialogs.length; i++) {

var dialog_button = dialogs[i];

var ix_parts = $(dialog_button).attr("id").split("_");
var index_tag = ix_parts[1];
var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
$(dialog_button).click(createHandler(dialog_button));
}
}

function createHandler(dlg) {
return function (event) {
$(dlg).dialog('open');
return false;
};
}

后者的优势在于,这意味着每次调用 createDialogs都会创建一小块内存(称为变量绑定(bind)对象),任何引用都不会引用它,并且可以在调用返回时将其清除,而对于前者( createHandler在其中 createDialogs),该内存由变量绑定(bind)对象引用以调用 createHandler,因此不符合清理条件。每个都有其用途,这仅取决于您是否需要访问 createDialog调用上下文中的任何内容(您无需显示的代码,但我意识到这只是摘录)。

更多阅读:
  • Poor misunderstood var
  • Closures are not complicated


  • 在您要求的评论中:

    "(make sure it's not inside any branches, it must be at the top level of the function)" can you elaborate on this? I've declared the function inside the for loop, and it seems to be working! Am I doing something wrong?



    JavaScript具有两种不同的 function构造:函数声明和函数表达式。从句法上讲,它们非常相似,但是它们在不同地方是合法的,并且它们发生在不同的时间。

    TL; DR:我的 createHandler是函数声明的示例。它们不能在控制结构内。您要传递给 functionclick构造是一个函数表达式,可以是。区别在于 function构造是否为右手值(不是,而是您的)。正确理解声明和表达式是熟练的JavaScript编程的关键。

    长版:

    这是一个函数声明:
    function foo() {
    }

    这是一个分配给变量的函数表达式:
    var foo = function() {
    };

    这是另一个函数表达式,这次被用作对象常量中的属性初始化器:
    var obj = {
    foo: function() {
    }
    };

    另一个函数表达式,这次被作为参数传递给函数:
    bar(function() {
    });

    如您所见,它们之间的区别在于函数声明是独立的,而函数表达式在包含表达式中用作右侧值-用作赋值( =)或初始化程序( :)的右侧,或作为参数传递给函数。

    在创建包含它们的作用域的函数声明之前,将在执行任何分步代码之前对其进行处理。因此给出:
    function bar() {

    function foo() {
    }

    return foo;
    }

    ...在调用 bar时,在运行任何逐步代码之前,都会创建 foo函数。只有到那时,逐步代码才会运行,在这种情况下,它将返回对 foo函数的引用。因此,上面的 与此完全等效:
    function bar() {

    return foo;

    function foo() {
    }
    }

    即使在 foo语句中看起来 return不应该存在,但它确实存在。

    由于函数声明发生在分步代码之前,因此它们不能位于控制流语句中:
    function bar(condition) {
    if (condition) {
    function foo() { // <== INVALID
    return alert("A");
    }
    }
    else {
    function foo() { // <== INVALID
    return alert("B");
    }
    }

    return foo;
    }
    var f = bar(true);
    f(); // alerts what?

    您会认为警报会显示“A”,对吗?因为我们为 true传递了 condition,所以发生了第一个分支。但这根本不会发生。从技术上讲,以上是语法错误,纯净而简单。但是大多数浏览器并不将其视为一体(Firefox引擎[SpiderMonkey]是我所知道的唯一一种)。那他们怎么办?这取决于。大多数引擎继续将那些视为函数声明,并且当您在同一作用域中为同一函数拥有两个函数声明时,规范说第二个为准。因此,这些引擎将警告“B”。但是其他引擎(IE就是其中之一)会即时重写您的代码,将这些声明转换为表达式,因此这些引擎将警告“A”。再见龙在这里。不要这样:-)

    另一方面,函数表达式将函数创建为逐步代码的一部分。当执行在流程中到达它们时,它们就会发生。因此,这与前面的示例非常不同:
    function bar() {
    var foo;

    return foo;

    foo = function() {
    };
    }

    在这里, bar返回 undefined,因为从 return语句开始, fooundefined,当然以下赋值语句永远不会发生。同样,这是有效的,并且其行为是明确定义的:
    function bar(condition) {
    var foo;

    if (condition) {
    foo = function() {
    return alert("A");
    };
    }
    else {
    foo = function() {
    return alert("B");
    };
    }

    return foo;
    }
    var f = bar(true);
    f(); // alerts "A"
    f = bar(false);
    f(); // alerts "B"

    因为现在我们使用的是函数表达式,所以它们在逐步的代码中出现,并且其行为是应有的。

    将所有内容带回您的特定示例:通常,在循环中创建函数是一个坏主意,但是有时有必要。在那种情况下,通常您需要一个像我的 createHandler函数这样的助手,它在循环之外,因此您可以更好地控制上下文。您可以这样做:
    for (var i = 0; i < dialogs.length; i++) {

    var dialog_button = dialogs[i];

    var ix_parts = $(dialog_button).attr("id").split("_");
    var index_tag = ix_parts[1];
    var dialog_panel = $(dialog_panel_selector.replace("$ix$", index_tag));
    $(dialog_button).click((function(dlg) {
    return function (event) {
    $(dlg).dialog('open');
    return false;
    };
    })(dialog_button));
    }

    ...但这是一个非常糟糕的主意。首先,它很难阅读。其次,创建额外的函数:每次循环迭代实际上都会创建两个函数对象,一个我们用来创建另一个函数对象(例如,一个是 createHandler),另一个是创建它。因此,如果有三个对话框,则创建六个函数,而不是三个,所有这些函数会一直存在直到删除处理程序。

    最后一点:上面显示的所有函数表达式都创建了匿名函数(无名称的函数)。我 don't like anonymous functions;为函数命名可以帮助您的工具。从技术上讲,给他们取名字是合法的:
    var f = function foo() { // <== WARNING, see below
    };

    ...但是由于 bugs in IE prior to IE9,您目前无法在野外执行此操作。 IE8及以下版本将对该构造进行两次遍历,一次作为函数声明,然后再次作为函数表达式。它确实会创建两个函数对象,这可能会引起各种麻烦。 :-)

    关于javascript - 循环中的jQuery变量捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8200814/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com