私が最近取り組んでいるプロジェクトでは、内部オブジェクトを再帰的にコピーして、JSON オブジェクトの完全なコピーを返すことができる関数を作成する必要がありました。数回失敗した後、私はこれを思いつきました:
function copyObj(obj) {
var copy;
if (obj instanceof Array) {
copy = [];
for (var i in obj) {
copy.push(copyObj(obj[i]));
}
}
else if (obj instanceof Object) {
copy = {};
for (var prop in obj) {
copy[prop] = copyObj(obj[prop]);
}
}
else {
copy = obj;
}
return copy;
}
この関数は、プリミティブ型、配列、およびネストされた汎用 JSON オブジェクトのみを含むオブジェクトをコピーするという私の目的に完全に対応しています。たとえば、次の完璧なコピーを返します: { prop1:0, prop2:'test', prop3:[1, 2, 3], prop4:{ subprop1:['a', 'b', 'c'], subprop2:false } }
.
ただし、この関数には 1 つの問題があります。それは、他のタイプのオブジェクト (オブジェクトなど)を処理できないことRegExp
です。それらを処理する機能を追加して改善したいのですが、同時に、else if (obj instanceof [insert object type here])
. そのため、私の質問は次のとおりです。JavaScript で汎用オブジェクト (つまり、 として宣言されたオブジェクトvar obj = { }
) と適切なプロトタイプ/コンストラクターを持つオブジェクトを区別する簡単な方法はありますか? もしそうなら、そのようなオブジェクトをコピーする簡単な一般化された方法もありますか? 質問の2番目の部分に対する私の期待はノーであり、コンストラクターを呼び出すにはまだ特別な処理が必要ですが、どちらにしても確実に知りたいです.
PS誰かがコンテキストに興味を持っている場合のために、プロジェクトでは、サーバー上のアイテムの大きなリストを操作する必要がありますが、接続されているクライアントごとに異なる方法で操作する必要があります。これを処理するために私が考えることができる最も簡単な方法は、1 つのマスター リストを作成し、接続するすべての新しいクライアントのマスター リストを変更せずにサーバーに新しいコピーを複製させて操作することでしcopyObj()
た。
編集:おそらく元の質問でこれについて言及する必要がありました-これは、ブラウザーではなくサーバーとして node.js で実行されているため、ブラウザーの相互互換性は問題ではありません。
編集 2:コメントが煩雑になりすぎないようにするために、ここで言及します。上記のサンプル オブジェクトを使用して、エクスプロイトcopyObj()
に対する関数の簡単なベンチマークを試みました。JSON.parse(JSON.stringify(obj))
私のバージョンは、JSON メソッドにかかる時間の約 75% で実行されるようです (100 万回のコピーは、私の場合は約 3.2 秒、JSON の場合は約 4.4 秒かかりました)。ですから、時間をかけて自分で書いたことについて気分が良くなります。
編集 3: Vitum.us の回答にあるオブジェクト タイプのリストを基に、copyObj()
関数の更新バージョンをまとめました。私はそれを広範囲にテストしておらず、パフォーマンスは古いバージョンの約 2 倍悪いですが、実際にはすべての組み込み型で動作するはずです (リストが完全であると仮定します)。
function copyObjNew(obj) {
var copy;
if (obj.constructor === Object) {
// Generic objects
copy = {};
for (var prop in obj) {
copy[prop] = copyObjNew(obj[prop]);
}
}
else if (obj.constructor === Array) {
// Arrays
copy = [];
for (var i in obj) {
copy.push(copyObjNew(obj[i]));
}
}
else if (obj.constructor === Number || obj.constructor === String || obj.constructor === Boolean) {
// Primitives
copy = obj;
}
else {
// Any other type of object
copy = new obj.constructor(obj);
}
return copy;
}
マイクが提案したように、私は.constructor
現在このプロパティを使用していますが、うまくいっているようです。これまでにRegExp
andDate
オブジェクトでテストしましたが、どちらも正しくコピーされているようです。これについて露骨に(または微妙に)間違っていると思う人はいますか?