JavaScript에서 객체를 깊이 복제하는 가장 효율적인 방법은 무엇입니까?



14 Answers

이 벤치 마크를 확인하십시오 : http://jsben.ch/#/bWfk9

속도가 주요 관심사였던 이전 테스트에서

JSON.parse(JSON.stringify(obj))

오브젝트를 깊게 복제하는 가장 빠른 방법입니다 ( jQuery.extend 와 deep 플래그가 10-20 %로 설정 됨).

deep 플래그가 false (얕은 복제)로 설정된 경우 jQuery.extend는 매우 빠릅니다. 이것은 타입 검증을위한 약간의 로직을 포함하고 정의되지 않은 프로퍼티 등을 복사하지 않기 때문에 좋은 옵션입니다.하지만 이것 역시 조금 느려질 것입니다.

복제하려는 객체의 구조를 알고 있거나 중첩 된 배열을 피할 수있는 경우 hasOwnProperty를 검사하는 동안 객체를 복제하기 for (var i in obj) 루프 for (var i in obj) 간단한 객체를 작성하면 jQuery보다 훨씬 빠릅니다.

마지막으로 핫 루프에서 알려진 오브젝트 구조를 복제하려고하면 복제 절차를 간단히 작성하고 수동으로 오브젝트를 구성하여 훨씬 더 많은 성능을 얻을 수 있습니다.

JavaScript 추적 엔진은 ~ for..in 루프를 최적화하고 hasOwnProperty를 검사하면 속도가 느려집니다. 속도가 절대적 일 때 수동 복제.

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

Date 객체에 JSON.parse(JSON.stringify(obj)) 메서드 사용주의 - JSON.stringify(new Date())JSON.parse() 변환 하지 않는 ISO 형식의 날짜 문자열 표현을 반환합니다. 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)); 그러나 효율성에 의문을 제기하십시오.

또한 여러 가지 결함이있는 재귀 복사 기능을 보았습니다.
나는 정식 해결책이 존재하지 않는다는 것에 놀랐다.




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});



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' } }

(면책 조항 : 저는 도서관의 저자입니다.)




JS에서 오브젝트를 Cloning 하는 것은 항상 중요한 문제 였지만, 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) JS5.parse 및 JSON.stringify를 사용하여 ES5 +.

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

희망이 도움이 ...




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)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }



내장 된 것이 없다면 시도해 볼 수 있습니다 :

    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;
    }




Only when you can use ECMAScript 6 or transpilers .

Features:

  • 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;
}



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



이것이 제가 사용하고있는 것입니다 :

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;
}



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)



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!




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
}



한 줄의 코드에서 객체를 복제하는 (효율적인 복제가 아닌) 효율적인 방법

Object.assign 메서드는 ECMAScript 2015 (ES6) 표준의 일부이며 필요한 작업을 정확하게 수행합니다.

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

Object.assign () 메서드는 열거 가능한 모든 속성의 값을 하나 이상의 소스 객체에서 대상 객체로 복사하는 데 사용됩니다.

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;
    }
  });
}


Related