Angular에서 angular.copy의 대안


136

Angular에서 객체를 복사하고 참조를 잃을 수 있습니까?

AngularJS에서는을 사용할 수 angular.copy(object)있지만 Angular에서 사용할 때 오류가 발생합니다.

EXCEPTION : ReferenceError : angular정의되지 않았습니다


이 솔루션을 확인하면 도움이 될 것입니다. 링크
Nourdine Alouane

많은 상황에서 사용하고 .copy()싶지만 실제로는 필요하지 않습니다. 내가 본 다양한 AngJS1 프로젝트에서, 그것은 관련 하위 구조의 수동 사본이 더 깔끔한 코드를 위해 만들었던 과잉이었다. 어쩌면 그것은 Angular 팀이 그것을 구현하지 않기로 한 결정의 일부였습니다.
phil294


답변:


180

ES6을 사용한다고 가정하면을 사용할 수 있습니다 var copy = Object.assign({}, original). 최신 브라우저에서 작동합니다. 구형 브라우저를 지원해야하는 경우이 polyfill을 확인하십시오

최신 정보:

TypeScript 2.1 이상에서는 ES6 속기 객체 스프레드 표기법을 사용할 수 있습니다.

const copy = { ...original }

75
angular.copy()와는 반대로 깊은 사본 을 만듭니다 Object.assign(). 당신은 깊은 복사를 사용 lodash를 원하는 경우 _.cloneDeep(value) lodash.com/docs#cloneDeep
bertrandg

Webstorm에서 나는 얻었다 Unresolved function or method assign(); IDE 세부 사항 : Webstorm 2016.2. 어떻게 해결할 수 있습니까?
mihai

2
@meorfi로 이동하여 File -> Settings -> Languages & Frameworks -> Javascript로 설정 Javascript language version하십시오 ECMAScript 6.0.
Siri0S

@bertrandg _.clone (value)은 angular.copy ()와 다르므로 새 인스턴스를 만들지 않으므로 _.cloneDeep (value)는 여전히 참조를 만듭니다. stackoverflow.com/questions/26411754/…
Zealitude

5
또한 배열을 복사하는 경우 다음을 사용하십시오.const copy = [ ...original ]
daleyjem

43

더 나은 솔루션이 나올 때까지 다음을 사용할 수 있습니다.

duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));

편집 : 설명

참고 : 위의 솔루션은 Angular 2가 개발중인 시점에 제공된 빠른 수정 1 라이너 일뿐입니다. 내 희망은 우리가 결국angular.copy() . 따라서 딥 복제 라이브러리를 쓰거나 가져오고 싶지 않았습니다.

이 방법에는 날짜 속성 구문 분석에 문제가 있습니다 (문자열이 됨).

프로덕션 앱에서는이 방법을 사용하지 마십시오 . Angular 2를 배우기 위해하고있는 실험 프로젝트에서만 사용하십시오.


11
이것은 당신의 데이트를 망치고 지옥만큼 느립니다.
LanderV

5
하나의 작업을 수행하기 위해 전체 라이브러리를 가져 오는 것만 큼 느리지는 않습니다. 여러분이하는 일이 아주 간단하다면 ...
Ian Belcher

1
이것은 끔찍합니다, 절대 사용하지 마십시오
Murhaf Sousli

1
@MurhafSousli이 답변의 내용을 이해하려고 노력하십시오. 이것은 Angular 2가 개발 중일 때 제공되었으며 결국 angular.copy () 함수와 동등한 기능을 얻을 수 있기를 바랍니다. 대기 시간을 줄이기 위해 더 나은 솔루션을 얻을 때까지이 솔루션을 임시 옵션으로 제시했습니다. 이것은 깊은 복제를 가진 1 개의 강선입니다. 이것은 끔찍하다 . 나는 동의한다. 그러나 그 당시의 실험적 맥락에서 볼 때, 그렇게 나쁘지 않다.
Mani

1
@ LazarLjubenović는 2018 년 물론입니다. 저는 오늘 당신과 완전히 동의 하지만 2016 년 웹 팩에는 나무가 흔들리지 않았으므로 대부분의 경우 전체 라이브러리를 가져옵니다.
Ian Belcher

22

내부에 중첩 된 객체가있는 객체를 딥 카피하는 대안은 lodash의 cloneDeep 메소드를 사용하는 것입니다.

Angular의 경우 다음과 같이 할 수 있습니다.

yarn add lodash또는로 lodash를 설치하십시오 npm install lodash.

컴포넌트에서 가져 와서 cloneDeep사용하십시오.

import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);

빌드에 18kb 만 추가되어 이점을 얻을 수 있습니다.

lodash의 cloneDeep을 사용하는 이유에 대한 더 많은 통찰력이 필요한 경우 여기기사를 작성했습니다 .


2
개체를 딥 카피 할 수 있도록 "18kb"만 출력에 추가 되었습니까? JavaScript는 혼란입니다.
Endrju

참조 된 기사를 읽은 후 cloneDeep메소드가 새 객체를 인스턴스화한다는 것을 이해 합니다. 대상 객체가 이미있는 경우에도 계속 사용해야합니까?
스테판

17

대한 얕은 ES6 기능은 당신이 Object.assign을 사용할 수 복사

let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false

딥 클로닝에는 사용하지 마십시오


3
딥 클로닝에 사용할 수있는 것은 무엇입니까?
DB

15

bertandg가 지시 한대로 lodash를 사용하십시오. 각도가 더 이상이 방법을 갖지 않는 이유는 각도 1이 독립형 프레임 워크이고 외부 라이브러리가 종종 각도 실행 컨텍스트에 문제가 있기 때문입니다. Angular 2에는 그러한 문제가 없으므로 원하는 라이브러리를 사용하십시오.

https://lodash.com/docs#cloneDeep


8

클래스 인스턴스를 복사하려면 Object.assign도 사용할 수 있지만 새 인스턴스를 {} 대신 첫 번째 매개 변수로 전달해야합니다.

class MyClass {
    public prop1: number;
    public prop2: number;

    public summonUnicorn(): void {
        alert('Unicorn !');
    }
}

let instance = new MyClass();
instance.prop1 = 12;
instance.prop2 = 42;

let wrongCopy = Object.assign({}, instance);
console.log(wrongCopy.prop1); // 12
console.log(wrongCopy.prop2); // 42
wrongCopy.summonUnicorn() // ERROR : undefined is not a function

let goodCopy = Object.assign(new MyClass(), instance);
console.log(goodCopy.prop1); // 12
console.log(goodCopy.prop2); // 42
goodCopy.summonUnicorn() // It works !

8

내가 찾은 가장 간단한 해결책은 다음과 같습니다.

let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

* 중요 단계 : 이것을 사용하려면 lodash를 설치해야합니다 (다른 답변과 명확하지 않음).

$ npm install --save lodash

$ npm install --save @types/lodash

그런 다음 ts 파일로 가져옵니다.

import * as _ from "lodash";

7

다른 사람들이 이미 지적했듯이 lodash 또는 밑줄을 사용하는 것이 아마도 가장 좋은 해결책 일 것입니다. 그러나 다른 라이브러리가 필요하지 않은 경우 다음과 같이 사용할 수 있습니다.

  function deepClone(obj) {

    // return value is input is not an Object or Array.
    if (typeof(obj) !== 'object' || obj === null) {
      return obj;    
    }

    let clone;

    if(Array.isArray(obj)) {
      clone = obj.slice();  // unlink Array reference.
    } else {
      clone = Object.assign({}, obj); // Unlink Object reference.
    }

    let keys = Object.keys(clone);

    for (let i=0; i<keys.length; i++) {
      clone[keys[i]] = deepClone(clone[keys[i]]); // recursively unlink reference to nested objects.
    }

    return clone; // return unlinked clone.

  }

그것이 우리가 결정한 것입니다.


1
// 날짜를 연결 해제하기 위해 추가 할 수 있습니다. if (Object.prototype.toString.call (obj) === '[object Date]') {return new Date (obj.getTime ()); }
A_J

1
또는 인스턴스 유형을 사용하여 날짜 확인-if (obj instanceof Date) {return new Date (obj.getTime ())}
Anoop Isaac

0

이 기능이 내 앱 '모델'(원시 백엔드 데이터를 객체로 변환)을 구성하는 데 필요했습니다. 그래서 Object.create (지정된 프로토 타입에서 새 객체 만들기)와 Object.assign (객체 간 속성 복사 )을 조합하여 사용했습니다 . 딥 카피를 수동으로 처리해야합니다. 나는 이것을 위해 요지 를 만들었다 .


0

같은 문제가 있었고 딥 클로닝을 위해 플러그인을 사용하고 싶지 않았습니다.

static deepClone(object): any {
        const cloneObj = (<any>object.constructor());
        const attributes = Object.keys(object);
        for (const attribute of attributes) {
            const property = object[attribute];

            if (typeof property === 'object') {
                cloneObj[attribute] = this.deepClone(property);
            } else {
                cloneObj[attribute] = property;
            }
        }
        return cloneObj;
    }

크레딧 : 이 기능을 더 읽기 쉽게 만들었습니다 . 아래 기능에 대한 예외 사항을 확인하십시오.


0

Angular 5 이상에서 사용할 서비스를 만들었습니다 angular.copy ().angularjs 의 기본을 사용하며 나에게 잘 작동합니다. 또한와 같은 다른 기능 isUndefined이 있습니다. 도움이되기를 바랍니다. 다른 최적화와 마찬가지로 알고 있으면 좋을 것입니다. 문안 인사

import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class AngularService {

  private TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
  private stackSource = [];
  private stackDest = [];

  constructor() { }

  public isNumber(value: any): boolean {
    if ( typeof value === 'number' ) { return true; }
    else { return false; }
  }

  public isTypedArray(value: any) {
    return value && this.isNumber(value.length) && this.TYPED_ARRAY_REGEXP.test(toString.call(value));
  }

  public isArrayBuffer(obj: any) {
    return toString.call(obj) === '[object ArrayBuffer]';
  }

  public isUndefined(value: any) {return typeof value === 'undefined'; }

  public isObject(value: any) {  return value !== null && typeof value === 'object'; }

  public isBlankObject(value: any) {
    return value !== null && typeof value === 'object' && !Object.getPrototypeOf(value);
  }

  public isFunction(value: any) { return typeof value === 'function'; }

  public setHashKey(obj: any, h: any) {
    if (h) { obj.$$hashKey = h; }
    else { delete obj.$$hashKey; }
  }

  private isWindow(obj: any) { return obj && obj.window === obj; }

  private isScope(obj: any) { return obj && obj.$evalAsync && obj.$watch; }


  private copyRecurse(source: any, destination: any) {

    const h = destination.$$hashKey;

    if (Array.isArray(source)) {
      for (let i = 0, ii = source.length; i < ii; i++) {
        destination.push(this.copyElement(source[i]));
      }
    } else if (this.isBlankObject(source)) {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    } else if (source && typeof source.hasOwnProperty === 'function') {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    } else {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    }
    this.setHashKey(destination, h);
    return destination;
  }

  private copyElement(source: any) {

    if (!this.isObject(source)) {
      return source;
    }

    const index = this.stackSource.indexOf(source);

    if (index !== -1) {
      return this.stackDest[index];
    }

    if (this.isWindow(source) || this.isScope(source)) {
      throw console.log('Cant copy! Making copies of Window or Scope instances is not supported.');
    }

    let needsRecurse = false;
    let destination = this.copyType(source);

    if (destination === undefined) {
      destination = Array.isArray(source) ? [] : Object.create(Object.getPrototypeOf(source));
      needsRecurse = true;
    }

    this.stackSource.push(source);
    this.stackDest.push(destination);

    return needsRecurse
      ? this.copyRecurse(source, destination)
      : destination;
  }

  private copyType = (source: any) => {

    switch (toString.call(source)) {
      case '[object Int8Array]':
      case '[object Int16Array]':
      case '[object Int32Array]':
      case '[object Float32Array]':
      case '[object Float64Array]':
      case '[object Uint8Array]':
      case '[object Uint8ClampedArray]':
      case '[object Uint16Array]':
      case '[object Uint32Array]':
        return new source.constructor(this.copyElement(source.buffer), source.byteOffset, source.length);

      case '[object ArrayBuffer]':
        if (!source.slice) {
          const copied = new ArrayBuffer(source.byteLength);
          new Uint8Array(copied).set(new Uint8Array(source));
          return copied;
        }
        return source.slice(0);

      case '[object Boolean]':
      case '[object Number]':
      case '[object String]':
      case '[object Date]':
        return new source.constructor(source.valueOf());

      case '[object RegExp]':
        const re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
        re.lastIndex = source.lastIndex;
        return re;

      case '[object Blob]':
        return new source.constructor([source], {type: source.type});
    }

    if (this.isFunction(source.cloneNode)) {
      return source.cloneNode(true);
    }
  }

  public copy(source: any, destination?: any) {

    if (destination) {
      if (this.isTypedArray(destination) || this.isArrayBuffer(destination)) {
        throw console.log('Cant copy! TypedArray destination cannot be mutated.');
      }
      if (source === destination) {
        throw console.log('Cant copy! Source and destination are identical.');
      }

      if (Array.isArray(destination)) {
        destination.length = 0;
      } else {
        destination.forEach((value: any, key: any) => {
          if (key !== '$$hashKey') {
            delete destination[key];
          }
        });
      }

      this.stackSource.push(source);
      this.stackDest.push(destination);
      return this.copyRecurse(source, destination);
    }

    return this.copyElement(source);
  }
}


0

angular.copy 및 angular.expect 작업에 문제가 발생했습니다. 이들은 객체를 복사하거나 종속성을 추가하지 않고 객체를 만들지 않기 때문입니다. 내 해결책은 다음과 같습니다.

  copyFactory = (() ->
    resource = ->
      resource.__super__.constructor.apply this, arguments
      return
    this.extendTo resource
    resource
  ).call(factory)

0
let newObj = JSON.parse(JSON.stringify(obj))

그만큼 JSON.stringify() 메소드는 JavaScript 객체 또는 값을 JSON 문자열로 변환합니다


2
이것은 이미 이것을 치료하는 최악의 방법이라고 철저히 언급되었습니다!
Alessandro

0

배열을 다음과 같이 복제 할 수 있습니다

 this.assignCustomerList = Object.assign([], this.customerList);

그리고 같은 개체를 복제

this.assignCustomer = Object.assign({}, this.customer);

0

lodash를 아직 사용하지 않는 경우이 방법으로 설치하지 않는 것이 좋습니다. 대신 'clone'과 같이 더 좁은 전문 라이브러리를 제안합니다.

npm install clone
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.