计算嵌套在数组中的可观察数组的总和

时间:2014-01-11 17:56:06

标签: javascript knockout.js

我用小提琴演示了我的问题:http://jsfiddle.net/sljux/zVg7R/3/

我有一个对象数组。为简单起见,假设每个对象都有一个名称和一个值数组,所有这些都是可观察的:

self.array = [
    {
        name: ko.observable("first"),
        values: ko.observableArray([1, 2, 3])
    },
    {
        name: ko.observable("second"),
        values: ko.observableArray([2, 3, 4])
    },
    {
        name: ko.observable("third"),
        values: ko.observableArray([3, 4, 5])
    }
];

从那个数组我需要过滤掉某些对象,并为每个对象添加一个额外的计算器,它计算所有值的总和。过滤部分对问题并不重要,因此不会这样做。问题是observable需要是每个对象的属性,并且需要引用该对象的values数组。

我最好的尝试是:

self.newArray = ko.observableArray([]);

for(var i = 0; i < self.array.length; i++) {
    var obj = {
        name: ko.observable("new " + self.array[i].name()),
        values: ko.observableArray(self.array[i].values())
    };

    obj.sum = ko.computed({
        read: function() {
            var res = 0;

            for (var j = 0; j < obj.values().length; j++)
                res += obj.values()[j];

            return res;
        },
        owner: obj
    });

    self.newArray.push(obj);
}

问题是对values可观察数组的引用以某种方式丢失了。也就是说,在第一次计算时,每个对象得到他的值的总和,但最后,每个计算得到的数据中最后对象的总和。

我尝试过,并且没有计算的owner部分,仍然会传输引用。错误显然在小提琴中可见,我设置了三个按钮来改变每个值数组。

我也尝试将其设置为一个类:

function Obj(name, values) {
    ...
}

self.newArray.push(new Obj("first", [1, 2, 3]);

但同样的事情发生了。

1 个答案:

答案 0 :(得分:2)

排序答案:使用ko.utils.arrayForEach代替手动for循环:

ko.utils.arrayForEach(self.array, function(item) {
    var obj = {
        name: ko.observable("new " + item.name()),
        values: ko.observableArray(item.values())
    };

    obj.sum = ko.computed({
        read: function() {
            var res = 0;

            for (var j = 0; j < obj.values().length; j++)
                res += obj.values()[j];

            return res;
        },
        owner: obj
    });

    self.newArray.push(obj);
});

演示JSFiddle

很长的答案:你已经咬过这样一个事实,即在JavaScript中变量是函数范围的,所以你的for循环创建三个本地obj变量但重用相同的一个变量,结合闭包的工作方式,最终会得到所有计算引用的最后一个值:

您可以通过将for正文包装在一个立即执行的函数中来解决此问题:

for(var i = 0; i < self.array.length; i++) {
    (function(){
    var obj = {
        name: ko.observable("new " + self.array[i].name()),
        values: ko.observableArray(self.array[i].values())
    };

    obj.sum = ko.computed({
        read: function() {
            var res = 0;

            for (var j = 0; j < obj.values().length; j++)
                res += obj.values()[j];

            return res;
        },
        owner: obj
    });

    self.newArray.push(obj);
    })();
}

演示JSFiddle