敲除订阅功能关闭未捕获

时间:2012-08-16 16:39:23

标签: knockout.js closures

我有一个带有一系列项目的淘汰视图模型。我想在所有项目上观看一个属性(已选中),并在更改时采取措施。

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

示例:如果单击列表中的项目,星号将指示是否已选中该项目。这样可行。订阅的函数也被调用,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 个答案:

答案 0 :(得分:3)

您遇到了由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