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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12333915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:44:53  来源:igfitidea点击:

How do I make a deep copy of a knockout object that was created by the mapping plugin

javascriptknockout.jsknockout-mapping-plugin

提问by emirhosseini

Here's my scenario. I'm using the knockout mapping plugin to create an observable viewmodel hierarchy for me. My hierarchy has nested elements in it. At a particular point in the hierarchy I want to put an Add button to insert a new blank copy of that element in the observablearray. The problem is I can't just say whateverArray.push(new MyObject()).

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

Since the mapping plugin actually created the whole hierarchy for me, I don't have access to "MyObject". So it seems the only thing I can do to insert a new item is to look at a previous item and copy it. I tried the ko.utils.extend function, but that doesn't appear to be making an actual clone. It gives me an object back, but when I update that object it still affects the original object that it was copied from.

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

See jsfiddle example

参见 jsfiddle示例

回答by Jeff Mercado

There might be a way to set this up in the mapping settings but I can't quite figure that out just yet.

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

In the mean time, you could just unmap the object and map it back so you are essentially making a copy.

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

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

This will be the easiest way to do it just like any other library, "deserialize" and "serialize" back again.

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



I was looking hard for a nice way to do this using the mapping options and found a way.

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

By default, the mapping plugin will take the observable instances from the source object and use the same instance in the target object. So in effect, both instances will share the same observables (bug?). What we needed to do was create a new observable for each property and copy the values over.

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

Fortunately, there is a handy utility function to map out each of the properties of an object. We can then create our new observable instances initialized with copies of the values.

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

Note that this will be a shallow copy, you'll probably have to recursively map the objects if you want a deep copy. This will fix the problem in your example however.

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

回答by Teddy

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;
};

Here is my solution, hope it helps. In this code, objwould be your viewModel object.

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