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

Posted

技术标签:

【中文标题】如何制作由映射插件创建的剔除对象的深层副本【英文标题】:How do I make a deep copy of a knockout object that was created by the mapping plugin 【发布时间】:2012-09-02 06:29:34 【问题描述】:

这是我的场景。我正在使用淘汰映射插件为我创建一个可观察的视图模型层次结构。我的层次结构中有嵌套元素。在层次结构中的特定点,我想放置一个添加按钮以在 observablearray 中插入该元素的新空白副本。问题是我不能只说whateverArray.push(new MyObject())。

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

见 jsfiddle example

【问题讨论】:

【参考方案1】:

可能有一种方法可以在映射设置中进行设置,但我现在还不太清楚。

与此同时,您可以取消映射对象并将其映射回来,这样您实际上就是在制作副本。

var newJob = ko.mapping.fromJS(ko.mapping.toJS(job));

这将是最简单的方法,就像任何其他库一样,再次“反序列化”和“序列化”。


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

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

幸运的是,有一个方便的实用函数来映射对象的每个属性。然后,我们可以创建新的 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);

请注意,这将是一个浅拷贝,如果您想要一个深拷贝,您可能必须递归地映射对象。但是,这将解决您示例中的问题。

【讨论】:

然而,我仍然想知道是否有更好的淘汰方法来简单地克隆一个包含可观察对象的对象。如果我不使用映射插件,这将不是一个真正的问题,因为那时我将自己创建每个子对象,我可以直接新建一个而不是尝试复制... 啊,我明白了。我正在放弃并写了一篇关于我的发现的长评论......然后我得到了一个启示并弄清楚了。【参考方案2】:
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;
;

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

【讨论】:

以上是关于如何制作由映射插件创建的剔除对象的深层副本的主要内容,如果未能解决你的问题,请参考以下文章

如何制作一个快速的类对象数组的深层副本

如何制作Java ArrayList的深层副本[重复]

使用敲除映射插件将深度层次化的对象映射到自定义类

创建任何对象的只读/不可变副本(包括深层属性)

如何在 Ruby 中创建对象的深层副本?

如何在 Java 中制作 ArrayList<Integer> 的深层副本? [复制]