6

我已经搜索过这个,但到目前为止还没有找到重复的,我可能使用了错误的关键字......

我正在尝试临时更改存储在对象中的函数,但无法将其设置回原来的状态。

考虑一下:

// Set the options object
var options = {
    success: function(){
        console.log('Original Function Called');
    }
}

// Save the options
$('#foo').data('bar',options);

然后在另一个函数中:

// Get the options
var options = $('#foo').data('bar');

// Store the old options
var old_options = options;

// Temporarily change the success function
options.success = function(){
    console.log('Temporary Function Called');
}

// Save the options
// This allows the other functions to access the temporary function
$('#foo').data('bar',options);

// Do stuff here that uses the new options

// Reset the options to include the original success function
$('#foo').data('bar',old_options);

我本来希望只显示Temporary Function Called一次,但是,它似乎success用临时回调完全替换了旧回调。

谁能告诉我为什么以及如何解决这个问题?

更新

我认为这extend可以解决这个问题,但似乎问题可能更深一些。这次我决定发布我的实际代码片段。阅读前请注意以下事项:

  1. SM几乎只是 的别名jQuery,请忽略它。
  2. success并且error是提供给函数的参数

这是我的代码:

// Get the properties
var properties = $(form).data('autosave');
switch(parameter){
    case 'save':
        var old_properties = $.extend({},properties);
        // Set the new callbacks if they have been supplied
        properties.options.success = typeof success!=='undefined' ? success : old_properties.options.success;
        properties.options.error = typeof error!=='undefined' ? error : old_properties.options.error;
        // Save the properties
        $(form).data('autosave',properties);
        // Call the save method before setting the interval
        SM(form)._as_save();
            properties = $.extend({},old_properties);
        // Save the old properties
        $(form).data('autosave',properties);
        // Clear the current interval
        clearInterval(properties.interval);
        // Call the save method periodically
        properties.interval = setInterval(function(){
        SM(form)._as_save();
        },properties.options.interval);
    break;
}
// Save the properties
$(form).data('autosave',properties);
4

4 回答 4

10

当您运行此代码时:

var old_options = options;

您没有复制整个options对象以便以后恢复。您只是保存对同一对象的引用。换句话说, 和是同一个对象,所以当你给 赋一个新值时,你会同时改变它和 ——因为它们是同一个对象。old_optionsoptionsoptions.successoptions old_options

要解决此问题,您可以使用对象克隆功能来制作对象的副本,然后您可以稍后恢复。由于您使用的是 jQuery,因此可以将上面的行更改为:

var old_options = $.extend( true, {}, options );

现在,当您更改时options.success,您只是在options对象中更改它。old_options不受影响,因此您以后的调用将成功恢复它:

$('#foo').data('bar',old_options);

有趣的是,即使options.success是异步回调(可能从名称上听来),它仍然可以正常工作。这是因为无论代码稍后调用该.success()方法,它们仍应保留对已修改options对象的引用——即使您同时将旧对象恢复回元素的数据。至少有人对此抱有希望;如果其他代码重新挖掘$().data()以找到.success回调,那么您将遇到麻烦。

上面的$.extend()调用对对象进行了“深度”(递归)复制options。也就是说,如果其中的一个属性options本身就是一个对象,它也会克隆该对象,而不是仅仅复制对它的引用。

如果省略true参数,$.extend()则改为执行浅拷贝:

var old_options = $.extend( {}, options );

这仍然会创建一个新对象并复制现有对象的所有属性,但如果这些属性之一本身就是一个对象,它不会克隆该对象,它只是复制一个引用。如果它适用于您正在使用的对象的结构,则效率更高,否则您可以使用深层副本。

如果您需要保存和恢复的属性/方法是主对象的直接子对象,那么浅拷贝就足够了。这是您肯定需要深拷贝的情况:

{
    url: 'test',
    events: {
        success: function( data ) {
            // ...
        }
    }
}

这里我们有一个具有events属性的对象,该属性本身就是一个具有自己的一些属性/方法的对象(在这个例子中是一个events.success()方法。如果你对这个对象进行浅拷贝,原始和副本将共享一个公共events对象。所以如果你做了这样的事情:

options.events.success = function(...) {...};

您实际上会在options和中更新它old_options。不好。这就是需要深拷贝的地方。

于 2013-06-19T20:55:14.307 回答
3

问题是options具有引用语义:

// Store the old options
var old_options = options;

这个评论是假的。您没有旧选项的副本;相反,您对同一选项对象有另一个引用(您可以使用它来引用它的另一个名称)。

所以当你覆盖的时候options.success,这个变化也是可见的old_options.data用于存储和恢复选项值的代码是多余的。

你唯一需要做的是:

var old_success = options.success;
options.success = function() { /* whatever */ };

// code that uses the new success callback

options.success = old_success; // restore original value
于 2013-06-19T20:55:28.530 回答
2

您的问题是您正在使用对象。您正在传递对对象的引用。底层对象是相同的,您所做的只是添加一个指向同一地址的变量。

您将不得不克隆该对象。EG 创建一个具有相同属性的新对象并将它们复制过来。

这篇文章可能对你有用。

于 2013-06-19T20:55:26.030 回答
2

当你这样做时,var old_options = options你不是在复制options值,而是对它的引用。从现在开始,old_options指向options同一个记忆点,其中任何一个的变化都会影响两者。

于 2013-06-19T20:55:40.563 回答