変数の値を別の値にコピーする


100

値としてJSONオブジェクトを持つ変数があります。この変数を他の変数に直接割り当てて、同じ値を共有するようにします。これがどのように機能するかです:

var a = $('#some_hidden_var').val(),
    b = a;

これは機能し、どちらも同じ値です。mousemoveイベントハンドラーを使用bして、アプリ全体を更新します。ボタンをクリックbすると、元の値、つまりに保存されている値に戻したいのですがa

$('#revert').on('click', function(e){
    b = a;
});

私は、同じ使用する場合は、この後にmousemoveイベントハンドラを、それは両方を更新aし、b以前のそれが唯一の更新されたときに、b予想通り。

私はこの問題に困惑しています!ここで何が問題になっていますか?


マウスムーブハンドラを表示してください。また、それがオブジェクトではなく、JSON(文字列)であると想定してa設定された.val()場合、それは正しいですか?JSON.parse(a)ある時点で実際のオブジェクトを取得するために使用しますか?
nnnnnn 2013

はい、それは文字列ですが$.parseJSON、それをオブジェクトに変換するために使用します。構造:{ 'key': {...}, 'key': {...}, ...}。ここにコードを投稿することはできません。職場では許可されていません。
Rutwick Gangurde 2013

1
それでaオブジェクトですか?
Armand

1
スコーピングの問題のように見えます。aグローバル変数ですか?
msturdy 2013

2
あなたが示したコードには文字列しかありません。オブジェクトを話している場合、複数の変数が同じオブジェクトを参照する可能性があるため、そのオブジェクトは任意の変数を介して変更できます。したがって、変数を操作するコード(どこ$.parseJSON()に入るのか?)を確認しないと、問題が何であるかを言うのは困難です。職場のルールに関しては、実際の実際のコードを完全に投稿する必要はありません。問題を示す短くてより一般的な例を考え出すだけです(理想的には、jsfiddle.netのライブデモへのリンクを含めます)。 。
nnnnnn 2013

回答:


198

=JavaScript の演算子が行うことと行わないことを理解することが重要です。

=オペレータはなりませんコピーデータのを。

=オペレータは、新しい作成し、参照同じデータを。

元のコードを実行した後:

var a = $('#some_hidden_var').val(),
    b = a;

aそしてb今、2つの異なる名前である同じオブジェクト

このオブジェクトの内容に加えた変更は、a変数を介して参照する場合もb変数を介して参照する場合も同じように表示されます。それらは同じオブジェクトです。

したがって、後でこのコードbを使用して元のaオブジェクトに「戻そう」とすると、次のようになります。

b = a;

とまったく同じものであるため、コードは実際には何もしませ。コードは、作成した場合と同じです。ab

b = b;

明らかに何もしません。

新しいコードが機能するのはなぜですか?

b = { key1: a.key1, key2: a.key2 };

ここでは、{...}オブジェクトリテラルを使用して新しいオブジェクトを作成しています。この新しいオブジェクトは、古いオブジェクトと同じではありません。したがってb、この新しいオブジェクトへの参照として設定しているので、必要な処理を実行できます。

任意のオブジェクトを処理するには、Armandの回答に記載されているようなオブジェクトのクローン作成関数を使用できます。または、jQueryを使用しているため、$.extend()関数を使用するだけです。この関数は、オブジェクトの浅いコピーまたは深いコピーを作成します。(これを、オブジェクトではなくDOM要素をコピーするための$().clone()メソッドと混同しないでください。)

浅いコピーの場合:

b = $.extend( {}, a );

または深いコピー:

b = $.extend( true, {}, a );

浅いコピーと深いコピーの違いは何ですか?浅いコピーは、オブジェクトリテラルで新しいオブジェクトを作成するコードに似ています。元のオブジェクトと同じプロパティへの参照を含む新しい最上位オブジェクトを作成します。

オブジェクトに数値や文字列などのプリミティブ型のみが含まれている場合、ディープコピーとシャローコピーはまったく同じことを行います。しかし、あなたのオブジェクトが内部にネスト他のオブジェクトや配列が含まれている場合は、浅いコピーはしませんコピーし、それらのネストされたオブジェクトを、それは単にそれらへの参照を作成します。したがって、ネストされたオブジェクトで、最上位オブジェクトと同じ問題が発生する可能性があります。たとえば、次のオブジェクトがあるとします。

var obj = {
    w: 123,
    x: {
        y: 456,
        z: 789
    }
};

そのオブジェクトの浅いコピーを行う場合x、新しいオブジェクトのプロパティはx元のオブジェクトと同じオブジェクトです。

var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;

これで、オブジェクトは次のようになります。

// copy looks as expected
var copy = {
    w: 321,
    x: {
        y: 654,
        z: 789
    }
};

// But changing copy.x.y also changed obj.x.y!
var obj = {
    w: 123,  // changing copy.w didn't affect obj.w
    x: {
        y: 654,  // changing copy.x.y also changed obj.x.y
        z: 789
    }
};

これはディープコピーで回避できます。ディープコピーは、ネストされたすべてのオブジェクトと配列(およびArmandのコードのDate)に再帰し、トップレベルのオブジェクトのコピーを作成したのと同じ方法でそれらのオブジェクトのコピーを作成します。したがって、変更copy.x.yしても影響はありませんobj.x.y

短い答え:疑問がある場合は、おそらく深いコピーが必要です。


ありがとう、これは私が探していたものです。それを回避する方法はありますか?私の現在の状況では、プロパティが2つしかないため、修正は簡単でした。しかし、より大きなオブジェクトが存在する可能性があります。
Rutwick Gangurde 2013

1
Armandの答えには、トリックを実行するオブジェクトクローン関数があります。または、jQueryを使用しているため、組み込み$.extend()関数を使用できます。上記の詳細。:-)
Michael Geary

素晴らしい説明マイケル!
Rutwick Gangurde 2013

7
@MichaelGearyそれはごちゃごちゃかもしれませんが、ルールはオブジェクトにのみ適用され、プリミティブ変数には適用されませんか?それは答えで注目に値するかもしれません
Jonathan dos Santos '20

これに対するJSの解決策は、Michael Gearyが言ったようにArmandsの回答にあります。=
AlexanderGriffin

57

JSONを使用して動作することがわかりましたが、循環参照がないか確認してください

var newInstance = JSON.parse(JSON.stringify(firstInstance));

2
これは少し遅いと思いますが、この方法に問題はありますか?それは最初の試みで驚くほどうまくいくようです。
Mankind1023

2
これはほとんどすべての状況で優れていますが、具体的になる可能性のあるデータを使用している場合、プログラムがクラッシュします。説明するコードの一部: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)TypeErrorが発生します
schu34

このソリューションはうまく機能します。しかし、これは処理中にどれくらい高価ですか?これは二重否定で機能するようです。
アベルCallejo

29

かなり長い間問題はすでに解決されていますが、将来の参考のために可能な解決策は

b = a.slice(0);

注意してください、これはaが数値と文字列のネストされていない配列である場合のみ正しく機能します



15

この理由は簡単です。JavaScriptはb = a参照を使用するため、割り当てるときに参照を割り当てるbため、更新時には更新aも行われますb

私はこれをstackoverflowで見つけました。オブジェクトのディープコピーを実行する場合は、このメソッドを呼び出すだけで、今後このような事態を防ぐことができます。

function clone(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

ありがとう!新しい質問を投稿する前にその投稿を見ました。しかし、それが私のシナリオに役立つかどうかはわかりませんでした。それが私が必要としていることがわかりました。
Rutwick Gangurde 2013

if (obj instanceof x) { ... }もしそうなら、最初のものを除いてすべてがそうでなければならないのですか?
ザック

@Zacこの場合はありません。すべてのif-body(対象)は値を返します。(次のifの前に関数を終了します)
Bitterblue '12

8

なぜ答えがとても複雑なのか理解できません。Javascriptでは、プリミティブ(文字列、数値など)は値で渡され、コピーされます。配列を含むオブジェクトは、参照によって渡されます。いずれの場合も、新しい値またはオブジェクト参照を「a」に割り当てても「b」は変更されません。ただし、「a」の内容を変更すると、「b」の内容も変更されます。

var a = 'a'; var b = a; a = 'c'; // b === 'a'

var a = {a:'a'}; var b = a; a = {c:'c'}; // b === {a:'a'} and a = {c:'c'}

var a = {a:'a'}; var b = a; a.a = 'c'; // b.a === 'c' and a.a === 'c'

上記の行のいずれかを(一度に1つずつ)ノードまたは任意のブラウザのJavaScriptコンソールに貼り付けます。次に、変数を入力すると、コンソールにその値が表示されます。


4

文字列または入力値の場合、これを単純に使用できます。

var a = $('#some_hidden_var').val(),
b = a.substr(0);

なぜプリミティブをサブストリング化するのですか?割り当てるだけで、文字列をコピーします。オブジェクトと配列(オブジェクト)のみが参照渡しされます。
トレントンD.アダムス

2

ここでの答えのほとんどは、組み込みのメソッドまたはライブラリ/フレームワークを使用しています。この単純な方法はうまくいくはずです:

function copy(x) {
    return JSON.parse( JSON.stringify(x) );
}

// Usage
var a = 'some';
var b = copy(a);
a += 'thing';

console.log(b); // "some"

var c = { x: 1 };
var d = copy(c);
c.x = 2;

console.log(d); // { x: 1 }

天才!他のメソッドは、その中の配列を含め、すべてを辞書に変換します。この私は、オブジェクトのクローンを作成し、内容はこれとは異なり、完全なままで:Object.assign({},a)
ティキ

0

とりあえず自分で解決しました。元の値には2つのサブプロパティしかありません。からのプロパティを使用して新しいオブジェクトを再構成し、aそれをに割り当てましたb。これで、イベントハンドラーはのみを更新bし、オリジナルaはそのままになります。

var a = { key1: 'value1', key2: 'value2' },
    b = a;

$('#revert').on('click', function(e){
    //FAIL!
    b = a;

    //WIN
    b = { key1: a.key1, key2: a.key2 };
});

これは正常に動作します。上記を除いて、コードのどこにも1行も変更していません。希望どおりに機能します。だから、私を信じて、他には何も更新していませんでしたa


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.