문제

나는 개체 x.고 싶으로 복사하는 개체 y, 는 변경 y 수정하지 마십시오 x.는 것을 깨달았을 복사에서 파생된 개체 내에서의 자바스크립트 오브젝트에 추가,원치 않는 특성.이 문제가 되지 않습니다 때문에,내가 복사 중 하나 내 자신의 문자-건설체입니다.

어떻게 올바르게 복제 JavaScript object?

도움이 되었습니까?

해결책

JavaScript의 모든 객체에 대해이 작업을 수행하는 것은 간단하거나 간단하지 않습니다. 프로토 타입에 남겨져 있고 새 인스턴스에 복사되지 않아야하는 객체의 프로토 타입에서 속성을 잘못 선택하는 문제가 발생합니다. 예를 들어, 당신이 추가하는 경우 clone 방법 Object.prototype, 일부 답변에서 알 수 있듯이 해당 속성을 명시 적으로 건너 뛰어야합니다. 그러나 다른 추가 방법이 추가되면 어떨까요? Object.prototype, 또는 다른 중간 프로토 타입을 모르는 다른 중간 프로토 타입? 이 경우, 당신은 당신이하지 말아야 할 속성을 복사 할 것이므로, 예기치 않은 비 국한 속성을 hasOwnProperty 방법.

발작 할 수없는 속성 외에도 숨겨진 속성이있는 개체를 복사하려고 할 때 더 힘든 문제가 발생합니다. 예를 들어, prototype 함수의 숨겨진 속성입니다. 또한 객체의 프로토 타입은 속성으로 참조됩니다. __proto__, 또한 숨겨져 있으며 소스 개체의 속성에 반복되는 for/in 루프로 복사되지 않습니다. 제 생각에는 __proto__ Firefox의 JavaScript 통역사에만 해당 될 수 있으며 다른 브라우저에서는 다른 것이지만 사진을 얻을 수 있습니다. 모든 것이 열거되는 것은 아닙니다. 이름을 알고 있다면 숨겨진 속성을 복사 할 수 있지만 자동으로 발견 할 수있는 방법을 모릅니다.

우아한 솔루션을 찾는 또 다른 걸림돌은 프로토 타입 상속을 올바르게 설정하는 문제입니다. 소스 객체의 프로토 타입 인 경우 Object, 그런 다음 단순히 새로운 일반 객체를 만듭니다 {} 작동하지만 소스의 프로토 타입이 일부 후손 인 경우 Object, 당신은 당신이 hasOwnProperty 필터 또는 프로토 타입에 있었지만 처음에는 열거 할 수 없었습니다. 한 가지 해결책은 소스 개체를 호출하는 것입니다. constructor 초기 사본 개체를 얻은 다음 속성을 복사하려면 속성이지만 여전히 발포 할 수없는 속성을 얻지 못합니다. 예를 들어, a 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 객체를 처리 할 수는 없지만, 던지는 모든 것에 대해서만 효과가 있다고 가정하지 않는 한 많은 목적으로 충분할 수 있습니다.

다른 팁

사용하지 않는 경우 DateS, 함수, 정의되지 않은 또는 무한대는 객체 내에서 매우 간단한 하나의 라이너는 다음과 같습니다. 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()

이것은 객체, 어레이, 문자열, 부울 및 숫자를 포함하는 모든 종류의 객체에 대해 작동합니다.

또한보십시오 브라우저의 구조화 된 클론 알고리즘에 대한이 기사 작업자에게 메시지를 게시 할 때 사용됩니다. 또한 깊은 복제 기능도 포함되어 있습니다.

jQuery와 함께 할 수 있습니다 얕은 사본 ~와 함께 연장하다:

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

후속 변경 copiedObject 영향을 미치지 않습니다 originalObject, 그 반대.

또는 만들기 위해 딥 카피:

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

ECMAScript 6에는 있습니다 Object.assign 모든 열거 가능한 자체 속성의 값을 한 객체에서 다른 개체로 복사하는 방법. 예를 들어:

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

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

MDN:

  • 얕은 사본을 원한다면 사용하십시오 Object.assign({}, a)
  • "딥"사본의 경우 사용하십시오 JSON.parse(JSON.stringify(a))

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

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

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

  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

이 솔루션이 우수하다고 생각하는 이유는 무엇입니까? 그것은 기본이므로 루핑이없고 재귀가 없습니다. 그러나 이전 브라우저에는 폴리 필이 필요합니다.

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

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

인터넷에 대부분의 솔루션에는 몇 가지 문제가 있습니다. 그래서 나는 수락 된 답변이 수락되지 않아야하는 이유를 포함하여 후속 조치를 결정하기로 결정했습니다.

시작 상황

나는 원한다 깊은 코피 자바 스크립트 Object 모든 자녀와 자녀들과 함께. 하지만 나는 평범한 개발자가 아니기 때문에 Object 가지다 정상 properties, circular structures 그리고 심지어 nested objects.

그래서 만들어 봅시다 circular structure 그리고 a 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.");
}

좋아 보인다, heh? 객체의 재귀 사본이며 다른 유형도 처리합니다. 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.create IE 8과 마찬가지로 오래된 브라우저에서는 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

와 같은 문제 기본 솔루션, 그러나 조금 더 나쁜 출력.

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

파고들 때 비슷한 질문을 발견했습니다.JavaScript에서는 딥 카피를 수행 할 때 속성이 "이것"이기 때문에주기를 어떻게 피합니까?) 이것에 대해서는 더 나은 솔루션으로.

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

요구 사항이 일치하지만 변경 변경을 포함하여 여전히 작은 문제가 있습니다. instancenested 그리고 circ 에게 Object.

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

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

결론

재귀와 캐시를 사용한 마지막 솔루션은 최고는 아니지만 진짜 대상의 깊은 코피. 간단합니다 properties, circular structures 그리고 nested object, 그러나 복제하는 동안 인스턴스를 엉망으로 만들 것입니다.

jsfiddle

얕은 사본으로 괜찮다면 alterscore.js 라이브러리에는 클론 방법.

y = _.clone(x);

또는처럼 확장 할 수 있습니다

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

확인, 아래 에이 물체가 있고 그것을 복제하고 싶다고 상상해보십시오.

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

또는

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

대답은 주로 그에 따른 것입니다 ecmascript 당신은 IN을 사용합니다 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

특히 우연한 솔루션 중 하나는 JSON 인코딩을 사용하여 멤버 메소드가없는 객체의 깊은 사본을 만드는 것입니다. 방법론은 JSON이 대상 객체를 인코딩 한 다음 해독하여 원하는 사본을 얻는 것입니다. 필요한만큼 많은 사본을 만들고 싶은만큼 해독 할 수 있습니다.

물론 함수는 JSON에 속하지 않으므로 멤버 방법이없는 객체에만 작동합니다.

이 방법론은 JSON Blobs를 키 값 저장소에 저장하고 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

당신은 단순히 사용 퍼짐성 를 복사가 없는 객체 참조가 있습니다.하지만 조심하십시오(댓글)에는'복사'는 다만에 최저체/배 수준입니다.중첩성은 아직도 참조!


완전 clone:

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 실제로 지원하지 않는 깊은 클론 자체적으로 제공합니다.사용하는 유틸리티 기능입니다.예를 들면 람다:

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

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

var destination = angular.copy(source);

또는

angular.copy(source, destination);

더 많은 angular.copy 선적 서류 비치...

A.Levy의 대답은 거의 완전했습니다. 여기에 작은 기여가 있습니다. 재귀 참조를 처리하는 방법이 있습니다,이 줄을 참조하십시오

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

객체가 xml dom 요소 인 경우 사용해야합니다. 클로네 노드 대신에

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의 메모도 답을 참조하십시오.

다음은 사용할 수있는 기능입니다.

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

이 기사에서 : 자바 스크립트로 배열 및 객체를 복사하는 방법 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;
};

ES-6에서는 단순히 Object.Assign (...)을 사용할 수 있습니다. 전:

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

좋은 참조가 여기에 있습니다.https://googlechrome.github.io/samples/object-assign-es6/

ECMAScript 2018에서

let objClone = { ...obj };

알고 있어야합니다 중첩 된 물체 여전히 복사되었습니다 참조로.

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

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

현재 객체를 지원하지 않는 브라우저 / 엔진의 경우 Create를 만듭니다.이 폴리 필드를 사용할 수 있습니다.

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

오래된 질문에 대한 새로운 답변! ECMAScript 2016 (ES6)을 스프레드 구문, 그것은 간단합니다.

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

이것은 물체의 얕은 사본에 대한 깨끗한 방법을 제공합니다. 재귀 적으로 중첩 된 모든 객체의 모든 값의 새로운 사본을 메이킹하는 딥 카피를 만들기 위의 무거운 솔루션이 필요합니다.

JavaScript는 계속 진화합니다.

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

ES6 솔루션 (얕은) 복제를 원한다면 수업 인스턴스 그리고 단지 속성 대상이 아닙니다.

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

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

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

Lodash 사용 :

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

간단하고 작동하는 대답이 있다고 생각합니다. 깊은 복사에는 두 가지 문제가 있습니다.

  1. 속성을 서로 독립적으로 유지하십시오.
  2. 복제 된 물체에 방법을 살리십시오.

따라서 간단한 솔루션 중 하나는 먼저 직렬화하고 필사적 인 다음 기능을 복사하기 위해 할당을 수행하는 것입니다.

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

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

딥 카피와 클론의 경우 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}}

나는 단지 모든 것을 추가하고 싶었다 Object.create 이 게시물의 솔루션은 NodeJS와 원하는 방식으로 작동하지 않는다는 솔루션입니다.

Firefox에서 결과

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

~이다

{test:"test"}.

nodejs에서는입니다

{}

이것은 기능의 복제 및 다중/순환 참조를 처리하기위한 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));
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;
};

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

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

위의 Jan Turoň의 답변은 매우 가깝고 호환성 문제로 인해 브라우저에서 사용하는 것이 가장 좋을 수 있지만 잠재적으로 이상한 열거 문제가 발생할 수 있습니다. 예를 들어 실행 :

for ( var i in someArray ) { ... }

배열의 요소를 반복 한 후 복제 () 메소드를 I에 할당합니다. 열거를 피하고 node.js와 함께 작동하는 적응이 있습니다.

Object.defineProperty( Object.prototype, "clone", {
    value: 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;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

DefineProperty () 기본값을 False에 열거 할 수 있으므로 클론 () 메소드를 열거 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top