ES6에서 객체를 복제하는 좋은 방법입니까?


155

"javascript clone object"에 대한 인터넷 검색은 정말 이상한 결과를 가져옵니다. 그중 일부는 절망적으로 구식이며 일부는 너무 복잡합니다.

let clone = {...original};

이것에 문제가 있습니까?


1
이것은 법적 ES6이 아닙니다. 그러나 이것이 복제본이 아닌 경우 복제본과 원래 속성이 모두 동일한 것을 가리 킵니다. 예를 들어, 문자 그대로 original = { a: [1,2,3] }의 복제본을 제공합니다 . 중 하나를 통해 수정 또는 수정 같은 것을 , 그래서,이) 나쁜 =입니다clone.aoriginal.acloneoriginal
마이크 'POMAX'Kamermans

2
@AlbertoRivera 자바 스크립트 표준에 향후 추가 될 가능성이있는 2 단계 제안이라는 점 에서 다소 유효한 JavaScript 입니다.
Frxstrem

ESF에 관한 질문에 @Frxstrem, 이것은 유효하지 않습니다 JavaScript =)
Mike 'Pomax'Kamermans

3
얕은 또는 깊은 복제?
Felix Kling

2
맞습니다 . 유효한 ES6이 아니며 유효한 ES9 입니다. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mikemaccana

답변:


240

얕은 클로닝에 좋습니다 . 객체 스프레드는 ECMAScript를 2018의 표준입니다 .

딥 클로닝을 위해서는 다른 솔루션 이 필요합니다 .

const clone = {...original} 얕은 클론에

const newobj = {...original, prop: newOne} 원본에 다른 소품을 불변 ​​적으로 추가하고 새 객체로 저장합니다.


18
그러나 이것은 단순한 복제본이 아닌가? 마찬가지로 속성은 재귀 적으로 복제되지 않습니까? 따라서 original.innerObject === clone.innerObject 및 original.innerObject.property를 변경하면 clone.innerObject.property가 변경됩니다.
milanio

18
예, 이것은 얕은 클론입니다. 딥 클론을 원한다면 반드시 사용해야합니다JSON.parse(JSON.stringify(input))
Mark Shust at M.academy

8
/! \ JSON.parse (JSON.stringify (input))은 정의되지 않은 날짜를 엉망으로 만듭니다 ... 복제의 총알이 아닙니다! 참조 : maxpou.fr/immutability-js-without-library
Guillaume

1
해킹 JSON.stringify () / JSON.parse ()는 ES6에서 객체를 딥 복제하는 데 권장되는 방법입니까? 나는 그것을 계속 추천합니다. 방해.
Solvitieg 2016 년

3
@MarkShust JSON.parse(JSON.stringify(input))는 작동하지 않습니다. 왜냐하면 값 이 functions있거나 infinity값으로 존재하면 단순히 null그 자리에 할당 되기 때문 입니다. 값이 단순 literals하고 그렇지 않은 경우에만 작동합니다 functions.
backslashN

66

편집 :이 답변을 게시했을 때 {...obj}대부분의 브라우저에서 구문을 사용할 수 없었습니다. 요즘에는 IE 11을 지원 해야하는 경우가 아니라면 잘 사용해야합니다.

Object.assign을 사용하십시오.

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

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

그러나 이것은 딥 클론을 만들지는 않습니다. 아직까지는 심층 복제 방법이 없습니다.

편집 : 의견에 언급 된 @Mike 'Pomax'Kamermans는 다음을 사용하여 간단한 객체 (예 : 프로토 타입, 함수 또는 순환 참조 없음)를 딥 복제 할 수 있습니다 JSON.parse(JSON.stringify(input))


19
객체가 실제 객체 리터럴이고 순수 데이터 인 경우 JSON.parse(JSON.stringify(input))하나의 딥 클론이 있습니다. 그러나 프로토 타입, 함수 또는 순환 참조가 작동하는 순간 해당 솔루션이 더 이상 작동하지 않습니다.
Mike 'Pomax'Kamermans

@ Mike'Pomax'Kamermans 사실입니다. getter 및 setter하는 기능을 잃는 ...하지만, 끔찍한
알베르토 리베라

객체를 딥 복제하기 위해 일반 기능이 필요한 경우 stackoverflow.com/a/13333781/560114를 확인하십시오 .
Matt Browne

1
이제 기본적으로 심층 복제 를 수행 할 수있는 방법이 있습니다 .
Dan Dascalescu 5

1
@DanDascalescu 비록 실험적이지만 꽤 유망 해 보입니다. 정보 주셔서 감사합니다!
Alberto Rivera

4

사용한 메소드가 Date 와 같은 데이터 유형과 관련된 오브젝트에서 제대로 작동하지 않으면 다음을 시도하십시오.

수입 _

import * as _ from 'lodash';

딥 클론 객체

myObjCopy = _.cloneDeep(myObj);

그냥 import _ from 'lodash';충분하다. 그러나 "바퀴를 재발 명하지 마십시오"라는 대답에 +1이 있습니다.
rustyx

lodash는 부풀어 오른다. 간단한 딥 카피를 위해 lodash를 가져올 필요가 없습니다. 여기에 다른 많은 솔루션이 있습니다. 이것은 희박한 앱을 구축하려는 웹 개발자에게 정말 나쁜 대답입니다.
Jason Rice

3

json.parse (json.stringify (object))를 사용하지 않으려면 재귀 적으로 키-값 사본을 만들 수 있습니다.

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

그러나 가장 좋은 방법은 자체 복제본을 반환 할 수있는 클래스를 만드는 것입니다

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}

2

@marcel의 답변에 이어 복제 된 객체에서 일부 기능이 여전히 누락되었음을 발견했습니다. 예 :

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

MyObject에서 methodA를 복제 할 수 있지만 methodB는 제외되었습니다. 누락 되었기 때문에 발생했습니다

enumerable: true

그것은에 나타나지 않았다 의미

for(let key in item)

대신에 나는

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

열거 할 수없는 키가 포함됩니다.

또한 프로토 타입 ( proto )이 복제되지 않았 음을 발견했습니다 . 이를 위해 나는 결국

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

추신 :이 작업을 수행 할 수있는 내장 함수를 찾을 수 없다는 좌절.


1

당신도 이것처럼 할 수 있습니다

let copiedData = JSON.parse(JSON.stringify(data));

-1
We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

그러나 Object.assign ()은 딥 클론을 만들지 않습니다.

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

이를 해결하기 위해 user [key]의 각 값을 검사하는 복제 루프를 사용해야하며, 객체 인 경우 해당 구조도 복제해야합니다. 이를 "딥 클로닝"이라고합니다.

딥 클로닝을위한 표준 알고리즘이 있는데, 위의 경우와 구조화 된 클로닝 알고리즘 이라는 복잡한 사례를 처리합니다 . 휠을 재발 명하지 않기 위해 JavaScript 라이브러리 lodash 에서 휠의 작동 구현을 사용할 수 있습니다 . 메소드는 _.cloneDeep (obj) 입니다.


-1

위의 모든 방법은 n 수준으로 중첩 된 개체의 깊은 복제를 처리하지 않습니다. 나는 다른 사람들보다 성능을 확인하지 않았지만 짧고 간단합니다.

아래의 첫 번째 예는 Object.assign첫 번째 수준까지의 복제본을 사용 하는 개체 복제를 보여줍니다 .

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

아래 접근 방식 딥 클론 객체 사용

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript


JSON.parse / stringify는 몇 년 동안 열악한 딥 클로닝 방법으로 언급되었습니다 . 이전 답변과 관련 질문을 확인하십시오. 또한 이것은 ES6에 새로운 것은 아닙니다.
Dan Dascalescu 5

@ DanDascalescu 나는 이것을 알고 간단한 객체에 사용하는 것이 문제가되지 않아야한다고 생각합니다. 다른 사람들은 또한 같은 게시물과 댓글로 답변에서 이것을 언급했습니다. 나는 그것이 공감할 가치가 없다고 생각한다.
Saksham

정확히- "다른 사람들은 또한 JSON.parse / stringify를 답변에 언급했습니다." 같은 솔루션으로 또 다른 답변을 게시해야하는 이유는 무엇입니까?
Dan Dascalescu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.