问题 克隆可观察量的最佳方法?


在Knockout中克隆Observable对象以制作某种事务机制的最佳方法是什么?

例如,编辑此模型:

var Action = function (name, ownerType, condition, expression, args) {
    var self = this;
    this.name = ko.observable(name);
    this.ownerType = ko.observable(ownerType);
    this.condition = ko.observable(condition);
    this.expression = ko.observable(expression);
    this.args = ko.observable(args);
};

我想在用户编辑之前保存该对象的状态。如果用户将取消编辑 - 回滚对象状态。

最简单的方法就是创建另一个项目,如:

self.tempAction = new Action(action.name(), action.ownerType(), action.condition(), action.expression(), action.args());

但我不确定这是优雅的解决方案..

那么,有什么想法吗?


10129
2018-05-10 13:57


起源



答案:


我通常会做以下事情:

首先,我有一个模仿jQuery的函数 $.extend 功能。它填充了一个 target 与所有的对象 observable (或不可观察的)财产价值 source 目的。

// extends observable objects intelligently
ko.utils.extendObservable = function ( target, source ) {
    var prop, srcVal, isObservable = false;

    for ( prop in source ) {

        if ( !source.hasOwnProperty( prop ) ) {
            continue;
        }

        if ( ko.isWriteableObservable( source[prop] ) ) {
            isObservable = true;
            srcVal = source[prop]();
        } else if ( typeof ( source[prop] ) !== 'function' ) {
            srcVal = source[prop];
        }

        if ( ko.isWriteableObservable( target[prop] ) ) {
            target[prop]( srcVal );
        } else if ( target[prop] === null || target[prop] === undefined ) {

            target[prop] = isObservable ? ko.observable( srcVal ) : srcVal;

        } else if ( typeof ( target[prop] ) !== 'function' ) {
            target[prop] = srcVal;
        }

        isObservable = false;
    }
    return target;
};

然后我有一个 copy 本质上将要复制的对象转换为的函数 JSON 然后拿走 JSON 复制并构建一个新的javascript对象。这样可以确保不会复制所有内存指针,并且您有一个与原始对象匹配的全新对象。这里的一个关键是你必须传入一个新对象的空实例(否则我们不知道要填充什么属性)

// then finally the clone function
ko.utils.clone = function(obj, emptyObj){
    var json = ko.toJSON(obj);
    var js = JSON.parse(json);

    return ko.utils.extendObservable(emptyObj, js);
};

然后您可以这样使用它:

var tempAction = ko.utils.clone(action, new Action());

15
2018-05-10 15:09



你能不能只使用ko.mapping插件从原始源对象映射到新的目标对象? - jaffa
@jaffa - 是的,映射插件是一个合适的选择。然而,对于琐碎的需求来说,这可能是过度的。另外,我找到了 extendObservable 在应用程序范围内不需要映射插件的情况下,函数非常有用。 - ericb
这如何与ko.computed值一起使用?我认为使用toJSON和JSON.parse复制对象将丢失其所有计算值。 - Greg Ennis
@ericb +1工作,但它看起来有点矫枉过正,我有类似于你的extendObservable的方法,由于某种原因,我不能通过传递空实例和复制原始对象而不先将原始实例序列化为JSON来实现它,确实有一个我的意思是,为什么这是因为你解开可观察的东西以获得纯粹的价值? - formatc
深度克隆中可能存在一些问题,我的意思是嵌套的viewModels。 - Teddy


答案:


我通常会做以下事情:

首先,我有一个模仿jQuery的函数 $.extend 功能。它填充了一个 target 与所有的对象 observable (或不可观察的)财产价值 source 目的。

// extends observable objects intelligently
ko.utils.extendObservable = function ( target, source ) {
    var prop, srcVal, isObservable = false;

    for ( prop in source ) {

        if ( !source.hasOwnProperty( prop ) ) {
            continue;
        }

        if ( ko.isWriteableObservable( source[prop] ) ) {
            isObservable = true;
            srcVal = source[prop]();
        } else if ( typeof ( source[prop] ) !== 'function' ) {
            srcVal = source[prop];
        }

        if ( ko.isWriteableObservable( target[prop] ) ) {
            target[prop]( srcVal );
        } else if ( target[prop] === null || target[prop] === undefined ) {

            target[prop] = isObservable ? ko.observable( srcVal ) : srcVal;

        } else if ( typeof ( target[prop] ) !== 'function' ) {
            target[prop] = srcVal;
        }

        isObservable = false;
    }
    return target;
};

然后我有一个 copy 本质上将要复制的对象转换为的函数 JSON 然后拿走 JSON 复制并构建一个新的javascript对象。这样可以确保不会复制所有内存指针,并且您有一个与原始对象匹配的全新对象。这里的一个关键是你必须传入一个新对象的空实例(否则我们不知道要填充什么属性)

// then finally the clone function
ko.utils.clone = function(obj, emptyObj){
    var json = ko.toJSON(obj);
    var js = JSON.parse(json);

    return ko.utils.extendObservable(emptyObj, js);
};

然后您可以这样使用它:

var tempAction = ko.utils.clone(action, new Action());

15
2018-05-10 15:09



你能不能只使用ko.mapping插件从原始源对象映射到新的目标对象? - jaffa
@jaffa - 是的,映射插件是一个合适的选择。然而,对于琐碎的需求来说,这可能是过度的。另外,我找到了 extendObservable 在应用程序范围内不需要映射插件的情况下,函数非常有用。 - ericb
这如何与ko.computed值一起使用?我认为使用toJSON和JSON.parse复制对象将丢失其所有计算值。 - Greg Ennis
@ericb +1工作,但它看起来有点矫枉过正,我有类似于你的extendObservable的方法,由于某种原因,我不能通过传递空实例和复制原始对象而不先将原始实例序列化为JSON来实现它,确实有一个我的意思是,为什么这是因为你解开可观察的东西以获得纯粹的价值? - formatc
深度克隆中可能存在一些问题,我的意思是嵌套的viewModels。 - Teddy