如何制作由映射插件创建的挖空对象的深层副本

emi*_*ini 22 javascript knockout-mapping-plugin knockout.js

这是我的情景.我正在使用knockout映射插件为我创建一个可观察的viewmodel层次结构.我的层次结构中嵌套了元素.在层次结构中的特定点,我想放置一个Add按钮,在observablearray中插入该元素的新空白副本.问题是我不能只说whatArray.push(new MyObject()).

由于映射插件实际上为我创建了整个层次结构,因此我无法访问"MyObject".所以我似乎唯一可以做的就是插入一个新项目来查看前一个项目并复制它.我尝试了ko.utils.extend函数,但这似乎并没有成为一个真正的克隆.它给了我一个对象,但是当我更新该对象时,它仍然会影响它从中复制的原始对象.

参见jsfiddle 示例

Jef*_*ado 38

可能有一种方法可以在映射设置中设置它,但我还不能完全理解它.

同时,您可以取消映射对象并将其映射回来,这样您实际上就可以制作副本了.

var newJob = ko.mapping.fromJS(ko.mapping.toJS(job));
Run Code Online (Sandbox Code Playgroud)

这将是最简单的方法,就像任何其他库一样,"反序列化"和"序列化"再次回来.


我正在努力寻找一种使用映射选项的好方法,并找到了一种方法.

默认情况下,映射插件将从源对象获取可观察的实例,并在目标对象中使用相同的实例.所以实际上,两个实例都将共享相同的observable(bug?).我们需要做的是为每个属性创建一个新的observable并复制值.

幸运的是,有一个方便的实用程序函数来映射对象的每个属性.然后,我们可以创建使用值的副本初始化的新的可观察实例.

// Deep copy
var options = {
    create: function (options) {
        // map each of the properties
        return ko.mapping.visitModel(options.data, function (value) {
            // create new instances of observables initialized to the same value
            if (ko.isObservable(value)) { // may want to handle more cases
                return ko.observable(value);
            }
            return value;
        });
    }
};
var newJob = ko.mapping.fromJS(job, options);
Run Code Online (Sandbox Code Playgroud)

请注意,这将是一个浅表副本,如果您需要深层副本,则可能必须以递归方式映射对象.这将解决您的示例中的问题.


小智 7

ko.utils.clone = function (obj) {
    var target = new obj.constructor();
    for (var prop in obj) {
        var propVal = obj[prop];
        if (ko.isObservable(propVal)) {
            var val = propVal();
            if ($.type(val) == 'object') {
                target[prop] = ko.utils.clone(val);
                continue;
            }
            target[prop](val);
        }
    }
    return target;
};
Run Code Online (Sandbox Code Playgroud)

这是我的解决方案,希望它有所帮助.在这段代码中,obj将是你的viewModel对象.