web-dev-qa-db-ja.com

JavaScriptでオブジェクトをマージするネイティブな方法

JavaScriptのオブジェクトには、ネイティブのマージ操作はありません。 2つのオブジェクトがある場合は、

_{a:1, b:2}
{c:3, d:4}
_

そして取得したい

_{a:1, b:2, c:3, d:4}
_

私の知る限り、オブジェクトを反復処理する必要があります。つまり、左マージ戦略または右マージ戦略のいずれかを決定し、次に(単純化)のようなことを行うということです。

_for (key in object2) {
  object1[key] = object2[key];
}
_

これで結構です。ただし、Javascriptにはcallおよびprototype機能があります。たとえば、argumentsArrayに変換するには、次のようにします。

Array.prototype.slice.call(arguments)

このアプローチは既存のネイティブコードを利用するため、プログラマーの愚かさの影響を受けにくく、ネイティブでない実装よりも高速に実行できます。

質問

このプロトタイプ/呼び出しパターンを、おそらくDOMのAttributeまたはNodeトラバーサル機能、または汎用のString関数のいくつかで使用するトリックはありますか?ネイティブオブジェクトのマージ?

コードは次のようになります。

var merged = somethingrandom.obscuremethod.call(object1, object2)

その結果、走査せずにネイティブマージを取得できます。

可能な、最適ではないソリューション

constructorObjectプロパティを使用して、あるオブジェクトを強制的に別のオブジェクトのコンストラクターにしてから、複合オブジェクトに対してnewを実行すると、無料でマージ。しかし、私はこの呼び出しを行うためのJavaScriptのconstructor機能の完全な影響をしっかりと把握していません。

補題

同じ質問がArraysにも当てはまります。一般的な問題は、たとえば7つの数値配列を取り、それらの配列の共通部分を見つけることです。つまり、7つの配列すべてに存在する数です。

あなたはそれらを一緒に連結し、ソートを行い、そしてトラバーサルを行うことができます。しかし、配列をネイティブに強制することができるどこかに隠れている一般的な交差がある場合、それはいいでしょう。

何かご意見は?

編集:

途中まで行く

アレイの問題については、次のことを実行できます。

array.concat(a、b、c).sort()。join( ':')そして、トラバースするためにいくつかのトリッキーなRegExpキャプチャと繰り返しパターンを使用します。 RegExpの実装は、わからない場合は、非常に単純なスタックベースの仮想マシンで実行します。正規表現を初期化すると、それは実際にはコンパイルされるプログラムです(RegExp.compileは非推奨のJSメソッドです)。次に、ネイティブは文字列を猛烈に速い方法で実行します。おそらく、それをメンバーシップのしきい値に利用して、パフォーマンスを向上させることができます...

それでもまだ完全には行きません。

47
kristopolous

これに対する私の答えは残念ですが、それでも:

いいえ

これの理由は単純です。jQueryでResig氏がマージ(またはオブジェクトに対して呼び出されると「拡張」)を実装すると、質問のループと同じようにループが実行されます。あなたはそれを見ることができます ここ 。そして、John Resigがそれを行うための賢い組み込み方法を見つけられなかった場合、私はあえて言うだけで、stackoverflowの単なる致命的なものはどちらでもないでしょう:)

24
Jakob

百万ドルの質問!私はこれを数多くの方法で試してみましたが、上記のループ方法は常に最も汚れているように見えました。 ES6のObject.setPrototypeOf()を使用すると、「プロパティオーバーライド」オブジェクトを「デフォルトのプロパティ」オブジェクトに委任でき、実行しようとしていることをほぼ達成できますが、Object.setPrototypeOf()を使用すると、いくつかの深刻な影響があります、スクリプト全体に対するブラウザのコンパイラ最適化を無効にするようなものです。

また、ループソリューションとObject.setPrototypeOf()ソリューションの両方で、「プロパティオーバーライド」オブジェクトが「デフォルトプロパティ」オブジェクトを変更できる状況が残ります。

defaultObj = {
    a: [1, 2]
}
...
overrideObj = {
    b: 3
}
Object.setPrototypeOf(overrideObj, defaultObj);
console.log(overrideObj); // {a: [1, 2], b: 3}
// Great!
...
overrideObj.a.Push(4);
console.log(defaultObj); // {a: [1, 2, 4]}
// Uh-oh.

これは問題ではないと思うかもしれませんが、このオブジェクトをサードパーティのlibの構成として使用しているとしましょう。これで、デフォルトオブジェクトのコントロールと、そのオブジェクトで参照されているすべてのものがサードパーティのlibに渡されます。

より良い解決策は、JSON.stringifyとJSON.parseを使用してオブジェクトをコピーおよび結合することです。ここに例の要点があります: https://Gist.github.com/spikesagal/6f7822466887f19b9c65

HTH

3
Spike Sagal

私が知っていることではありません。また、次のようにマージメソッドを記述します。

function mergeInto(o1, o2) {
  if (o1 == null || o2 == null)
    return o1;

  for (var key in o2)
    if (o2.hasOwnProperty(key))
      o1[key] = o2[key];

  return o1;
}
2
Brian Donovan

ES6(ES2015)を使用すると、Object.assignメソッドを使用できます。

var x = {a:1, b:2};
var y = {c:3, d:4};
var z = Object.assign({},x,y);

ES7(ES2016、Chrome 60+またはBabel))を使用すると、オブジェクトスプレッド演算子を使用できます。

var x = {a:1, b:2};
var y = {c:3, d:4}; 
var z = {...x, ...y};
2
DanSavoy

フレームワークを必要とせずに、ネイティブJS 1.7を使用して以下を実行できます。 fiddle の例を参照してください(例は単純なオブジェクトのみを対象としており、複雑なネストされたオブジェクトは対象外です)

var obj1 = {a: "a", b: "b"};
var obj2 = {c: "c", d: "d"};

// The magic: ugly but works perfectly
var value = (JSON.stringify(obj1).concat(JSON.stringify(obj2))).replace("}{", ",");

document.getElementById("lbl1").setAttribute("value", value);

// back to object
var obj3 = JSON.parse(value);
document.getElementById("lbl2").setAttribute("value", obj3.a + " " + obj3.b + " " + obj3.c + " " + obj3.d);
1
Ron Anon

ECMA-Scriptにはネイティブの方法はありません。以下を使用します。

function merge(o1,o2) {
 if (typeof(o1)!=='object') o1={};
 if (typeof(o2)!=='object') o2={};
 for (var k in o2) {
 if (o1[k]!==undefined)
  alert ('Collision Error'); // TODO
 else
   o1[k]=o2[k];
 }
 return o1;
}
0
Informate.it