JavaScript 객체를 올바르게 복제하려면 어떻게합니까?


3076

나는 물건이있다 x. y변경 사항이 y수정되지 않도록 객체로 복사하고 싶습니다 x. 내장 JavaScript 객체에서 파생 된 객체를 복사하면 원치 않는 속성이 추가로 생성됩니다. 나는 문자 그대로 구성된 객체 중 하나를 복사하기 때문에 문제가되지 않습니다.

JavaScript 객체를 올바르게 복제하려면 어떻게합니까?


30
이 질문을보십시오 : stackoverflow.com/questions/122102/…
Niyaz

256
JSON, 나는 사용mObj=JSON.parse(JSON.stringify(jsonObject));
주님 Loh에 있습니다.

67
나는 아무도 제안하지 않는 이유를 정말로 얻지 못합니다 Object.create(o). 저자가 요구하는 모든 것을합니까?
froginvasion

43
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2; 이 작업을 수행 한 후, y.deep.key또한이 될 것입니다, 따라서 Object.create CAN은 ... 복제에 사용할 수 없습니다
루벤 Stolk

17
@ r3wt 작동하지 않습니다 ... 솔루션의 기본 테스트를 수행 한 후에 만 ​​게시하십시오.
akshay

답변:


1561

JavaScript의 객체에 대해이 작업을 수행하는 것은 간단하거나 간단하지 않습니다. 프로토 타입에 남겨두고 새 인스턴스로 복사하지 않아야하는 객체의 프로토 타입에서 속성을 잘못 선택하는 문제가 발생합니다. 예를 들어에 대한 clone메소드를 추가하는 경우 Object.prototype일부 답변에서 볼 수 있듯이 해당 속성을 명시 적으로 건너 뛰어야합니다. 그러나 모르는 다른 추가 방법 Object.prototype이나 다른 중간 프로토 타입에 추가 된 방법 은 무엇입니까? 이 경우, 사용하지 말아야 할 속성을 복사하므로 hasOwnProperty메소드 를 사용하여 예상치 못한 로컬이 아닌 속성을 감지해야합니다 .

열거 할 수없는 속성 외에도 숨겨진 속성이있는 객체를 복사하려고하면 더 어려운 문제가 발생합니다. 예를 들어, prototype함수의 숨겨진 속성입니다. 또한 객체의 프로토 타입은 속성으로 참조되며이 속성 __proto__도 숨겨져 있으며 소스 객체의 속성을 반복하는 for / in 루프에 의해 복사되지 않습니다. __proto__Firefox의 JavaScript 인터프리터에만 해당되는 것으로 생각 되며 다른 브라우저에서는 다를 수 있지만 그림이 나타납니다. 모든 것이 열거 가능한 것은 아닙니다. 숨겨진 속성의 이름을 알고 있으면 복사 할 수 있지만 자동으로 검색하는 방법은 모르겠습니다.

우아한 솔루션을 찾는 또 다른 걸림돌은 프로토 타입 상속을 올바르게 설정하는 문제입니다. 소스 객체의 프로토 타입이 Object인 경우 간단히 새 일반 객체를 만드는 것이 {}작동하지만 소스의 프로토 타입이 일부 자손 인 Object경우 해당 프로토 타입에서 hasOwnProperty필터를 사용하여 건너 뛴 추가 멤버가 누락 되거나 프로토 타입에 있었지만 처음에는 열거 할 수 없었습니다. 한 가지 해결 방법은 소스 객체의 constructor속성 을 호출하여 초기 복사 객체를 가져온 다음 속성을 복사하는 것이지만 열거 할 수없는 속성은 얻지 못할 수 있습니다. 예를 들어 Date객체는 데이터를 숨겨진 멤버로 저장합니다.

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

의 날짜 문자열은의 날짜 문자열 d1보다 5 초 뒤에 있습니다 d2. 하나 Date를 다른 것과 동일 하게 만드는 setTime방법 은 메소드 를 호출하는 것이지만 Date클래스 에 따라 다릅니다 . 나는이 문제에 대한 방탄적인 일반적인 해결책이 없다고 생각하지만 잘못되어 기쁠 것입니다!

나는 내가 단지 일반 복사 할 필요가 있음을 가정하여 손상 결국 복사 일반적으로 깊은을 구현했다 때 Object, Array, Date, String, Number, 또는 Boolean. 마지막 3 가지 유형은 변경할 수 없으므로 얕은 복사를 수행하고 변경에 대해 걱정할 필요가 없습니다. 또한 그 목록에 포함 Object되거나 Array그 목록에있는 6 가지 간단한 유형 중 하나 일 것이라고 가정했습니다 . 이것은 다음과 같은 코드로 수행 할 수 있습니다.

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

위의 함수는 객체와 배열의 데이터가 트리 구조를 형성하는 한 위에서 언급 한 6 가지 간단한 유형에 적절하게 작동합니다. 즉, 객체에 동일한 데이터에 대한 참조가 두 개 이상 없습니다. 예를 들면 다음과 같습니다.

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

JavaScript 객체를 처리 할 수는 없지만 던질 때만 작동한다고 가정하지 않는 한 많은 목적으로 충분할 수 있습니다.


5
거의 nodejs에서 잘 작동했습니다. (var i = 0, var len = obj.length; i <len; ++ i) {의 행을 (var i = 0; i <obj.length; ++ i) {
Trindaz

5
미래의 구글 직원 : gist.github.com/2234277
Trindaz

7
오늘날 JSON.parse(JSON.stringify([some object]),[some revirer function])해결책이 되겠습니까?
KooiInc

5
첫 번째 스 니펫에서는 그렇지 않아야 var cpy = new obj.constructor()합니까?
cyon

8
객체 할당은 Chrome에서 실제 사본을 만들지 않는 것 같습니다. 원본 객체에 대한 참조를 유지합니다. JSON.stringify 및 JSON.parse를 사용하여 복제했습니다.
1owk3y

974

Date객체 내에서 s, 함수, undefined, regExp 또는 Infinity를 사용하지 않는 경우 매우 간단한 라이너는 JSON.parse(JSON.stringify(object))다음과 같습니다.

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

이것은 객체, 배열, 문자열, 부울 및 숫자를 포함하는 모든 종류의 객체에 적용됩니다.

작업자와 메시지를 게시 할 때 사용되는 브라우저의 구조화 된 클론 알고리즘에 대한이 기사를 참조하십시오 . 또한 심층 복제 기능도 포함합니다.


42
이것은 테스트에만 사용할 수 있습니다. 첫째, 시간과 메모리 소비 측면에서 최적과는 거리가 멀다. 둘째, 모든 브라우저에이 방법이있는 것은 아닙니다.
Nux

2
항상 JSON2.js 또는 JSON3.js를 포함 할 수 있습니다. 어쨌든 앱에 필요합니다. 그러나 JSON.stringify에는 상속 된 속성이 포함되어 있지 않으므로 이것이 최선의 해결책이 아닐 수도 있습니다.
Tim Hong

78
@Nux, 왜 시간과 메모리 측면에서 최적이 아닌가? MiJyn은 다음과 같이 말합니다. "이 방법이 얕은 복사 (깊은 개체에서)보다 느린 이유는이 방법이 정의에 따라 깊은 복사본이기 때문입니다. 그러나 JSON은 대부분의 브라우저에서 기본 코드로 구현되므로 상당히 빠릅니다. 다른 자바 스크립트 기반의 딥 카피 솔루션을 사용하는 것보다, 때로는 자바 스크립트 기반의 얕은 카피 기술보다 빠를 수도 있습니다 (jsperf.com/cloning-an-object/79 참조). " stackoverflow.com/questions/122102/…
BeauCielBleu

16
2014 년 10 월 업데이트를 추가하고 싶습니다. JSON.parse (JSON.stringify (oldObject)); 이것을 사용하면 Javascript 엔진이 원하는 경우 더 나은 것을보고 최적화하는 것이 매우 쉽다는 이점이 있습니다.
mirhagk

17
2016 업데이트 : 이것은 현재 널리 사용되는 거의 모든 브라우저에서 작동해야합니다. ( 사용할 수 있습니까 ... 참조 ) 이제 주요 질문은 성능이 충분한 지 여부입니다.
James Foster

783

jQuery를 사용하면 extend로 얕은 복사 를 할 수 있습니다 .

var copiedObject = jQuery.extend({}, originalObject)

에 대한 후속 변경 사항 copiedObject은에 영향을 미치지 않으며 originalObject그 반대도 마찬가지입니다.

또는 깊은 사본 을 만들려면 :

var copiedObject = jQuery.extend(true, {}, originalObject)

164
심지어 :var copiedObject = jQuery.extend({},originalObject);
그랜트 맥클린

82
딥 카피의 첫 번째 매개 변수로 true를 지정하는 것도 유용합니다.jQuery.extend(true, {}, originalObject);
Will Shaver

6
예,이 링크가 도움이 되었음 (파스칼과 동일한 솔루션) stackoverflow.com/questions/122102/…
Garry English

3
참고로, 이것은 원본 객체 의 프로토 생성자를 복사하지 않습니다
Sam Jones

1
stackoverflow.com/questions/184710/… 에 따르면 , "얕은 복사"는 originalObject의 참조를 복사하는 것 같습니다 ...subsequent changes to the copiedObject will not affect the originalObject, and vice versa.... 정말 혼란스러워서 죄송합니다.
Carr

686

ECMAScript 6에는 열거 가능한 모든 자체 속성 값을 한 객체에서 다른 객체로 복사하는 Object.assign 메소드 가 있습니다. 예를 들면 다음과 같습니다.

var x = {myProp: "value"};
var y = Object.assign({}, x); 

그러나 중첩 된 개체는 여전히 참조로 복사됩니다.


그래, 나는 그것이 Object.assign갈 길이 라고 믿는다 . 폴리 필하기도 쉽습니다 : gist.github.com/rafaelrinaldi/43813e707970bd2d77fa

22
(이 이후 또한이 객체 리터럴을 통해 정의 된 "방법"을 통해 복사된다는 점에 유의 있습니다 만 열거) 하지 은 "클래스"메커니즘을 통해 무시 방법 (이 이후 없습니다 열거).
Marcus Junius Brutus

16
나는 이것이 Edge를 제외하고 IE에 의해 지원되지 않는다고 언급해야한다고 생각합니다. 어떤 사람들은 여전히 ​​이것을 사용합니다.
Saulius

1
이것은 @EugeneTiurin의 대답과 동일합니다.
Wilt

5
여기서 경험으로 말하면 ... 이것을 사용하지 마십시오. 객체는 계층 구조로 설계되었으며 첫 번째 레벨 만 복사하여 전체 객체가 복사되도록 지정합니다. 날 믿으면 머리가 긁히지 않을 수 있습니다.
ow3n

232

MDN 당 :

  • 얕은 복사를 원한다면 Object.assign({}, a)
  • "딥"복사의 경우 JSON.parse(JSON.stringify(a))

외부 라이브러리는 필요하지 않지만 먼저 브라우저 호환성 을 확인해야합니다 .


8
JSON.parse (JSON.stringify (a))는 아름답게 보이지만 사용하기 전에 원하는 컬렉션에 걸리는 시간을 벤치마킹하는 것이 좋습니다. 객체 크기에 따라 가장 빠른 옵션이 아닐 수도 있습니다.
Edza

3
JSON 메서드는 날짜 객체를 문자열로 변환하지만 다시 날짜로 변환하지 않는 것으로 나타났습니다. 자바 스크립트에서 시간대의 재미를 처리하고 수동으로 날짜를 수정해야합니다. 날짜 외에 다른 유형의 유사 사례가 될 수 있음
스티브 시거

Date의 경우 clone기능 이 있기 때문에 moment.js를 사용 합니다. 자세한 내용은 here here momentjs.com/docs/#/parsing/moment-clone
Tareq

이것은 좋지만 객체가 내부에 기능을 가지고 있는지주의하십시오
lesolorzanov

json 파싱의 또 다른 문제는 순환 참조입니다. 객체 내부에 순환 참조가있는 경우이 오류가 발생합니다
philoj

133

많은 답변이 있지만 ECMAScript 5의 Object.create 에 대한 언급 은 없지만 정확한 사본을 제공하지는 않지만 소스를 새 객체의 프로토 타입으로 설정합니다.

따라서 이것은 질문에 대한 정확한 대답은 아니지만 단선 솔루션이므로 우아합니다. 그리고 두 가지 경우에 가장 효과적입니다.

  1. 그러한 상속이 유용한 곳 (duh!)
  2. 소스 객체가 수정되지 않는 경우 두 객체 간의 관계가 문제가되지 않습니다.

예:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

왜이 솔루션이 우수하다고 생각합니까? 그것은 네이티브이므로 반복도없고 재귀도 없습니다. 그러나 오래된 브라우저에는 폴리 필이 필요합니다.


참고 : Object.create는 딥 카피가 아닙니다 (재귀를 언급하지 않았습니다). 증명 :var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
zamnuts

103
이것은 복제가 아닌 프로토 타입 상속입니다. 이것들은 완전히 다른 것입니다. 새 객체에는 자체 속성이 없으며 프로토 타입의 속성 만 가리 킵니다. 복제의 요점은 다른 객체의 속성을 참조하지 않는 새로운 객체를 새로 만드는 것입니다.
d13

7
나는 당신에게 완전히 동의합니다. 나는 이것이 의도 된대로 복제되지 않는다는 것에 동의합니다. 그러나 표준화되지 않은 모호한 솔루션을 찾는 대신 JavaScript의 본질을 받아들이십시오. 물론 프로토 타입이 마음에 들지 않으며 프로토 타입이 모두 "blah"이지만 실제로 수행중인 작업을 알고 있으면 매우 유용합니다.
froginvasion

4
@RobG :이 기사는 참조와 복제의 차이점을 설명합니다 : en.wikipedia.org/wiki/Cloning_(programming) . Object.create부모의 속성 값이 변경되면 자식의 속성도 변경됨을 의미합니다. 중첩 배열 및 객체에 대한 놀라운 부작용이 있는데,이를 모르는 경우 코드에서 찾기 어려운 버그로 이어질 수 있습니다 : jsbin.com/EKivInO/2 . 복제 된 객체는 부모와 동일한 속성과 값을 갖지만 부모와 연결되지 않은 완전히 새로운 독립 객체입니다.
d13

1
이것은 사람들을 오도합니다 ... Object.create ()는 상속의 수단으로 사용될 수 있지만 복제는 그 근처에 없습니다.
prajnavantha

128

한 줄의 코드로 Javascript 객체를 복제하는 우아한 방법

Object.assign방법은 ECMAScript를 2015 년 (ES6) 표준의 일부이며 정확하게 당신이 필요로한다.

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

Object.assign () 메소드는 열거 가능한 모든 고유 특성의 값을 하나 이상의 소스 오브젝트에서 대상 오브젝트로 복사하는 데 사용됩니다.

더 읽어보기 ...

구형 브라우저를 지원 하는 폴리 필 :

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

바보 같은 질문에 대해 죄송하지만 polyfill Object.assignvalue함수가 하나의 매개 변수 만 사용할 때 두 개의 매개 변수를 사용하는 이유는 무엇입니까?
Qwertie

@Qwertie 어제는 모든 인수는 마지막 통과 인수에서 속성의 우선 순위, 반복과 하나의 객체로 병합
유진 Tiurin

오, 감사합니다 ( arguments이전 에 객체에 익숙하지 않았습니다 .) Object()Google 을 통해 찾는 데 어려움을 겪고 있습니다 ... 유형입니다. 그렇지 않습니까?
Qwertie 2016 년

44
이것은 얕은 "복제"만 수행합니다
Marcus Junius Brutus

1
이 답변은 다음과 동일합니다. stackoverflow.com/questions/122102/… 같은 사람이 알고 있지만 답변을 복사하는 대신 참조해야합니다.
lesolorzanov 2014 년

86

인터넷에서 대부분의 솔루션에는 몇 가지 문제가 있습니다. 그래서 나는 후속 답변을하기로 결정했는데, 여기에는 왜 받아 들여진 대답을 받아들이지 않아야하는지 포함되어 있습니다.

시작 상황

모든 자식과 자식 등 으로 자바 스크립트 를 딥 카피 하고 싶습니다 Object. 내가하지 종류의 일반 개발자의 때죠하지만, 내가 Object있다 정상 properties , circular structures심지어 nested objects.

먼저 circular structurea를 만들어 봅시다 nested object.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Object이름이 지정된 모든 것을 하나로 모 읍시다 a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

다음으로 a이름이 지정된 변수 에 복사 b하고 변경하려고합니다.

var b = a;

b.x = 'b';
b.nested.y = 'b';

그렇지 않다면이 위대한 질문에 부딪치지 않기 때문에 여기서 무슨 일이 있었는지 알 것입니다.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

이제 해결책을 찾아 보자.

JSON

내가 시도한 첫 번째 시도는을 사용하는 것 JSON입니다.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

너무 많은 시간을 낭비하지 마십시오 TypeError: Converting circular structure to JSON.

재귀 사본 (허용 된 "답변")

허용되는 답변을 살펴 보겠습니다.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    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! Its type isn't supported.");
}

좋아 보인다? 그것은 객체의 재귀 복사본이며 다른 유형도 처리 Date하지만 필수는 아닙니다.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

재귀와 circular structures함께 잘 작동하지 않습니다 ...RangeError: Maximum call stack size exceeded

기본 솔루션

내 동료와 말다툼을 한 후, 상사는 우리에게 무슨 일이 있었는지 물었고, 그는 인터넷 검색 후 간단한 해결책 을 찾았습니다 . 이라고 Object.create합니다.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

이 솔루션은 얼마 전에 Javascript에 추가되었으며 심지어 처리합니다 circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... 그리고 내부의 중첩 구조에서는 작동하지 않았습니다.

기본 솔루션을위한 폴리 필

Object.createIE 8과 같은 구형 브라우저 에는 polyfill이 있습니다. Mozilla에서 권장하는 것과 같으며 물론 완벽하지는 않으며 네이티브 솔루션 과 동일한 문제가 발생 합니다 .

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

나는 놨는데 F우리가 무엇을 살펴 가질 수 있도록 범위를 벗어난 instanceof우리에게 있습니다.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

기본 솔루션 과 동일한 문제 이지만 출력이 약간 더 나쁩니다.

더 나은 (그러나 완벽하지는 않은) 솔루션

주위를 파고 때, 나는 (비슷한 질문을 찾을 때문에 "이"?되는 속성하여 자바 스크립트, 깊은 복사를 수행 할 때, 어떻게,주기를 피하는가를 이 일에)하지만, 더 나은 방법 솔루션.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

그리고 출력을 보자 ...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

요구 사항은 일치하지만, 변경을 포함하여 몇 가지 작은 문제, 거기에 여전히 instancenestedcirc에가 Object.

나뭇잎을 공유하는 나무의 구조는 복사되지 않으며 두 개의 독립적 인 나뭇잎이됩니다.

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

결론

재귀와 캐시를 사용하는 마지막 솔루션은 최고는 아니지만 객체 의 실제 깊은 복사입니다. 그것은 간단 처리 properties, circular structuresnested object,하지만 것 중 인스턴스까지 엉망 복제있다.

jsfiddle


12
결론은 그 문제를 피하는 것입니다 :)
mikus

@mikus 는 기본 사용 사례 이상을 다루는 실제 사양이 될 때까지 그렇습니다.
Fabio Poloni

2
위에 제공된 솔루션을 올바르게 분석했지만 저자가 결론을 내렸다면이 질문에 대한 해결책이 없음을 나타냅니다.
Amir Mog

2
JS가 네이티브 클론 기능을 포함하지 않는 것은 부끄러운 일입니다.
l00k

1
모든 최고의 답변 중에서도 이것이 올바른 답변에 가깝다고 생각합니다.
KTU

77

얕게 복사해도 괜찮다면 underscore.js 라이브러리에는 복제 방법이 있습니다.

y = _.clone(x);

또는 당신은 그것을 확장 할 수 있습니다

copiedObject = _.extend({},originalObject);

2
감사. Meteor 서버에서이 기술을 사용합니다.
Turbo

lodash를 빨리 시작하려면 lodash뿐만 아니라 npm, Browserify를 배우는 것이 좋습니다. 'npm i --save lodash.clone'을 사용한 복제본을 얻은 다음 'var clone = require ('lodash.clone ');' 작업을 요구하려면 browserify와 같은 것이 필요합니다. 설치하고 작동 방식을 배우면 코드를 실행할 때마다 (Chrome으로 직접 이동하는 대신) 'browserify yourfile.js> bundle.js; start chrome index.html'을 사용합니다. 그러면 파일과 npm 모듈에서 필요한 모든 파일이 bundle.js로 수집됩니다. 그래도 Gulp로 시간을 절약 하고이 단계를 자동화 할 수 있습니다.
Aaron Bell

65

자, 아래에이 객체가 있고 복제하려고한다고 상상해보십시오.

let obj = {a:1, b:2, c:3}; //ES6

또는

var obj = {a:1, b:2, c:3}; //ES5

대답은 주로 사용 하는 ECMAscript 에 따라 달라집니다 .에서 복제를 수행하는 데 ES6+간단히 사용할 수 있습니다 Object.assign.

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

또는 다음과 같이 스프레드 연산자를 사용하십시오.

let cloned = {...obj}; //new {a:1, b:2, c:3};

그러나을 ES5사용하는 경우 몇 가지 방법을 사용할 수 있지만, JSON.stringify큰 데이터 덩어리를 사용하여 복사하지 마십시오.하지만 많은 경우 한 줄 편리한 방법이 될 수 있습니다.

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

무엇에 해당하는지 예를 들어 주 big chunk of data시겠습니까? 100kb? 100MB? 감사!
user1063287

예, @ user1063287, 기본적으로 더 큰 데이터, 성능이 더 나쁩니다. 따라서 kb, mb 또는 gb가 아니라 실제로 의존합니다. 기능과 다른 것들을 위해 ...
Alireza

3
Object.assign얕은 사본을 만듭니다 (확산 된 그대로 @Alizera)
Bogdan D

es5에서 let을 사용할 수 없습니다 : ^) @Alireza
SensationSama

40

특히 우아하지 않은 솔루션 중 하나는 JSON 인코딩을 사용하여 멤버 메소드가없는 오브젝트의 깊은 사본을 작성하는 것입니다. 방법론은 대상 객체를 JSON으로 인코딩 한 다음 디코딩하여 찾고있는 사본을 얻는 것입니다. 필요한만큼 사본을 만들려는 횟수만큼 디코딩 할 수 있습니다.

물론 함수는 JSON에 속하지 않으므로 멤버 메소드가없는 객체에서만 작동합니다.

이 방법론은 JSON 값을 키-값 저장소에 저장하고 JavaScript API에서 객체로 노출 될 때 각 객체는 실제로 객체의 원래 상태 사본을 포함하므로 유스 케이스에 완벽했습니다. 호출자가 노출 된 객체를 변경 한 후 델타를 계산할 수 있습니다.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

함수가 JSON에 속하지 않는 이유는 무엇입니까? 나는 그들이 다시 한 번 다음 JSON으로 옮겨진 본 적이 ...
the_drow

5
함수는 JSON 사양의 일부가 아니기 때문에 데이터를 전송하는 안전한 (또는 현명한) 방법이 아니므로 JSON이 만들어졌습니다. Firefox의 기본 JSON 인코더는 전달 된 함수를 단순히 무시하지만 다른 사람의 동작에 대해서는 잘 모르겠습니다.
Kris Walker

1
@mark : { 'foo': function() { return 1; } }는 리터럴로 구성된 객체입니다.
abarnert

@abarnert 함수는 데이터가 아닙니다. "함수 리터럴"은 잘못된 이름입니다. 함수는 할당과 모든 "직렬화 할 수없는"항목을 포함하여 임의의 코드를 포함 할 수 있기 때문입니다.
vemv

35

스프레드 속성 을 사용하여 참조없이 객체를 복사 할 수 있습니다 . 그러나 '복사'는 객체 / 배열 수준이 가장 낮습니다. 중첩 속성은 여전히 ​​참조입니다!


완전한 클론 :

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

두 번째 수준에서 참조가있는 복제 :

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript는 실제로 기본적으로 딥 클론을 지원하지 않습니다. 유틸리티 기능을 사용하십시오. 예를 들어 Ramda :

http://ramdajs.com/docs/#clone


1
이것은 작동하지 않습니다 ... 아마도 x가 예를 들어 x = [ 'ab', 'cd', ...]에 대한 배열 일 때 작동합니다.
Kamil Kiełczewski

3
이것은 작동하지만 이것은 SHALLOW 사본이므로 다른 객체에 대한 깊은 참조는 참조로 남아 있습니다!
벅스 버니

부분 복제는 다음과 같은 방식으로도 발생할 수 있습니다.const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Cristian Traìna

25

AngularJS를 사용하는 사람들에게는이 라이브러리의 객체를 복제하거나 확장하는 직접적인 방법도 있습니다.

var destination = angular.copy(source);

또는

angular.copy(source, destination);

angular.copy 문서 에 대한 추가 정보 ...


2
이것은 깊은 사본입니다.
zamnuts

22

사용할 수있는 기능은 다음과 같습니다.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

10
이 답변은 꽤 가깝지만 정확하지는 않습니다. Date 객체를 복제하려고하면 Date 생성자 함수를 호출하면 현재 날짜 / 시간으로 새 Date가 초기화되므로 같은 날짜가 표시되지 않습니다. 이 값은 열거 할 수 없으며 for / in 루프에 의해 복사되지 않습니다.
A. Levy

완벽하지는 않지만 기본적인 경우에는 좋습니다. 예를 들어 기본 Object, Array 또는 String이 될 수있는 인수를 간단하게 복제 할 수 있습니다.
james_womack

을 사용하여 생성자를 올바르게 호출하도록 찬성했습니다 new. 수락 된 답변은 그렇지 않습니다.
GetFree

다른 모든 노드에서 작동합니다! 여전히 왼쪽 참조 링크
user956584

재귀적인 생각은 훌륭하지만 값이 배열이면 작동합니까?
Q10Viking

22

A. Levy의 대답은 거의 완벽합니다. 여기에 약간의 기여 가 있습니다. 재귀 참조를 처리하는 방법 이 있습니다.

if(this[attr]==this) copy[attr] = copy;

객체가 XML DOM 요소 인 경우 대신 cloneNode 를 사용해야 합니다

if(this.cloneNode) return this.cloneNode(true);

A.Levy의 철저한 연구와 Calvin의 프로토 타이핑 접근법에서 영감을 얻은이 솔루션을 제공합니다.

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

답변에서 Andy Burke의 메모를 참조하십시오.


3
Date.prototype.clone = function() {return new Date(+this)};
RobG

22

이 기사에서 : Brian Huisman이 자바 스크립트배열과 객체를 복사하는 방법 :

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var 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;
};

4
이것은 가깝지만 어떤 객체에서도 작동하지 않습니다. 이것으로 Date 객체를 복제하십시오. 모든 속성을 열거 할 수있는 것은 아니므로 for / in 루프에 모두 표시되지는 않습니다.
A. Levy

이와 같은 객체 프로토 타입에 추가하면 jQuery가 깨졌습니다. clone2로 이름을 바꾼 경우에도 마찬가지입니다.
iPadDeveloper2011 23

3
@ iPadDeveloper2011 위의 코드에는 '(var i의 경우)'대신 'i' '(이 경우 i)'라는 전역 변수를 생성하는 버그가 있습니다. 나는 업장을 편집하고 수정할 수있는 충분한 업장이 있습니다.
mikemaccana

1
@Calvin : 열거 할 수없는 속성을 만들어야합니다. 그렇지 않으면 '복제'가 'for'루프에 나타납니다.
mikemaccana

2
var copiedObj = Object.create(obj);좋은 방법이 아닌가?
Dan P.

19

ES-6에서는 간단하게 Object.assign (...)을 사용할 수 있습니다. 전의:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

https://googlechrome.github.io/samples/object-assign-es6/을 참조하십시오.


12
개체를 딥 복제하지 않습니다.
8 월

그것은 사본이 아니라 과제입니다. clone.Title = "단지 복제본"은 obj.Title = "단지 복제본"을 의미합니다.
HoldOffHunger

@HoldOffHunger 당신은 착각합니다. 브라우저의 JS 콘솔에서 확인하십시오 ( let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";)
collapsar

@collapsar : 정확히 내가 확인한 다음 console.log (person)은 "Thor Odinson"이 아니라 "Whazzup"입니다. 8 월의 의견을보십시오.
HoldOffHunger

1
@HoldOffHunger Chrome 60.0.3112.113 또는 Edge 14.14393에서는 발생하지 않습니다. 프리미티브 유형 obj의 속성 값이 실제로 복제되므로 8 월의 의견은 적용되지 않습니다 . 객체 자체 인 속성 값은 복제되지 않습니다.
collapsar

18

ECMAScript 2018에서

let objClone = { ...obj };

주의하십시오 중첩 된 객체가 여전히 복사됩니다 참고로.


1
중첩 된 객체가 여전히 참조로 복사된다는 힌트에 감사드립니다! "복제본"에서 중첩 속성을 수정했지만 원본이 수정 되었기 때문에 코드를 디버깅 할 때 거의 미쳤다.
Benny Neugebauer

이것은 2018이 아닌 ES2016이며이 답변은 2 년 전에 제공되었습니다 .
Dan Dascalescu 2016 년

그래서 내가뿐만 아니라 중첩 된 재산의 사본을 무엇을 원하는 경우
선일 Garg를

1
@SunilGarg 중첩 된 속성을 복사하려면 다음을 사용할 수 있습니다. const objDeepClone = JSON.parse(JSON.stringify(obj));
Pavan Garre

16

Lodash 사용하기 :

var y = _.clone(x, true);

5
세상에 복제를 재창조하는 것은 미친 짓입니다. 이것이 유일한 정답입니다.
Dan Ross

5
나는 _.cloneDeep(x)본질적으로 위와 같은 것이 더 좋지만 더 잘 읽습니다.
garbanzio

14

간단한 객체 복제에 관심이 있습니다.

JSON.parse(JSON.stringify(json_original));

출처 : JavaScript 객체를 참조가 아닌 새 변수에 복사하는 방법은 무엇입니까?


아주 좋은-간단합니다.
Matt H

@MattH :이 답변은 2012 년 에 이미 제공 되었습니다 . 당신은 그것을 볼 않았다? 모하메드, 당신은 그들 중 하나를 복제하기 전에 기존 답변을 확인 했습니까?
Dan Dascalescu 2016 년

잘 한 가지 방법입니다. ty는 그런 생각을하지 않았습니다
1-14x0r

13

한 줄의 코드를 사용하여 객체를 복제하고 이전 객체에서 참조를 제거 할 수 있습니다. 간단히 :

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

현재 Object.create를 지원하지 않는 브라우저 / 엔진의 경우이 polyfill을 사용할 수 있습니다.

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

1
+1 Object.create(...)은 확실히 갈 길입니다.
René Nyffenegger 2016 년

완벽한 답변. 아마 당신은에 대한 설명을 추가 할 수 Object.hasOwnProperty있습니까? 그렇게하면 사람들이 프로토 타입 링크를 검색하지 못하게하는 방법을 알게됩니다.
froginvasion

잘 작동하지만 polyfill은 어떤 브라우저에서 작동합니까?
Ian Lunn

11
이것은 프로토 타입으로 obj1을 사용하여 obj2를 만듭니다. textobj2 에서 멤버를 음영 처리하기 때문에 작동합니다 . obj2에서 멤버를 찾을 수 없을 때 프로토 타입 체인을 지연시키면서 사본을 작성하지 않습니다.
Nick Desaulniers

2
이것은 "참조없이"생성하지 않고 단지 참조를 프로토 타입으로 이동시킵니다. 여전히 참조입니다. 속성이 원본에서 변경되면 "복제본"의 프로토 타입 속성도 변경됩니다. 전혀 클론이 아닙니다.
Jimbo Jonny

13

오래된 질문에 대한 새로운 답변! Spread Syntax 와 함께 ECMAScript 2016 (ES6)을 사용하는 것이 즐겁다면 쉽습니다.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

이것은 객체의 얕은 사본을위한 깨끗한 방법을 제공합니다. 딥 카피를 만들려면 모든 재귀 적으로 중첩 된 객체의 모든 값에 대한 새로운 카피를 작성해야합니다. 위의 더 무거운 솔루션이 필요합니다.

자바 스크립트는 계속 발전하고 있습니다.


2
당신이 개체에 대해 정의 함수가있을 때 작동하지 않습니다
페트르 마렉

내가 스프레드 오퍼레이터가 iterables로만 작동하는 것을 본다면 developer.mozilla.org 는 말합니다 : var obj = {'key1': 'value1'}; var array = [...obj]; // TypeError: obj is not iterable
Oleh

@Oleh 사용하므로 '{...} OBJ 대신 ... OBJ] 중,'
manikant 가우 탐

@manikantgautam 이전에 Object.assign ()을 사용하고 있었지만 이제는 최신 Chrome, Firefox (여전히 Edge 및 Safari에서는 지원되지 않음)에서 객체 확산 구문이 지원됩니다. ECMAScript 제안은 ...하지만 Babel은 내가 볼 수있는 한 그것을 지원하므로 아마도 사용하기에 안전합니다.
Oleh

12
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

속성 객체뿐만 아니라 클래스 인스턴스 를 (얕게) 복제하려는 경우 ES6 솔루션 .


이것이 어떻게 다른 let cloned = Object.assign({}, obj)가요?
ceztko 2016 년

10

나는 간단하고 효과적인 답변이 있다고 생각합니다. 딥 카피에는 두 가지 우려가 있습니다.

  1. 속성을 서로 독립적으로 유지하십시오.
  2. 그리고 복제 된 객체에 메소드를 유지하십시오.

따라서 하나의 간단한 해결책은 먼저 직렬화 및 역 직렬화 한 다음 함수를 복사하기 위해 할당하는 것입니다.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

이 질문에 많은 답변이 있지만이 질문도 도움이되기를 바랍니다.


lodash를 가져올 수 있지만 lodash 사용을 선호합니다 cloneDeep.
ConductedClever

2
JSON.parse (JSON.stringify (source))를 사용하고 있습니다. 항상 작동합니다.
Misha

2
@Misha, 이렇게하면 기능을 놓치게됩니다. '작품'이라는 용어는 많은 의미를 갖습니다.
ConductedClever

그리고 내가 언급 한 방식으로 첫 번째 레이어의 기능 만 복사됩니다. 따라서 서로 안에 어떤 객체가 있다면, 유일한 방법은 필드별로 재귀 적으로 복사하는 것입니다.
ConductedClever

9

딥 카피 및 복제의 경우 JSON.stringify를 선택한 다음 JSON.parse 객체를 구문 분석합니다.

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

꽤 영리합니다 ...이 접근법의 단점은 무엇입니까?
Aleks

8

이것은 A. Levy의 코드를 수정하여 함수 및 다중 / 순환 참조의 복제도 처리합니다. 즉, 복제 된 트리의 두 속성이 동일한 객체의 참조 인 경우 복제 된 객체 트리는 속성은 참조 된 객체의 동일한 복제본을 가리 킵니다. 이것은 또한 처리되지 않은 채로 있으면 무한 루프로 이어지는 순환 종속성의 경우를 해결합니다. 알고리즘의 복잡성은 O (n)입니다.

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

몇 가지 빠른 테스트

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

1
2016 년 9 월 현재이 질문에 대한 유일한 해결책입니다.
DomQ

6

난 그냥 모든에 추가하고 싶었다 Object.create 이 게시물의 솔루션에 nodejs에서 원하는 방식으로 작동하지 않습니다.

Firefox에서 다음의 결과

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

이다

{test:"test"}.

nodejs에서는

{}

이것은 복제가 아닌 프로토 타입 상속입니다.
d13

1
@ d13 인수가 유효하지만 JavaScript에는 객체를 복제하는 표준화 된 방법이 없습니다. 이것은 프로토 타입 상속이지만, 개념을 이해하면 복제본으로 사용할 수 있습니다.
froginvasion

@froginvasion. Object.create를 사용할 때의 유일한 문제는 중첩 된 객체와 배열이 프로토 타입의 중첩 된 객체와 배열에 대한 포인터 참조 일뿐입니다. jsbin.com/EKivInO/2/edit?js,console . 기술적으로 "복제 된"객체에는 다른 객체의 속성에 대한 공유 참조가 아닌 고유 한 속성이 있어야합니다.
d13

@ d13 좋아, 지금 당신의 요점을 참조하십시오. 그러나 내가 의미하는 바는 너무 많은 사람들이 프로토 타입 상속의 개념으로 소외되었고 그것이 어떻게 작동하는지 배우지 못한다는 것입니다. 내가 실수 Object.hasOwnProperty하지 않으면 배열을 소유하고 있는지 여부를 확인하기 위해 호출하여 예제를 수정할 수 있습니다 . 예. 이것은 프로토 타입 상속을 처리하기 위해 복잡성을 추가합니다.
froginvasion

6
function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};

2
if(!src && typeof src != "object"){. 그게 있어야한다고 생각 ||하지 &&.
MikeM

6

이후 mindeavor이 객체가 복제 된 수있는 '문자 - 건설'객체가 될 것을 언급, 해결책은 간단에있을 생성하는 객체의 인스턴스를 복제보다 객체를 여러 번 오히려 :

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

6

내 자신의 구현을 작성했습니다. 더 나은 솔루션으로 계산되는지 확실하지 않습니다.

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

다음은 구현입니다.

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

내 경우는 약간 복잡하지만 내 개체에서 작동하지 않습니다.
Sajuuk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.