Nodejs:オブジェクトを複製する方法


90

アレイのクローンを作成する場合は、 cloneArr = arr.slice()

nodejsでオブジェクトを複製する方法を知りたいです。

回答:


175

パフォーマンスのすべての低下を絞る必要がないユーティリティとクラスの場合、私はよくチートし、JSONを使用してディープコピーを実行します。

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}

これが唯一の答えや最もエレガントな答えではありません。他のすべての回答は、本番のボトルネックについて検討する必要があります。ただし、これは迅速かつダーティなソリューションであり、非常に効果的で、プロパティの単純なハッシュを複製するほとんどの状況で役立ちます。


2
@djechlinもちろんです。試してみてください:jsfiddle.net/katowulf/E5jC3(ノード0.10.11でテスト済み)関数やプロトタイプデータを再構成することはできませんが、適切な値を取得します。
加藤

11
これは日付を文字列に変換します
backus '28

4
@Backusだけでなく、オブジェクトと関数。
加藤

2
@加藤、循環参照はなぜ深いコピーを作成する必要がないことを意味するのですか?それらは2つの無関係なものです。循環参照をサポートし、JSONを使用して複製しないNodeJSの適切な複製メソッドを作成することは確かに可能です。github.com/pvorb/node-clone
Samuel Neff

2
コメントと説明にすでに記載されています。関数、オブジェクトプロトタイプ、または一般的なcloneメソッドで実行してはならないその他のことは複製できません。そのためには複数行のコードが必要であり、とにかくそのジャンクのクローンを作成するべきではありません。内部での処理方法を知っているクラスにcloneメソッドを配置してくださいnewObj = obj.clone(args...);
Kato

34

Object.assignは、上記の回答のいずれにも記載されていません。

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

ES6を使用している場合は、spread演算子を使用できます。

let cloned = { ... source };

リファレンス:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


28
これらはどちらも浅いクローンであり、オブジェクトのスプレッドオペレーターにはnode.js 8以降が必要です。
jlh 2017年

1
@jlhが言及したように、これはネストされたオブジェクトに対しては機能しません。このメソッドの詳細なクローンの問題の詳細については、developer.mozilla.org
en

Shndが言ったように、この答えは正しくありません。ディープクローニングでは、Object.assign()がプロパティ値をコピーするため、他の代替手段を使用する必要があります。ソース値がオブジェクトへの参照である場合、その参照値のみがコピーされます。
トロイ

1
これはクローンではなく、元のオブジェクトを参照するだけです。つまり、何かを変更すると、マスターに
Aidan Welch

@AidanWelch私はそれが正しいとは思いません。let a = {x:1}; let b = Object.assign({}, a); a.x = 2; a.y = 1; console.log(a, b);
ミゲルピント

20

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

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

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


10

再帰的に複製するものとコピーするだけのものは特定のオブジェクトがどのように機能するかに依存しているため、一般的ですが有用な複製操作を行うのは困難です。

役に立つかもしれないものは

function clone(x)
{
    if (x === null || x === undefined)
        return x;
    if (typeof x.clone === "function")
        return x.clone();
    if (x.constructor == Array)
    {
        var r = [];
        for (var i=0,n=x.length; i<n; i++)
            r.push(clone(x[i]));
        return r;
    }
    return x;
}

このコードでは、ロジックは

  • の場合、nullまたはundefined同じものを返す場合(cloneメソッドが存在するかどうかを確認するのはエラーなので、特別なケースが必要です)
  • オブジェクトにはcloneメソッドがありますか?それを使う
  • オブジェクトは配列ですか?その後、再帰的なクローン操作を行います
  • それ以外の場合は、同じ値を返します

このクローン機能により、カスタムクローンメソッドを簡単に実装できます...たとえば

function Point(x, y)
{
    this.x = x;
    this.y = y;

    ...
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
};



function Polygon(points, style)
{
    this.points = points;
    this.style = style;

    ...
}

Polygon.prototype.clone = function()
{
    return new Polygon(clone(this.points),
                       this.style);
};

オブジェクト内で特定の配列の正しい複製操作が浅いコピーであることがわかっている場合は、values.slice()ではなくを呼び出すことができますclone(values)

たとえば、上記のコードでは、ポリゴンオブジェクトのクローンがポイントをクローンするが、同じスタイルオブジェクトを共有することを明示的に要求しています。代わりにスタイルオブジェクトも複製する場合は、単にを渡すことができclone(this.style)ます。


1
+1は、オブジェクト.clone自体がメソッドを実装する必要があることを説明します。これは、オブジェクトのクローンを処理するための最良の方法です。
レイノス

3
if (x.clone)する必要がありますif (typeof x.clone === 'function')
ヤニックRochon

@YanickRochon:ありがとう、修正されました。すみません、どういうわけか私は前にこのコメントに気づきませんでした...
6502

9

オブジェクトのクローンを作成するネイティブメソッドはありません。アンダースコア_.cloneは、浅いクローンです。

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

スライスするか拡張します。

ここに _.extend

// extend the obj (first parameter)
_.extend = function(obj) {
  // for each other parameter
  each(slice.call(arguments, 1), function(source) {
    // loop through all properties of the other objects
    for (var prop in source) {
      // if the property is not undefined then add it to the object.
      if (source[prop] !== void 0) obj[prop] = source[prop];
    }
  });
  // return the object (first parameter)
  return obj;
};

Extendは単にすべてのアイテムを反復処理し、アイテムを含む新しいオブジェクトを作成します。

必要に応じて、独自の単純な実装を展開できます

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}

クロージャーは複製できないため、ディープクローニングを回避する十分な理由があります。

私は個人的に質問しましたが、deep cloning objects before私が結論に達したのは、あなたがそれをしないことです。

私の推奨は使用でunderscoreあり、それ_.cloneは浅いクローンのための方法です


9

浅いコピーの場合は、次のように(通常はモジュールなどで)reduceパターンを使用します。

var newObject = Object.keys(original).reduce(function (obj, item) {
    obj[item] = original[item];
    return obj;
},{});

ここではオプションのカップルのためのjsperfだ:http://jsperf.com/shallow-copying


シンプル、エレガント、華麗。私はあなたの他のSOの貢献を楽しみにしています^ _ ^
ありがとう

7

古い質問ですが、これまでに提案されたものよりもエレガントな答えがあります。組み込みのutils._extendを使用します。

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

var varToCopy = { test: 12345, nested: { val: 6789 } };

var copiedObject = extend({}, varToCopy);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 } }

空のオブジェクト{}での最初のパラメーターの使用に注意してください-これは、コピーされたオブジェクトを新しいオブジェクトにコピーする必要があることを拡張に伝えます。既存のオブジェクトを最初のパラメーターとして使用する場合、2番目(およびそれ以降)のパラメーターは、最初のパラメーター変数にディープマージコピーされます。

上記のサンプル変数を使用して、これを行うこともできます。

var anotherMergeVar = { foo: "bar" };

extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }

非常に便利なユーティリティです。特に、AngularJSとjQueryでの拡張に慣れている場合は特に便利です。

これが他の誰かを助けることを願っています。オブジェクト参照の上書きは悲惨であり、これは毎回それを解決します!


1
_extendの使用は廃止されました。使用Object.assign()の代わりに:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
MacRomanの

7

lodashも使用できます。それは持っているクローンcloneDeep方法を。

var _= require('lodash');

var objects = [{ 'a': 1 }, { 'b': 2 }];

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);

1

クローンオブジェクトをどのように処理するかに応じて、JavaScriptのプロトタイプ継承メカニズムを利用して、次のようにして、多少クローンオブジェクトを作成できます。

var clonedObject = Object.create(originalObject);

これは完全なクローンではないことを覚えておいてください-良くも悪くも。

これの良い点は、実際にはオブジェクトを複製していないため、メモリフットプリントが低くなることです。

このメソッドについて覚えておく必要があるいくつかのトリッキーなことは、プロトタイプチェーンで定義されたプロパティの反復が少し異なる場合があることと、元のオブジェクトへの変更は、そのプロパティがそれ自体にも設定されていない限り、クローンオブジェクトにも影響するということです。


3
これはとんでもない危険であり、まったくクローンとは異なります。var a = {foo: "bar"}, b = Object.create(a); a.foo = "broken"; console.log(b.foo); // "broken";
あなたに感謝

@naomik:b.foo = "working"; console.log(a.foo); // still "broken";もちろん、元のオブジェクトの変更は「クローン」に反映され、「クローン」の変更は元のオブジェクトに反映されないことに注意する必要があります–しかし、私はそれを危険だとは思いません–それはすべて、クローンで何をしたいかに依存します
VoxPelli 2014年

@naomik:私はJSにかなり慣れていないので、いくつかの精度を教えてください。olli-kによって提案され、あなたが気に入った浅いクローン(vospelliによって提案されたソリューションと同じ制限があるように見えます(私見ですが、私は間違っているかもしれません)。オリジナルと浅いクローズも同じ「アイテム」を共有し、オリジナルのアイテムを更新すると浅いクローンにも影響します。それとも?
bmorin 2015年

@ bmornin、Olli Kの手法を使用する場合、元のオブジェクトを変更しても新しいオブジェクトは変更されません
ありがとう

1
@VoxPelli「クローン」の目的はすべて、変更を元のオブジェクトから分離することであるため、この方法の方危険です。
Doug

1

完全なディープコピーを実装しました。私はそれが一般的なクローンメソッドに最適だと思いますが、循環参照を処理しません。

使用例:

parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;

obj2 = copy(obj)

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3

parent.prop_chain=4
obj2.a = 15

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4

コード自体:

このコードは、オブジェクトをプロトタイプとともにコピーします。また、関数もコピーします(誰かに役立つ場合があります)。

function copy(obj) {
  // (F.prototype will hold the object prototype chain)
  function F() {}
  var newObj;

  if(typeof obj.clone === 'function')
    return obj.clone()

  // To copy something that is not an object, just return it:
  if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
    return obj;

  if(typeof obj === 'object') {    
    // Copy the prototype:
    newObj = {}
    var proto = Object.getPrototypeOf(obj)
    Object.setPrototypeOf(newObj, proto)
  } else {
    // If the object is a function the function evaluate it:
    var aux
    newObj = eval('aux='+obj.toString())
    // And copy the prototype:
    newObj.prototype = obj.prototype
  }

  // Copy the object normal properties with a deep copy:
  for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
      if(typeof obj[i] !== 'object')
        newObj[i] = obj[i]
      else
        newObj[i] = copy(obj[i])
    }
  }

  return newObj;
}

このコピーでは、元の構造とコピーされたものの違いを見つけることができません。ただし、元の構造でクロージャーが使用されている場合を除きます。そのため、適切な実装だと思います。

それが役に立てば幸い


1

配列の場合、使用できます

var arr = [1,2,3]; 
var arr_2 = arr ; 

print ( arr_2 ); 

arr = arr.slice(0);

print ( arr ); 

arr[1]=9999; 

print ( arr_2 ); 

1

JavaScriptのオブジェクトと配列は参照渡しを使用します。コピーされた値を更新すると、元のオブジェクトに反映される可能性があります。これを防ぐには、lodashライブラリのcloneDeepメソッドのrunコマンドを使用して、オブジェクトをディープクローンし、参照が渡されないようにします。

npmインストールlodash

const ld = require('lodash')
const objectToCopy = {name: "john", age: 24}
const clonedObject = ld.cloneDeep(objectToCopy)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.