Какая альтернатива angular.copy в Angular


136

Как я могу скопировать объект и потерять ссылку на него в Angular?

Я могу использовать AngularJS, angular.copy(object)но получаю некоторую ошибку, используя это в Angular.

ИСКЛЮЧЕНИЕ: ReferenceError: angularне определено


Проверьте это решение, оно может помочь: Ссылка
Nourdine Alouane

Во многих ситуациях вы можете захотеть использовать, .copy()но на самом деле это не понадобится. В различных проектах AngJS1, которые я видел, это было излишним, когда для более чистого кода было бы сделано ручное копирование соответствующих подструктур. Возможно, это было частью решения не реализовывать его командой Angular.
phil294 04

кстати, связанные (и тоже без ответа): stackoverflow.com/questions/41124528/…
phil294 04

Ответы:


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 08

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 находился в активной разработке. Я надеялся, что в конце концов мы сможем получить эквивалент angular.copy(). Поэтому я не хотел писать или импортировать библиотеку глубокого клонирования.

У этого метода также есть проблемы с анализом свойств даты (он станет строкой).

Пожалуйста, не используйте этот метод в рабочих приложениях . Используйте его только в своих экспериментальных проектах - тех, которые вы делаете для изучения Angular 2.


11
это портит ваши свидания и идет чертовски медленно.
LanderV

5
Не так медленно, как импорт всей библиотеки для выполнения одной задачи, если то, что вы делаете, довольно просто ...
Ян Белчер,

1
это ужасно, никогда не используйте это
Мурхаф Сусли

1
@MurhafSousli, пожалуйста, постарайтесь понять контекст этого ответа. Это было предусмотрено, когда Angular 2 находился в стадии разработки, и мы надеялись, что в конечном итоге мы получим эквивалент функции angular.copy (). Чтобы сократить период ожидания, я предлагаю это решение в качестве временного варианта, пока у нас не будет лучшего решения. Это однострочник с глубоким клонированием. Я согласен, это ужасно ... Но, учитывая экспериментальный контекст того времени, это не так уж и плохо.
Mani

1
@ LazarLjubenović, конечно, в 2018 году это так, и я полностью согласен с вами сегодня , но в 2016 году в веб-пакете не было дрожания дерева, поэтому в большинстве случаев вы импортировали бы целую библиотеку.
Ян Белчер

22

Альтернативой глубокому копированию объектов, содержащих вложенные объекты, является использование метода lodash cloneDeep.

Для Angular это можно сделать так:

Установите lodash с помощью yarn add lodashили npm install lodash.

В своем компоненте импортируйте cloneDeepи используйте его:

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

В вашу сборку добавлено всего 18 КБ, что стоит того.

Я также написал здесь статью , если вам нужно больше информации о том, зачем использовать lodash cloneDeep.


2
"только 18kb" добавлено к выводу, чтобы просто иметь возможность глубокого копирования объектов? JavaScript - это беспорядок.
Эндрю

После прочтения указанной статьи я понял, что cloneDeepметод создает экземпляр нового объекта. Стоит ли нам по-прежнему использовать его, если у нас уже есть целевой объект?
Стефан

17

Для неглубокого копирования вы можете использовать Object.assign, который является функцией ES6.

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

НЕ используйте его для глубокого клонирования


3
Что можно использовать для глубокого клонирования?
DB

15

Используйте lodash, как указано bertandg. Причина, по которой 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 07

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, я бы не рекомендовал устанавливать его только для этого метода. Вместо этого я предлагаю более узкую специализированную библиотеку, такую ​​как «клон»:

npm install clone
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.