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ます。つまり、内部のプロパティの 1 つがそれoptions自体がオブジェクトである場合、そのオブジェクトへの参照をコピーするだけでなく、そのオブジェクトのクローンも作成します。

true引数を省略した場合$.extend()は、代わりに浅いコピーを行います。

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

これでも新しいオブジェクトが作成され、既存のオブジェクトからすべてのプロパティがコピーされますが、それらのプロパティの 1 つがそれ自体がオブジェクトである場合、そのオブジェクトは複製されず、参照がコピーされるだけです。これは、使用しているオブジェクトの構造で機能する場合はより効率的です。それ以外の場合は、ディープ コピーを使用できます。

保存および復元する必要があるプロパティ/メソッドがメイン オブジェクトの直接の子である場合は、浅いコピーで十分です。ディープ コピーが確実に必要になるケースを次に示します。

{
    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をコピーします。これからは、同じメモリ スポットをポイントし、それらのいずれかを変更すると、両方に影響します。optionsold_optionsoptions

于 2013-06-19T20:55:40.563 に答える