Какая альтернатива 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.