2

私が最近取り組んでいるプロジェクトでは、内部オブジェクトを再帰的にコピーして、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現在このプロパティを使用していますが、うまくいっているようです。これまでにRegExpandDateオブジェクトでテストしましたが、どちらも正しくコピーされているようです。これについて露骨に(または微妙に)間違っていると思う人はいますか?

4

2 に答える 2

0

これを使用して、オブジェクトが正規表現であるかどうかを検出できます

Object.prototype.toString.call( regexpObject ) == "[object RegExp]"

これは、オブジェクトのクラスを取得するための仕様で言及されている方法です。

ECMAScript 5から、セクション 8.6.2 オブジェクトの内部プロパティとメソッド:

[[Class]] 内部プロパティの値は、組み込みオブジェクトの種類ごとにこの仕様によって定義されます。ホスト オブジェクトの [[Class]] 内部プロパティの値は、 "Arguments"、"Array"、"Boolean"、"Date"、"Error"、"Function"、"JSON"のいずれかを除く任意の文字列値にすることができます。 、「Math」、「Number」、「Object」、「RegExp」、および「String」。[[Class]] 内部プロパティの値は、さまざまな種類のオブジェクトを区別するために内部的に使用されます。この仕様は、Object.prototype.toString (15.2.4.2 を参照) を介する場合を除いて、プログラムがその値にアクセスする手段を提供しないことに注意してください。

RegExp は、セクション 15.10 RegExp(RegularExpression)Objectsの仕様で定義されているオブジェクトのクラスです。

RegExp オブジェクトには、正規表現と関連するフラグが含まれています。

次に、次を使用して RegExp オブジェクトをコピーできます。new RegExp()

var oldObject = /[a-z]+/;
var newObject = new RegExp(oldObject);
于 2013-10-30T16:47:19.233 に答える