未捕获淘汰订阅功能关闭

Posted

技术标签:

【中文标题】未捕获淘汰订阅功能关闭【英文标题】:knockout subscribe function closure not captured 【发布时间】:2012-08-13 01:48:01 【问题描述】:

我有一个带有一系列项目的淘汰视图模型。我想查看所有项目上的一个属性(已选择)并在它发生更改时采取行动。

为此,我有一个 SectionManager 可以做到这一点。我初始化管理器并为每个项目设置订阅。 myid 闭包未被捕获,它始终是最后一个值 3。谁能告诉我在哪里丢的?

示例:如果您单击列表中的某个项目,星号将指示它是否被选中。这样可行。 subscribed 函数也被调用,myid 被写入控制台,但始终是3

约翰

html

  <!DOCTYPE html>
  <html>
  <head>
     <title></title>
     <script type="text/javascript" src="Scripts/jquery-1.7.2.js"></script>
     <script type="text/javascript" src="Scripts/knockout-2.1.0.debug.js"></script>
     <script type="text/javascript" src="test.js"></script>
  </head>
  <body>
     <ul data-bind="foreach: roles">
        <li data-bind="click: toggle">
           <span data-bind="text: id"></span>
           <span data-bind="visible: selected">*</span>
        </li>
     </ul>
  </body>
  </html>

还有这个脚本:

  var roles = [
      id: 1 ,
      id: 2, selected: true ,
      id: 3 
  ];

  var viewModel = (function (roles) 
     var obj = ;
     var arr = [];
     for (var i = 0; i < roles.length; i++) 
        arr.push(
           id: roles[i].id,
           selected: ko.observable(roles[i].selected || false),
           toggle: function () 
              this.selected(!this.selected());
           
        );
     
     obj.roles = ko.observable(arr);

     return obj;
  )(roles);

  var sectionManager=(function()
     return 
        init: function (roles) 
           for (var i = 0; i < roles.length; i++) 
              var item = roles[i];
              var myid = item.id;

              item.selected.subscribe(function () 
                 console.log(myid);  // ALWAYS 3!!
              );
           
        
     ;
  )();

  $(function () 
     sectionManager.init(viewModel.roles());
     ko.applyBindings(viewModel);
  );

【问题讨论】:

【参考方案1】:

您遇到了一个由 JavaScript 中的闭包引起的经典问题。在您的循环中,实际上只有一个名为myid 的变量。您在订阅每个项目时创建的函数都可以访问相同的myid 变量。在循环结束时,它的值为 3,因此当处理程序运行时,它们都会报告该变量的值。

有很多关于 JavaScript 中的闭包和作用域的参考资料。例如,这是另一个 SO 问题:JavaScript closure inside loops – simple practical example

您处理此问题的一种方法是不使用中间变量并确保您的处理程序与当前项目一起运行。

item.selected.subscribe(function () 
    console.log(this.id);
, item);

在本例中,第二个参数定义函数运行时this 的值。因此,它将以正确的项目作为上下文运行,您可以从 this 访问其属性。

http://jsfiddle.net/rniemeyer/WV6g5/

您甚至可以在第二个参数中使用item.id

正如您在下面指出的,您当然可以创建一个接收变量的函数并让它返回一个函数以围绕该特定值创建一个闭包。

var subscriber = function(id) 
 
    return function()  
        console.log(id); 
    ; 
; 

...

item.selected.subscribe(subscriber(myid)); 

在 Knockout 的上下文中,我认为处理你的数据项并确保它在你的处理程序运行时是this 更实际。

【讨论】:

对于这种特殊情况,这确实是一个正确的答案。感谢您的链接,一个更通用的解决方案(不依赖于“this”而是修复闭包)将是使用 var subscriber = function(id) return function() console.log(id); ; ; item.selected.subscribe(订阅者(myid));如果您同意,也许您可​​以将其添加到您的答案中。 我用您的替代解决方案更新了答案。在 Knockout 的上下文中,我仍然建议将其绑定到您的数据项。它更简单,让您可以灵活地处理项目的多个属性。了解和控制this 的值是Knockout 中的一个重要概念。谢谢。 我理解,如果它只是订阅中需要的 observable,那就是它。但我还需要其他信息(可观察的之外),因此需要一个新的范围。本例中的示例过于简单,对此深表歉意。

以上是关于未捕获淘汰订阅功能关闭的主要内容,如果未能解决你的问题,请参考以下文章

订阅不适用于淘汰模型作为功能

推送订阅错误未触发捕获

放大未捕获的 AppSync 订阅(承诺中)

未捕获的错误:react-apollo 仅支持每个 HOC 的查询、订阅或突变

未认证个人订阅使用自定义菜单

使用 Javascript 智能按钮灯箱的 PayPal 访客订阅未关闭