CoffeeScript 在 'for v in values' 中创建一个全局变量 'v',导致事件中的错误引用

CoffeeScript makes a global variable of `v` in `for v in values` causing wrong references at events

本文关键字:全局变量 误引用 一个 事件 错误 in for values 创建 CoffeeScript      更新时间:2023-09-26

我有以下CoffeeScript代码:

for date of dates
    Dom.section !->
        Dom.div !->
            Dom.text if date.attending then 'Y' else 'N'
            Dom.onTap !->
                Dom.text date.id

它被转换为以下JavaScript(根据 coffeescript.org(

var date;
for (date in dates) {
  Dom.section(!function() {
    return Dom.div(!function() {
      Dom.text(date.attending ? 'Y' : 'N');
      return Dom.onTap(!function() {
        return Dom.text(date.id);
      });
    });
  });
}

不要打扰代码的确切作用,因为我正在使用发生的API(一个新的对话应用程序,用户可以在CoffeeScript中制作插件(,因此,无论如何你都不会理解它。

不过我会解释一下:

  • dates 是一个长度为 10 的数组,其中包含对象。这些对象包含iddayyearattending等键。
  • 我遍历所有日期,并在屏幕上将它们呈现在所谓的"部分"中。
  • 每个部分都包含多个div(为了简单起见,在上面的示例中只有一个(。
  • 上面的div 包含一些文本。"Y"表示用户正在参加活动,"N"表示他不在。
  • 当用户点击此div 上的 ('onTap'-event( 时,它将更新本地和共享存储(在上面的示例中,它只会添加一些文本进行调试(。

一切正常,它在正确的部分中显示正确的日期和与会者人数。因此,date对象包含正确的数据。

问题所在

我遇到的问题是当我点击带有 onTap-event 的div 时。当我点击它时,它将始终使用渲染的最后日期。因此,假设我的日期01-12-1410-12-14。我点击05-12-14,它会在屏幕上添加 10-12-14 的 id 作为文本。经过长时间的打破和诅咒,我找到了导致这种情况的原因,但我不知道如何解决它。

请参阅 JavaScript 的第一行(seconde 代码示例(。如您所见,它正在声明一个全局变量date。一切都呈现良好,但是当我单击某些内容时,它将引用date,但是由于date已在循环的最后一次迭代中更新,因此它将始终使用该id。

所以。如何确保date每次迭代循环保持私密,并确保它在事件中使用正确的值?

注意

for k, v in dates也不起作用,因为它也使k全球化。因此,简单地使用dates[k].id是行不通的。

它不是声明全局的,只是该代码的全局内容。如果它在函数中,则不会是全局函数。

这个问题与全局变量无关。这是因为闭包的工作方式。闭包具有对它们关闭的变量的持久引用,而不是创建闭包时其值的副本。因此,您为 onTap 处理程序创建的所有函数都使用相同的单个date对象。

为了防止这种情况,给他们每个人一些自己的东西来关闭,不会改变。如果dates是数组,请使用 forEach

(对于那些熟悉CoffeeScript但不熟悉Happen的人注意:->前面的!不是通常的!运算符。发生扩展了CoffeeScript,使得一个函数是用!->而不是->来声明的,Coffeescript通常具有的隐式返回值不会生成。[感谢Erik Dolor指出这一点。

所以:

dates.forEach (date) !->
    Dom.section !->
        Dom.div !->
            Dom.text if date.attending then 'Y' else 'N'
            Dom.onTap !->
                Dom.text date.id

。它变成了这个JavaScript:

dates.forEach(function(date) {
  Dom.section(function() {
    Dom.div(function() {
      Dom.text(date.attending ? 'Y' : 'N');
      Dom.onTap(function() {
        Dom.text(date.id);
      });
    });
  });
});

现在,为forEach回调的每个迭代创建的闭包将关闭该回调迭代的参数date,该参数不会更改。