Node.jsでのオブジェクトの複製


203

node.jsでオブジェクトを複製する最良の方法は何ですか

たとえば、次のような状況は避けたいです。

var obj1 = {x: 5, y:5};
var obj2 = obj1;
obj2.x = 6;
console.log(obj1.x); // logs 6

オブジェクトは属性として複雑な型を含む可能性があるため、単純なfor(var x in obj1)は解決しません。自分で再帰的なクローンを作成する必要がありますか?それとも、私が見ないものに組み込まれているものがありますか?


23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas

4
上記の@SalmanPKのコメントでは、これは浅いクローンであることに注意してください。そのため、sliftyの例では機能しますが、ネストされた配列またはオブジェクトがある場合、それらは参照になります。:/
ジェシー

1
私はこの記事が非常に役に立ったと感じました: heyjavascript.com/4-creative-ways-to-clone-objects
ジョーダンハドソン

3
@Jordan Hudson-2番目の例でのJSONの非常に優れた使用法。var newObj = JSON.parse(JSON.stringify(oldObj)); //今、newObjはクローンです。唯一の問題は、stringifyが再帰参照で機能しないため、注意が必要です。
Kfir Erez 2013

回答:


298

可能性1

飾り気のないディープコピー:

var obj2 = JSON.parse(JSON.stringify(obj1));

可能性2(非推奨)

注意:このソリューションは、Node.jsドキュメントで非推奨としてマークされています

util._extend()メソッドは、内部Node.jsモジュールの外部で使用することを意図したものではありません。コミュニティはとにかくそれを見つけて使用しました。

これは非推奨であり、新しいコードでは使用しないでください。JavaScriptには、Object.assign()を介して非常によく似た組み込み機能が付属しています。

元の答え::

浅いコピーの場合は、Nodeの組み込みutil._extend()関数を使用します。

var extend = require('util')._extend;

var obj1 = {x: 5, y:5};
var obj2 = extend({}, obj1);
obj2.x = 6;
console.log(obj1.x); // still logs 5

Nodeの_extend関数のソースコードはこちらにあります:https : //github.com/joyent/node/blob/master/lib/util.js

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || typeof add !== 'object') return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

5
質問は特に再帰的なクローンを必要としました。これは浅いクローンです。
ベンジャミンアトキン2013

28
名前は、_*それがプライベートメソッドであり、信頼されるべきではないことを意味するはずです。
2013

7
任意のサイズのすべてのJavaScriptプロジェクトには、extend()の実装が1つ以上あり、Nodeも例外ではありません。Node.jsコアはこの関数を広範囲に使用します。アイザックを引用すると、「すぐにはどこにも行かない」
ジンボ2013

2
私には完璧に働きました。Objectプロトタイプ
imoをいじる

12
これは間違った答えです。ノードのドキュメントによると: nodejs.org/api/util.html#util_util_extend_obj このutil._extend()メソッドは、内部Node.jsモジュールの外部で使用することを意図したものではありません。コミュニティはとにかくそれを見つけて使用しました。 これは非推奨であり、新しいコードでは使用しないでください。JavaScriptには、非常によく似た組み込み機能が付属していますObject.assign().
Jordie

265

Object.assign記載されていないことに驚いています。

let cloned = Object.assign({}, source);

利用可能な場合(例:Babel)、オブジェクト拡散演算子を使用できます:

let cloned = { ... source };

1
あなたは私の日を救った!ありがとう
wzr1337

2
これは、サードパーティのライブラリをインポートしたり、不完全なJSON回避策を使用したりするよりもはるかに優れたソリューションです。ありがとう!
ニールS

75
これは浅いコピーです
ジョーダンデビッドソン

14
ディープクローンの警告、ディープクローンについては、他の代替手段を使用する必要があります。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
gsalgadotoledo

1
オブジェクトスプレッドオペレーターはES6のものではなく、ステージ3の提案です。つまり、私が理解していることからではなく、バベルでそれを使用することができます。github.com/tc39/...
macdja38

24
Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

これにより、使用できる拡張メソッドが定義されます。コードはこの記事からています。


これがどのように機能するかはわかりません。元のオブジェクトを変更します!この関数を使用してオブジェクトのクローンを取得するにはどうすればよいですか?ここにいくつかの使用法コードを追加できますか?あなたの投稿とブログの投稿を読んだ後でも、オブジェクトの複製にこれがどのように使用されることが意図されているのか、まだわかりません。
ブラッド、

3
これは本当に機能しますか?"if(name in dest)"-プロパティが既にdestに存在する場合にのみプロパティを変更します。それは否定されるべきです。
memical 2012年

8
Object.prototypeを変更することは禁止されているのではないですか?また、その記事のリンクは壊れています。
ダニエルシャファー2012年

記事のリンクを試してみましたが、私にとってはうまくいきました。多分それはあなたがそれを試したときネットワークブリップでした。
マイケルディロン

いくつかのコメントに基づいて、オブジェクトのプロトタイプに追加されないバリアントを含めるように回答を更新しました。
Shamasis Bhattacharya 2014

20
var obj2 = JSON.parse(JSON.stringify(obj1));

2
これは、この既存の回答ですでに示唆されており、繰り返すことはできません。
Shadow WizardはEar For Youである

@ShadowWizardこれらは異なるメソッドです。これは単純にjsonに変換されてオブジェクトに戻りますが、リンクされた回答はObject.keys()オブジェクトを反復処理するために使用されます
2013年

この答えは間違っています。JSON.stringifyは日付オブジェクトを受け取り、それを文字列に変換します。次に、解析後にそれを文字列として残し、オブジェクトの状態を変更して、日付の場合とは異なるオブジェクトを残します。
twboc

13

JQueryから拡張機能を使用できます。

var newClone= jQuery.extend({}, oldObject);  
var deepClone = jQuery.extend(true, {}, oldObject); 

Node.jsプラグインもあります:

https://github.com/shimondoodkin/nodejs-clone-extend

JQueryまたはプラグインなしでそれを行うには、こちらをお読みください。

http://my.opera.com/GreyWyvern/blog/show.dml/1725165


Node.jsプラグインは完璧です。ありがとう!
ジャスティン

すばらしい答えです。私は少し便利なモジュールが大好きです。
Chev


8

「自分でロール」したくない場合は、いくつかのNodeモジュールがあります。これはよく見えます:https : //www.npmjs.com/package/clone

循環参照を含むあらゆる種類のものを処理するように見えます。githubページから:

clone mastersは、オブジェクト、配列、日付オブジェクト、およびRegExオブジェクトを複製します。すべてが再帰的に複製されるため、たとえば、オブジェクトの配列に日付を複製できます。[...]循環参照?うん!


7

このコードも機能します。Object.create()メソッドは、指定されたプロトタイプオブジェクトとプロパティを使用して新しいオブジェクトを作成します。

var obj1 = {x:5, y:5};

var obj2 = Object.create(obj1);

obj2.x; //5
obj2.x = 6;
obj2.x; //6

obj1.x; //5

4
これは浅いコピーです
Radagast the Brown

6

NodeJSでオブジェクトを複製するシンプルで最速の方法は、Object.keys(obj)メソッドを使用することです

var a = {"a": "a11", "b": "avc"};
var b;

for(var keys = Object.keys(a), l = keys.length; l; --l)
{
   b[ keys[l-1] ] = a[ keys[l-1] ];
}
b.a = 0;

console.log("a: " + JSON.stringify(a)); // LOG: a: {"a":"a11","b":"avc"} 
console.log("b: " + JSON.stringify(b)); // LOG: b: {"a":0,"b":"avc"}

メソッドObject.keysにはJavaScript 1.8.5が必要です。nodeJS v0.4.11はこのメソッドをサポートしています

もちろん、ネストされたオブジェクトの場合、再帰的な関数を実装する必要があります


他の解決策は、ネイティブJSON(JavaScript 1.7で実装)を使用することですが、以前のJSONよりはるかに遅い(約10倍遅い)

var a = {"a": i, "b": i*i};
var b = JSON.parse(JSON.stringify(a));
b.a = 0;

5

のより直接的な移植を目的とするプロジェクトもGithubにありjQuery.extend()ます。

https://github.com/dreamerslab/node.extend

jQuery docsから変更された例:

var extend = require('node.extend');

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};

var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

var merged = extend(object1, object2);


4

Y'allは苦しんでいますが、解決策は簡単です。

var obj1 = {x: 5, y:5};

var obj2 = {...obj1}; //ブーム


3

真のクローンオプションを探して、私はridcullyのリンクを見つけました。

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

そのページのソリューションを変更して、Objectプロトタイプに接続された関数が列挙できないようにしました。これが私の結果です:

Object.defineProperty(Object.prototype, 'clone', {
    enumerable: false,
    value: function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (i in this) {
        if (i == 'clone') continue;
            if (this[i] && typeof this[i] == "object") {
                newObj[i] = this[i].clone();
            } else newObj[i] = this[i]
        } return newObj;
    }
});

うまくいけば、これは他の誰かにも役立ちます。いくつかの注意点があることに注意してください...特に「clone」という名前のプロパティに関して。これは私にとってはうまくいきます。私はそれを書いたとして、何の信用もしません。繰り返しますが、定義方法を変更しただけです。


これは間違っています。日付タイプはオブジェクトなので、このコードは日付を空のオブジェクトに置き換えます...これは使用しないでください。
jtblin 2013


0

あなたがコーヒースクリプトを使っているなら、それは次のように簡単です:

newObject = {}
newObject[key] = value  for own key,value of oldObject

これは深いクローンではありませんが。


0

どの回答も私を満足させませんでした。いくつかは機能しないか、浅いクローンです。@ clint-harrisからの回答とJSON.parse / stringifyの使用は良いですが、かなり遅いです。ディープクローニングを高速に行うモジュールを見つけました:https : //github.com/AlexeyKupershtokh/node-v8-clone


0

node.jsのオブジェクトの実際のクローン(ディープコピー)を実行する組み込みの方法はありません。いくつかのトリッキーなエッジケースがあるので、これには必ずライブラリを使用する必要があります。simpleooライブラリにそのような関数を書きました。deepCopyライブラリ(それは非常に小さい)から何も必要としない場合は、それを使用せずに関数を使用できます。この関数は、配列、日付、正規表現を含む複数のデータ型の複製をサポートし、再帰的な参照をサポートし、コンストラクター関数に必要なパラメーターを持つオブジェクトを操作します。

これがコードです:

//If Object.create isn't already defined, we just do the simple shim, without the second argument,
//since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy = function deepCopy(src, /* INTERNAL */ _visited) {
    if(src == null || typeof(src) !== 'object'){
        return src;
    }

    // Initialize the visited objects array if needed
    // This is used to detect cyclic references
    if (_visited == undefined){
        _visited = [];
    }
    // Ensure src has not already been visited
    else {
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If src was already visited, don't try to copy it, just return the reference
            if (src === _visited[i]) {
                return src;
            }
        }
    }

    // Add this object to the visited array
    _visited.push(src);

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice(0) would soft clone
        ret = src.slice();
        var i = ret.length;
        while (i--){
            ret[i] = deepCopy(ret[i], _visited);
        }
        return ret;
    }
    //Date
    if (src instanceof Date) {
        return new Date(src.getTime());
    }
    //RegExp
    if (src instanceof RegExp) {
        return new RegExp(src);
    }
    //DOM Element
    if (src.nodeType && typeof src.cloneNode == 'function') {
        return src.cloneNode(true);
    }

    //If we've reached here, we have a regular object, array, or function

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var ret = object_create(proto);

    for(var key in src){
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        ret[key] = deepCopy(src[key], _visited);
    }
    return ret;
};

0
npm install node-v8-clone

最速のクローン、それはnode.jsからネイティブのクローンメソッドを開きます

var clone = require('node-v8-clone').clone;
var newObj = clone(obj, true); //true - deep recursive clone

0

別の解決策は、次を使用して新しい変数に直接カプセル化することです。 obj1= {...obj2}


これは浅いコピーである
レミDoolaeghe

0

このクローンライブラリを使用して、オブジェクトをディープクローンすることもできます。

 npm install --save clone
const clone = require('clone');

const clonedObject = clone(sourceObject);

-2

オブジェクトのプロトタイプを作成し、オブジェクトを使用および変更するたびにオブジェクトインスタンスを呼び出すことができます。

function object () {
  this.x = 5;
  this.y = 5;
}
var obj1 = new object();
var obj2 = new object();
obj2.x = 6;
console.log(obj1.x); //logs 5

オブジェクトコンストラクターに引数を渡すこともできます

function object (x, y) {
   this.x = x;
   this.y = y;
}
var obj1 = new object(5, 5);
var obj2 = new object(6, 6);
console.log(obj1.x); //logs 5
console.log(obj2.x); //logs 6

これがお役に立てば幸いです。

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