Nodejs : 객체를 복제하는 방법


91

어레이를 복제하면 cloneArr = arr.slice()

nodejs에서 객체를 복제하는 방법을 알고 싶습니다.

답변:


178

성능을 떨어 뜨릴 필요가없는 유틸리티 및 클래스의 경우, 필자는 종종 속임수를 쓰며 JSON을 사용하여 깊은 복사를 수행합니다.

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

이것은 유일한 대답이나 가장 우아한 대답이 아닙니다. 프로덕션 병목 현상에 대해서는 다른 모든 답변을 고려해야합니다. 그러나 이것은 빠르고 더러운 솔루션이며 매우 효과적이며 간단한 속성 해시를 복제하는 대부분의 상황에서 유용합니다.


2
@djechlin 물론입니다. 시도해보십시오 : jsfiddle.net/katowulf/E5jC3 (노드 0.10.11로 테스트 됨) 함수 나 프로토 타입 데이터를 재구성 할 수는 없지만 값은 괜찮습니다.
Kato

13
이것은 날짜를 문자열로 변환합니다
backus

4
@Backus뿐만 아니라 개체 및 기능.
Kato

2
@Kato, 왜 순환 참조는 깊은 사본을 만들 필요가 없다는 것을 의미합니까? 서로 관련이없는 두 가지입니다. 순환 참조를 지원하고 복제를 위해 JSON을 사용하지 않는 NodeJS에 대한 좋은 복제 방법을 작성하는 것은 확실히 가능합니다. github.com/pvorb/node-clone
사무엘 네프

2
댓글과 설명에 이미 언급되어 있습니다. 함수, 객체 프로토 타입 또는 일반 복제 방법으로 수행해서는 안되는 기타 작업을 복제 할 수 없습니다. 이를 위해서는 한 줄 이상의 코드가 필요하며 어쨌든 그 쓰레기를 복제해서는 안됩니다. 클래스에 내부를 처리하고 수행하는 방법을 알고있는 복제 메서드를 추가하십시오newObj = obj.clone(args...);
Kato

35

Object.assign은 위의 답변에서 언급되지 않았습니다.

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

ES6를 사용하는 경우 스프레드 연산자를 사용할 수 있습니다.

let cloned = { ... source };

참조 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


29
이 두 가지 모두 얕은 복제본이며 객체의 확산 연산자에는 node.js 8 이상이 필요합니다.
jlh

1
@jlh가 언급했듯이 이것은 중첩 된 개체에 대해서는 작동하지 않습니다. 이 방법의 딥 클론 문제에 대한 자세한 내용은 다음에서 확인할 수 있습니다. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Shnd

Shnd가 말했듯이이 대답은 올바르지 않습니다. 딥 클로닝의 경우 Object.assign ()이 속성 값을 복사하기 때문에 다른 대안을 사용해야합니다. 원본 값이 개체에 대한 참조 인 경우 해당 참조 값만 복사합니다.
트로이

1
이것은하지 클론, 그냥 의미, 원래의 객체를 참조 않습니다 당신은 마스터로 이동합니다 뭔가 변경하는 경우
에이단 웰치

@AidanWelch 나는 그것이 정확하다고 생각하지 않습니다. let a = {x:1}; let b = Object.assign({}, a); a.x = 2; a.y = 1; console.log(a, b);
Miguel Pynto

20

"자신의 롤링"을 원하지 않는 경우 일부 노드 모듈이 있습니다. 이것은 좋아 보인다 : https://www.npmjs.com/package/clone

순환 참조를 포함하여 모든 종류의 항목을 처리하는 것처럼 보입니다. 로부터 github의의 페이지 :

복제 마스터는 개체, 배열, 날짜 개체 및 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
객체가 .clone메소드 자체 를 구현해야 함을 언급하는 +1 . 복제 개체를 처리하는 가장 좋은 방법입니다.
Raynos

3
if (x.clone)해야if (typeof x.clone === 'function')
Yanick 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 } }

빈 객체 {}와 함께 첫 번째 매개 변수를 사용하는 것에 유의하십시오. 이것은 복사 된 객체를 새 객체로 복사해야한다는 것을 extend에게 알려줍니다. 기존 객체를 첫 번째 매개 변수로 사용하는 경우 두 번째 (및 모든 후속) 매개 변수가 첫 번째 매개 변수 변수에 대해 딥 병합 복사됩니다.

위의 예제 변수를 사용하여 다음을 수행 할 수도 있습니다.

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

복제 된 객체로 수행하려는 작업에 따라 자바 스크립트의 프로토 타입 상속 메커니즘을 활용하고 다음을 통해 다소 복제 된 객체를 얻을 수 있습니다.

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

@naomik : 저는 JS를 처음 접했으므로 정밀도를 원합니다. olli-k가 제안하고 좋아하는 얕은 클론은 voxpelli가 제안한 솔루션과 동일한 제한이있는 것 같습니다 (IMHO,하지만 제가 틀렸을 수 있습니다). 원본 및 얕은 닫기도 동일한 "항목"을 공유하므로 원본의 항목을 업데이트하면 얕은 복제본에도 영향을줍니다. 또는?
bmorin

@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 메서드 실행 명령을 사용하여 참조가 전달되지 않도록 개체를 딥 클론 할 수 있습니다.

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.