JavaScriptでオブジェクトを深くクローンする最も効率的な方法は何ですか?



Answers

このベンチマークをチェックアウト: http://jsben.ch/#/bWfk9 : http://jsben.ch/#/bWfk9

スピードが主な関心事だった以前のテストでは、

JSON.parse(JSON.stringify(obj))

オブジェクトを深くクローンする最速の方法です(深いフ​​ラグをtrueに設定してjQuery.extendを10〜20% jQuery.extend )。

ディープフラグがfalse(浅いクローン)に設定されていると、jQuery.extendはかなり高速です。 型バリデーションのための余分なロジックが含まれており、未定義のプロパティなどをコピーしないため、これは良い選択ですが、これも少し遅くなります。

クローンを作成しようとしているオブジェクトの構造や深いネストされた配列を避けることができれば、hasOwnPropertyをチェックしながらオブジェクトをクローンするfor (var i in obj)の単純なfor (var i in obj)ループを書くことができ、jQueryよりはるかに高速です。

最後に、ホットループで既知のオブジェクト構造を複製しようとすると、クローン手順を単純にインライン化してオブジェクトを手作業で構築するだけで、より多くのパフォーマンスを得ることができます。

JavaScriptトレースエンジンは、 for..inループを最適化するfor..in吸い込み、hasOwnPropertyをチェックすることであなたの速度が低下します。 速度が絶対必要である場合、手動クローン。

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

DateオブジェクトのJSON.parse(JSON.stringify(obj))メソッドを使用することをJSON.parse(JSON.stringify(obj))くださいJSON.parse(JSON.stringify(obj)) JSON.stringify(new Date())は、 JSON.parse() DateオブジェクトにDateます。 詳細については、この回答を参照してください

さらに、少なくともChrome 65では、ネイティブクローニングは行かないことに注意してください。 このJSPerfによれば、新しい機能を作成してネイティブクローニングを実行することは、JSON.stringifyを使用するよりも800倍近く遅くなります。

Question

JavaScriptオブジェクトを複製する最も効率的な方法は何ですか? 私はobj = eval(uneval(o)); 使用されていますが、これは非標準でFirefoxのみでサポートされています。

私はobj = JSON.parse(JSON.stringify(o));ようなことをしましたobj = JSON.parse(JSON.stringify(o)); 効率について疑問を呈している。

私はまた、さまざまな欠陥を持つ再帰的なコピー機能を見てきました。
私は標準的な解決策が存在しないことに驚いている。




This isn't generally the most efficient solution, but it does what I need. Simple test cases below...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Cyclic array test...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Function test...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false



Only when you can use ECMAScript 6 or transpilers .

特徴:

  • Won't trigger getter/setter while copying.
  • Preserves getter/setter.
  • Preserves prototype informations.
  • Works with both object-literal and functional OO writing styles.

コード:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}



function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }



There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)



これは私が使用しているものです:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}



var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});



Lodash has a nice lodash.com/docs#cloneDeep method:

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

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



内蔵されていないものがあれば、試してみることができます:

    function clone(obj) {
      if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

      if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
      else
        var temp = obj.constructor();

      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          obj['isActiveClone'] = null;
          temp[key] = clone(obj[key]);
          delete obj['isActiveClone'];
        }
      }

      return temp;
    }




I disagree with the answer with the greatest votes here . A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.

And here's the function for quick reference:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}



1つのコード行でオブジェクトを複製する(ディープクローンではない)効率的な方法

Object.assignメソッドは、ECMAScript 2015(ES6)標準の一部であり、必要なものを正確に実行します。

var clone = Object.assign({}, obj);

Object.assign()メソッドは、1つ以上のソースオブジェクトからすべての列挙可能な独自のプロパティの値をターゲットオブジェクトにコピーするために使用されます。

Object.assign

古いブラウザをサポートするためのpolyfill

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}



オブジェクトのCloningは、JSでは常に問題でしたが、ES6以前では、以下のJavaScriptでオブジェクトをコピーするさまざまな方法を挙げていました。下のオブジェクトがあり、それを深くコピーしたいと考えています。

var obj = {a:1, b:2, c:3, d:4};

原点を変更することなく、このオブジェクトをコピーする方法はいくつかあります。

1)ES5 +、単純な関数を使用してコピーを作成する:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2)ES5 +、JSON.parseとJSON.stringifyを使用します。

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3)AngularJs:

var  deepCopyObj = angular.copy(obj);

4)jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5)UnderscoreJs&Loadash:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

これらのお役に立てば幸いです...




The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));



これは非常にうまくいくライブラリ(「クローン」と呼ばれます)があります。 私が知っている任意のオブジェクトの最も完全な再帰的クローニング/コピーを提供します。 また、循環参照もサポートしていますが、これは他の回答ではカバーされていません。

あなたはnpmでもそれを見つけることができます 。 これはブラウザやNode.jsでも使用できます。

それを使用する方法の例はここにあります:

それをインストールする

npm install clone

Enderパッケージ化してください。

ender build clone [...]

また、ソースコードを手動でダウンロードすることもできます。

その後、ソースコードで使用することができます。

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(免責事項:私は図書館の著者です。)




I usually use var newObj = JSON.parse( JSON.stringify(oldObje) ); but, here's a more proper way:

var o = {};

var oo = Object.create(o);

(o === oo); // => false

Watch legacy browsers!




Links