JavaScript:オブジェクトを値で渡す方法は?


96
  • オブジェクトをパラメーターとして渡す場合、JavaScriptは参照によってそれらを渡し、オブジェクトのローカルコピーの作成を困難にします。

    var o = {};
    (function(x){
        var obj = x;
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)
    

    oとに.fooなり.barます。

  • これを複製することで回避できます。簡単な例:

    var o = {};
    
    function Clone(x) {
       for(p in x)
       this[p] = (typeof(x[p]) == 'object')? new Clone(x[p]) : x[p];
    }
    
    (function(x){
        var obj = new Clone(x);
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)
    

    o.fooまたははありません.bar


質問

  1. ローカルコピー/クローンを作成する以外に、値でオブジェクトを渡すより良い方法はありますか?

3
これが必要なユースケースは何ですか?
jondavidjohn 2009

2
プログラミングの楽しみ。新しいJSエンジンがこれに対処したかどうかを確認します(技術的には、値で参照を渡します)が、主に楽しみのためです。
vol7ron 2011

1
JavaScriptは参照渡しまたは値渡しの言語ですか?を参照してください-JavaScriptは参照渡しされません。Javaのように、オブジェクトを関数に渡す場合、値で渡しますが、値は参照です。Javaは参照渡しですか?も参照してください
Richard JP Le Guen、2011

1
私が知る限り、値でオブジェクトを渡すことはできません。たとえあったとしても、それはあなたが上記で言及しているクローン作成を実際に行うことになるので、私が見ることができるものは何もありません。おそらく3行のコードを節約する以外に。
Thor84no 2011

1
@ vol7ronはい、彼らは言語設計特性を正しく実装することによってそれに対処しました。
jondavidjohn 2011

回答:


61

あんまり。

あなたが実際に必要なものに応じて、1つの可能性はセットにすることもo、新しいオブジェクトのプロトタイプとして。

var o = {};
(function(x){
    var obj = Object.create( x );
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o);

alert( o.foo ); // undefined

したがって、追加したプロパティはに追加されobjませんo。のプロパティobjと同じプロパティ名で追加されたプロパティは、プロパティoをシャドウしoます。

もちろん、に追加されたプロパティは、シャドウされていない場合oでも使用できobjます。oプロトタイプチェーン内にあるすべてのオブジェクトは、への同じ更新を参照しますo

また、obj配列などの別のオブジェクトを参照するプロパティがある場合は、オブジェクトにメンバーを追加する前にそのオブジェクトをシャドウする必要があります。そうでない場合、それらのメンバーはに追加されobj、すべてのオブジェクト間で共有されます。持っているobjプロトタイプチェーン内に。

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // 'new_value'

ここでは、bazon obazプロパティで配列をシャドウしていないためobjo.baz配列が変更されていることがわかります。

したがって、代わりに、最初にそれをシャドウイングする必要があります。

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz = [];
    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // undefined

3
+1、私もObject.create私の答えで提案するつもりでしたが、私はコピーの浅さを説明するためにストレッチしたくありませんでした;-)
Andy E

+1、これを忘れました。やってみましたvar obj = new Object(x)。私には、デフォルトでnew Object(x)実行Object.create(x)されると思います-興味深い
vol7ron

1
@ vol7ron:ええ、たぶんnew Object(x)同じオブジェクトを吐き出します(おそらくお気付きのように)。クローンを作成するネイティブな方法があったらいいですね。作品に何かがあるかどうかわかりません。
user113716 2011

43

この回答を確認してくださいhttps://stackoverflow.com/a/5344074/746491

つまり、JSON.parse(JSON.stringify(obj))オブジェクトをjsonにシリアル化できる場合は、オブジェクトをコピーする高速な方法です。


2
オブジェクトを常にシリアル化できるとは限らないため、これを回避する傾向があります。また、IE7 / 8のような中年のブラウザーでJSONは、は含まれておらず、定義する必要があるためCloneです。ただし、JSONはデータベースやIDEのような非ブラウザーベースのソフトウェアにより深く統合されているため、私の意見は将来変わる可能性があると思います
vol7ron

1
関数と日付オブジェクトがない場合は、JSON.parseソリューションが最適のようです。stackoverflow.com/questions/122102/…–
Herr_Hansen

本当に?!?オブジェクトのコピーを作成するためだけにJSONにシリアル化しますか?この不条理な言葉を嫌うさらに別の理由。
RyanNerd

1
JSONのシリアライズは本当にそれをコピーする最も速い方法ですか?NodeJSのドキュメントによると、JSON操作は最もCPUを集中的に使用するタスクです。nodejs.org/en/docs/guides/dont-block-the-event-loop/...
harshad

38

オブジェクトのディープコピーを実行するクローン関数を次に示します。

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);

    return temp;
}

これで、次のように使用できます:

(function(x){
    var obj = clone(x);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o)

3
これは深い答えでした。
ネイサン

綺麗な!ベストアンサーIMO。
ガンダー

23

使用する Object.assign()

例:

var a = {some: object};
var b = new Object;
Object.assign(b, a);
// b now equals a, but not by association.

同じことを行うよりクリーンな例:

var a = {some: object};
var b = Object.assign({}, a);
// Once again, b now equals a.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


4
より良い:var b = Object.assign({}, a);
Bergi

同意した。それに応じて回答を更新します。また、すべてのブラウザがassign()メソッドをサポートしているわけではありません。互換性の要件によっては、MDNのようなポリフィルの使用が必要になる場合があります。
ブランドン

3
@Brandonは、あなたが提供したまさにそのリンクで、ディープクローン作成に対して警告を発します。その参照値のみをコピーします。」
pwilcox 2017年

2
これは機能しますが、ディープコピーを実行しないので、内部aに含まれるオブジェクトは参照としてコピーされます。
Stoinov

15

JavaScriptでオブジェクトがどのように機能するかについて少し混乱しています。オブジェクトの参照変数の値です。シリアル化されていない値はありません。オブジェクトを作成すると、その構造はメモリに格納され、割り当てられた変数はその構造への参照を保持します。

あなたが求めているものが、ある種の簡単なネイティブ言語構造で提供されていたとしても、それは技術的にはクローンです。

JavaScriptは実際には単なる値渡しです...渡された値が何かへの参照である可能性があるだけです。


1
ねえアンディ!私は混乱していませんが、JSがデフォルトでクローンを作成することを期待していました。セルフクローニングには大きな非効率性があるので、エンジンがそれを実行した場合、それがより良いと確信しています。しかし、必要性/需要が利益を超えるとは思えません。
vol7ron 2011

12
JSでは、人々が値ではなく参照で言うとき、それらは値が重複ではなくポインタのようなものであることを意味します。それはかなり確立された専門用語です。
エリック・レッペン、2012

4
@エリック:私の控えめな意見では、新参者をその言語に混乱させないように、それをより明確に定義する方が良いと思います。何かが十分に確立されているからといって、それが適切である、または技術的に正しいとは限らない(それが正しくないため)。実際にどのように機能するかを説明するよりも簡単に言うことができるので、人々は「参照によって」言う。
アンディE

6
値は、メモリ内の場所への参照です。最終的には、コピーを操作するのではなく、メモリ内の同じアイテムを操作することになります。その区別を曖昧にすることは、初心者をどのように助けることになっているのでしょうか?これは、O'ReillyのAuthoritative Guideを含む多くの技術書で説明されている方法です。
Erik Reppen、

1
@AndyE私は大騒ぎが何であるかを見るためにもう少し読んだ。IMO、これはJSが「真の」OOPを持っているか、他の言語と比較して何かを「メソッド」と適切に呼ぶことができるかという問題のようなものです。割り当て演算子の動作のパフォーマンスを低下させる分岐でクロージャベースの言語でコピーされたポインタを上書きするのではなく、varをメモリ内の同じオブジェクトに直接書き込むものとして扱うことが実際に可能かどうかはわかりません。JSと他の言語で私たちが意味することの唯一の実際の動作の違いは、代入演算子を使用したときに何が起こるかです。
エリックレッペン、

15

これを使って

x = Object.create(x1);

xそしてx12つの異なるオブジェクト、変更になるxわけではありません変更x1


私はObject.assign()を使用することになります。単純なテストケースでは.create()は機能しますが、より複雑な(まだパターンを見つけることができない)場合でも、同じアドレスを参照します。
Coisox 2018

オブジェクトの値が参照によって格納されていない場合、つまり、値が数値やブール値などの主要なデータ型である場合にのみ変更されます。たとえば、以下のコードを考えてみましょうa = {x:[12]、y:[]}; b = Object.create(a); bxpush(12); console.log(ax);
Jjagwe Dennis

8

JavaScriptは常に値で渡されます。この場合は、参照のコピーをo無名関数に渡します。コードは参照のコピーを使用していますが、単一のオブジェクトを変更しています。値以外の方法でJavaScriptを渡す方法はありません。

この場合、必要なのは、基になるオブジェクトのコピーを渡すことです。オブジェクトを複製することが唯一の手段です。あなたのクローンメソッドは少し更新が必要です

function ShallowCopy(o) {
  var copy = Object.create(o);
  for (prop in o) {
    if (o.hasOwnProperty(prop)) {
      copy[prop] = o[prop];
    }
  }
  return copy;
}

1
+1「常に値渡し」の場合(「[参照]の値渡し」ではなく)オブジェクト共有呼び出しを使用し、「値」を「オブジェクト自体」に昇格させます(ただし、参照は単なる実装の詳細であり、JavaScriptに公開されていない(または仕様で直接言及されていない)ため、オブジェクトはコピー/複製/複製されません)。

リモート接続から受信しているオブジェクトにプロパティを追加する際に問題が発生したため(そのオブジェクトにプロパティを追加できないことが問題である可能性があると考えました)、このシャローコピー関数を試してこのエラーが発生しました-オブジェクトプロトタイプvar copy = Object.create(o);ではObjectかnullのみです。私の問題を解決する方法
Harshit Laddha 14

これはディープコピーではありませんか?
タム川2014

@RiverTamいいえ、オブジェクト内のオブジェクトのクローンを作成しないため、ディープコピーではありません。ただし、各サブオブジェクトで自分自身を再帰的に呼び出すだけで、簡単にディープコピーを作成できます。
ABPerson

7

jQueryユーザーへの考慮事項として、フレームワークを使用してこれを簡単な方法で行う方法もあります。jQueryによって私たちの生活が少し楽になります。

var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy    = jQuery.extend(true, {}, o); 

参照:


ああ-あなたはこの古い質問を発見しました:)元の質問はJS言語の探求に関するものだったと思います。私の例はクローンではなくディープコピーだったので、当時は私の用語法も弱いようでした。しかし、オブジェクト/ハッシュは参照によってのみ渡されるようです。これはスクリプト言語では一般的です
vol7ron

1
:)正確な機能を探してそれを渡った。元のオブジェクトを保持しながら送信する前に編集のためにコピーする必要があるデータオブジェクトがありました。私はネイティブJSソリューションを好み、自分のベースライブラリで使用しますが、jQueryソリューションは一時的な修正です。受け入れられた答えは素晴らしいです。:D
ブレットウェーバー

6

実際、JavaScriptは常に値渡しです。ただし、オブジェクト参照はであるため、オブジェクトは参照によって渡されるように動作します

したがって、これを回避するには、オブジェクトを文字列化し、JSONを使用して解析します。以下のコードの例を参照してください。

var person = { Name: 'John', Age: '21', Gender: 'Male' };

var holder = JSON.stringify(person);
// value of holder is "{"Name":"John","Age":"21","Gender":"Male"}"
// note that holder is a new string object

var person_copy = JSON.parse(holder);
// value of person_copy is { Name: 'John', Age: '21', Gender: 'Male' };
// person and person_copy now have the same properties and data
// but are referencing two different objects

2
はい、これは単純な変換/再変換のオプションです。一般に、文字列の操作は非常に非効率的です。ただし、しばらくの間はJSON解析のパフォーマンスを評価していません。これは単純なオブジェクトに対しては機能しますが、値として関数を含むオブジェクトはコンテンツを失うことに注意してください。
vol7ron 2017

4

use obj2 = {... obj1}これで、両方のオブジェクトに同じ値のバストの異なる参照があります


2
JavaScriptの新機能はこちら。スプレッド演算子は、参照によってコピーする代わりに新しい値を作成しますか?では、なぜこれが反対投票されたのですか?
jo3birdtalk

2
一見、それは私にはうまくいきます。しかし、私はそれが変数の最初の層にのみあることを発見しました。例えば、それは時にうまくいくlet a = {value: "old"} let b = [...a] b.value = "new"、となり{value: "old"}、Bになります{value: "new"}。しかし、それは時にうまくいかないlet a = [{value: "old"}] let b = [...a] b[0].value = "new"、となり[{value: "new"}]、Bになります[{value: "new"}]
Brady Huang

はい、それは浅いコピーをします。オブジェクトをネストしている場合は、ディープコピーを行う必要があります。簡単にするために、lodashのclone関数とdeepClone関数を使用できます
Geetanshu Gulati

3

オブジェクトを値で(参照ではなく)コピーする必要があり、このページが役に立ちました。

JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?。特に、John Resigによる次のコードでオブジェクトを複製します。

//Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

問題は、jQueryライブラリではなく、JavaScriptに関するものです。
j08691

1
確かに、なんらかの理由でJqueryを使用できない人にとっては、これは役に立ちません。ただし、jqueryはjavascriptライブラリーであるため、ほとんどのjavascriptユーザーには役立ちます。
Anthony Haffey

それはサーバー上でそれを行おうとするプログラマーを助けません。だから-いいえ。有効な答えではありません。
TomaszGałkowski2017年


1

簡単に言えば、それは単なる複雑すぎる複雑なプロキシですが、Catch-Allプロキシがそれを実行できるのでしょうか?

var o = {
    a: 'a',
    b: 'b',
    func: function() { return 'func'; }
};

var proxy = Proxy.create(handlerMaker(o), o);

(function(x){
    var obj = x;
    console.log(x.a);
    console.log(x.b);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(proxy);

console.log(o.foo);

function handlerMaker(obj) {
  return {
   getOwnPropertyDescriptor: function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name);
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getPropertyDescriptor:  function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name); // not in ES5
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getOwnPropertyNames: function() {
     return Object.getOwnPropertyNames(obj);
   },
   getPropertyNames: function() {
     return Object.getPropertyNames(obj);                // not in ES5
   },
   defineProperty: function(name, desc) {

   },
   delete:       function(name) { return delete obj[name]; },   
   fix:          function() {}
  };
}

リチャード、私は応答に少し遅れています:)しかし、私はいくつかの理由でそうしたことはなかったと思います。あなたの答えは長く見えた、そして私はあなたが「プロキシ」を使うのを切らされた。3年経った今でもまだ電話が切れる。おそらく私はクラスで十分な注意を払っていなかったかもしれませんが、ネットワーキングのように、CSのプロキシは仲介者として機能することになっていると思いました。多分それもここにあり、それが仲介しているものについて私は正しく考えていません。
vol7ron 2014

0

lodashまたはnpmを使用している場合は、lodashのmerge関数を使用して、次のように、オブジェクトのすべてのプロパティを新しい空のオブジェクトにディープコピーします。

var objectCopy = lodash.merge({}, originalObject);

https://lodash.com/docs#merge

https://www.npmjs.com/package/lodash.merge


ディープコピーにはイベントも含める必要があります。lodashに詳しくないので、イベントを新しいオブジェクトにミラーリングしますか?
vol7ron 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.