1

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

为此,我有一个 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);
  });
4

1 回答 1

3

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

JavaScript 中的闭包和作用域有很多参考资料。例如,这是另一个 SO 问题: JavaScript 闭包内循环 – 简单实用示例

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

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在您的处理程序运行时更实际。

于 2012-08-16T17:46:41.373 回答