Angularのangular.copyに代わるものは何ですか


136

Angularでオブジェクトをコピーしてその参照を失うにはどうすればよいですか?

AngularJSでは、を使用できますangular.copy(object)が、Angularでそれを使用するとエラーが発生します。

例外:ReferenceError:angular定義されていません


このソリューションが役立つ可能性があることを確認してください:リンク
Nourdine Alouane 2017年

多くの状況では、使用したいかもしれません.copy()が、実際には必要ありません。私が見たさまざまなAngJS1プロジェクトでは、関連する下位構造を手動でコピーすることで、コードをより簡潔にすることができました。たぶん、それはAngularチームが実装しないという決定の一部でした。
phil294 2017

方法、関連する(及び未応答)によって:stackoverflow.com/questions/41124528/...
phil294

回答:


180

ES6を使用している場合は、を使用できますvar copy = Object.assign({}, original)。最新のブラウザで動作します。古いブラウザをサポートする必要がある場合は、このポリフィルをチェックしてください

更新:

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 2016

2
@meorfiに移動しFile -> Settings -> Languages & Frameworks -> Javascriptてに設定Javascript language versionECMAScript 6.0ます。
Siri0S 2016

@bertrandg _.clone(値)は、それが_.cloneDeep(値)が依然として基準作成するように、新しいインスタンスを作成しませんangular.copy()とは異なる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 2016年

5
ただし、ライブラリ全体をインポートして1つのタスクを実行するほど遅くはありませんが、実行している作業が非常に単純である限りは…
Ian Belcher

1
これは恐ろしいです。決して使用しないでください
Murhaf Sousli

1
@MurhafSousliこの回答のコンテキストを理解してみてください。これは、Angular 2の開発中に提供されたものであり、最終的にはangular.copy()関数と同等の機能が得られることが期待されていました。待機期間を埋めるために、私はこのソリューションを一時的な選択肢として出し、より良いソリューションを手に入れるまでにしました。これは、詳細なクローニングを備えたワンライナーです。これは恐ろしいことです、しかし私は同意します...しかし、その時の実験的状況を考えると、それほど悪くはありません。
Mani

1
もちろん、@LazarLjubenovićは2018年のことであり、今日も完全に同意しますが、2016年のWebpackではツリーの揺れがなかったため、ほとんどの場合、ライブラリ全体をインポートします。
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を使用します。angularにこのメソッドがなくなった理由は、angular 1がスタンドアロンのフレームワークであり、外部ライブラリが度々Angular実行コンテキストの問題に遭遇したためです。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 2017年

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
これは、これがそれを治療するための最悪の方法であるとすでに十分に言われています!
アレッサンドロ

0

次のように配列を複製できます

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

次のようにオブジェクトを複製します

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

0

lodashをまだ使用していない場合は、この1つの方法だけでインストールすることはお勧めしません。代わりに、「クローン」などのより限定的なライブラリをお勧めします。

npm install clone
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.