자바 스크립트 ES6 클래스 인스턴스를 복제하는 방법


98

ES6를 사용하여 Javascript 클래스 인스턴스를 복제하는 방법은 무엇입니까?

jquery 또는 $ extend 기반 솔루션에 관심이 없습니다.

나는 문제가 매우 복잡하다는 것을 암시하는 객체 복제에 대한 아주 오래된 논의를 보았지만 ES6에서는 매우 간단한 해결책이 제시됩니다.

편집 : 내 질문이 중복 된 것으로 제안되고 있습니다. 나는 그 대답을 보았지만 7 살이고 pre-ES6 js를 사용하는 매우 복잡한 대답을 포함합니다. ES6를 허용하는 내 질문에 훨씬 더 간단한 솔루션이 있다고 제안합니다.


2
Stack Overflow의 이전 질문에 대한 새 답변이있는 경우 새 질문을 만드는 것이 아니라 원래 질문에 해당 답변을 추가하십시오.
Heretic Monkey

1
ES6 클래스 인스턴스가 "일반"객체와 다르게 작동하기 때문에 Tom이 직면 한 문제를 확인했습니다.
CherryNerd

2
또한 수락 된 답변의 첫 번째 코드 "가능한 중복"은 ES6 클래스의 인스턴스에서 실행하려고 할 때 실제로 충돌을 제공합니다
CherryNerd

ES6 클래스 인스턴스가 객체이지만 모든 객체가 ES6 클래스 인스턴스가 아니므로 다른 질문 이이 질문의 문제를 다루지 않기 때문에 이것이 중복이 아니라고 생각합니다.
Tomáš Zato-Monica 복원

5
중복이 아닙니다. 다른 질문은 Object데이터 홀더로 사용되는 순수에 관한 것이 었습니다 . 이것은 ES6 classes와 클래스 유형 정보를 잃지 않는 문제에 관한 것입니다. 다른 솔루션이 필요합니다.
flori

답변:


113

복잡하다; 나는 많이 시도했다! 결국이 한 줄짜리 줄은 내 사용자 지정 ES6 클래스 인스턴스에서 작동했습니다.

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

프로토 타입 설정을 피하는 이유 는 코드를 많이 느리게 만든다고 하기 때문 입니다.

기호를 지원하지만 getter / setter에 적합하지 않으며 열거 할 수없는 속성으로 작동하지 않습니다 ( Object.assign () 문서 참조 ). 또한 기본 내부 클래스 (예 : Array, Date, RegExp, Map 등)를 복제하는 것은 슬프게도 개별 처리가 필요한 것처럼 보입니다.

결론 : 엉망입니다. 언젠가는 기본적이고 깨끗한 복제 기능이 있기를 바랍니다.


1
실제로 열거 가능한 속성이 아니기 때문에 정적 메서드를 복사하지 않습니다.
씨 Lavalamp

5
@Mr. Lavalamp와 정적 메서드를 어떻게 복사 할 수 있습니까?
flori

이것은 배열을 파괴합니다! 모든 배열을 "0", "1", ... 키가있는 객체로 변환합니다.
Vahid

1
@KeshaAntonov typeof 및 Array 메서드를 사용하여 솔루션을 찾을 수 있습니다. 나는 모든 속성을 수동으로 복제하는 것을 선호했습니다.
Vahid

1
객체 자체 인 속성을 복제 할 것으로 기대하지 마십시오 : jsbin.com/qeziwetexu/edit?js,console
jduhls

10
const clone = Object.assign( {}, instanceOfBlah );
Object.setPrototypeOf( clone, Blah.prototype );

Object.assign 의 특성에 유의하십시오. 단순 복사를 수행하고 클래스 메소드를 복사하지 않습니다.

전체 복사 또는 복사에 대한 더 많은 제어를 원하면 lodash 복제 기능이 있습니다.


2
Object.create지정된 프로토 타입을 사용하여 새 객체를 생성 하므로 const clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah). 또한 클래스 메서드도 복사됩니다.
barbatus

1
@barbatus 그래도 잘못된 프로토 타입을 사용합니다 Blah.prototype != instanceOfBlah. 당신은 사용해야합니다Object.getPrototypeOf(instanceOfBlah)
BERGI

1
@Bergi 아니요, ES6 클래스 인스턴스에 항상 프로토 타입이있는 것은 아닙니다. codepen.io/techniq/pen/qdZeZm 이 인스턴스에서도 작동하는지 확인하십시오 .
barbatus

@barbatus 죄송합니다. 나는 따르지 않는다. 모든 인스턴스에는 프로토 타입이 있으므로 인스턴스를 만듭니다. flori의 답변에서 코드를 시도하십시오.
Bergi

1
@Bergi 나는 그것이 Babel 구성이나 무언가에 달려 있다고 생각합니다. 현재 반응 형 네이티브 앱을 구현하고 있으며 상속 된 속성이없는 인스턴스에는 프로토 타입 null이 있습니다. 또한 여기에서 볼 수 있듯이 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… getPrototypeOf가 null을 반환 할 수 있습니다 .
barbatus

3

프로토 타입을 확장하는 것은 권장하지 않습니다. 코드 / 구성 요소를 테스트 할 때 문제가 발생합니다. 단위 테스트 프레임 워크는 자동으로 프로토 타입 확장을 가정하지 않습니다. 따라서 좋은 습관이 아닙니다. 여기에 프로토 타입 확장에 대한 더 많은 설명이 있습니다. 왜 네이티브 객체를 확장하는 것이 나쁜 습관입니까?

JavaScript에서 개체를 복제하는 방법은 간단하거나 간단하지 않습니다. 다음은 "Shallow Copy"를 사용하는 첫 번째 인스턴스입니다.

1-> 얕은 클론 :

class Employee {
    constructor(first, last, street) {
        this.firstName = first;
        this.lastName = last;
        this.address = { street: street };
    }

    logFullName() {
        console.log(this.firstName + ' ' + this.lastName);
    }
}

let original = new Employee('Cassio', 'Seffrin', 'Street A, 23');
let clone =  Object.assign({},original); //object.assing() method
let cloneWithPrototype Object.create(Object.getPrototypeOf(original)), original) //  the clone will inherit the prototype methods of the original.
let clone2 = { ...original }; // the same of object assign but shorter sintax using "spread operator"
clone.firstName = 'John';
clone.address.street = 'Street B, 99'; //will not be cloned

결과 :

original.logFullName () :

결과 : Cassio Seffrin

clone.logFullName () :

결과 : John Seffrin

original.address.street;

결과 : 'Street B, 99'// 원래 하위 개체가 변경되었음을 알 수 있습니다.

참고 : 인스턴스에 클로저가 자체 속성으로있는 경우이 메서드는 래핑하지 않습니다. ( 클로저에 대해 자세히 읽어보십시오. ) 또한 하위 개체 "address"는 복제되지 않습니다.

clone.logFullName ()

작동하지 않을 것이다.

cloneWithPrototype.logFullName ()

복제가 프로토 타입도 복사하기 때문에 작동합니다.

Object.assign을 사용하여 어레이를 복제하려면 :

let cloneArr = array.map((a) => Object.assign({}, a));

ECMAScript 스프레드 신택스를 사용하여 어레이 복제 :

let cloneArrSpread = array.map((a) => ({ ...a }));

2-> 딥 클론 :

완전히 새로운 객체 참조를 보관하기 위해 JSON.stringify ()를 사용하여 원래 객체를 문자열로 구문 분석 한 후 다시 JSON.parse ()로 구문 분석 할 수 있습니다.

let deepClone = JSON.parse(JSON.stringify(original));

딥 클론을 사용하면 주소에 대한 참조가 유지됩니다. 그러나 deepClone 프로토 타입이 손실되므로 deepClone.logFullName ()이 작동하지 않습니다.

3-> 타사 라이브러리 :

또 다른 옵션은 loadash 또는 underscore와 같은 타사 라이브러리를 사용하는 것입니다. 새 개체를 만들고 각 값을 원본에서 새 개체로 복사하여 참조를 메모리에 유지합니다.

밑줄 : let cloneUnderscore = _ (original) .clone ();

Loadash 복제 : var cloneLodash = _.cloneDeep (original);

lodash 또는 밑줄의 단점은 프로젝트에 추가 라이브러리를 포함해야한다는 것입니다. 그러나 그들은 좋은 옵션이며 또한 고성능 결과를 생성합니다.


1
에 할당 {}하면 복제본은 원본의 프로토 타입 메서드를 상속하지 않습니다. clone.logFullName()전혀 작동하지 않습니다. Object.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)당신은 전에 벌금이었다 한, 당신은 왜 그 변경 않았다?
Bergi

1
@Bergi는 귀하의 기여에 감사드립니다. 지금 내 대답을 편집하고 있었으며 프로토 타입을 복사하라는 귀하의 요점을 추가했습니다!
Cassio Seffrin

1
@Bergi의 도움에 감사드립니다. 지금 의견을 보내주십시오. 나는 에디션을 마쳤다. 이제 대답이 거의 모든 질문을 다룬 것 같습니다. Thks!
Cassio Seffrin

1
예, 마찬가지로 Object.assign({},original), 작동하지 않습니다.
Bergi

1
때로는 더 간단한 접근 방식 만 있으면됩니다. 당신은 프로토 타입 및 복잡한 객체 필요가없는 경우 수도 그냥 "클론 = {... 원래}"문제를 해결할 수
카시오 Seffrin

0

또 하나의 라이너 :

대부분의 경우 ... (Date, RegExp, Map, String, Number, Array에서 작동), btw, 복제 문자열, 숫자는 약간 재미 있습니다.

let clone = new obj.constructor(...[obj].flat())

복사 생성자가없는 클래스의 경우 :

let clone = Object.assign(new obj.constructor(...[obj].flat()), obj)

0
class A {
  constructor() {
    this.x = 1;
  }

  y() {
    return 1;
  }
}

const a = new A();

const output = Object.getOwnPropertyNames(Object.getPrototypeOf(a)).concat(Object.getOwnPropertyNames(a)).reduce((accumulator, currentValue, currentIndex, array) => {
  accumulator[currentValue] = a[currentValue];
  return accumulator;
}, {});

여기에 이미지 설명 입력


0

나는 거의 모든 대답을 좋아합니다. 이 문제를 해결하기 위해 clone()메서드 를 정의하여 수동으로 수행 하고 그 안에 전체 개체를 처음부터 빌드했습니다. 나에게 이것은 결과 객체가 복제 된 객체와 자연적으로 동일한 유형이기 때문에 의미가 있습니다.

타이프 스크립트를 사용한 예 :

export default class ClassName {
    private name: string;
    private anotherVariable: string;
   
    constructor(name: string, anotherVariable: string) {
        this.name = name;
        this.anotherVariable = anotherVariable;
    }

    public clone(): ClassName {
        return new ClassName(this.name, this.anotherVariable);
    }
}

나는이 솔루션이 더 '객체 지향적'으로 보이기 때문에 좋아합니다.


-3

예를 들어 Obj라는 객체를 복제하려는 경우 스프레드 연산자를 사용할 수 있습니다.

let clone = { ...obj};

복제 된 오브젝트를 변경하거나 추가하려면 다음을 수행하십시오.

let clone = { ...obj, change: "something" };
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.