Що альтернативно angular.copy у Angular


136

Як я можу скопіювати об’єкт і втратити його посилання в кутовий?

З AngularJS я можу використовувати angular.copy(object), але я отримую деяку помилку, використовуючи цю в Angular.

ВИХІД: ReferenceError: angularне визначено


Перевірте це рішення, можливо, це допоможе: Посилання
Nourdine Alouane

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

до речі, пов'язаних з (а також без відповіді): 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

у Веб-шторм я потрапив Unresolved function or method assign(); Деталі IDE: Веб-штурм 2016.2. Як я можу це вирішити?
mihai

2
@meorfi Перейти до File -> Settings -> Languages & Frameworks -> Javascriptі набір Javascript language versionдо ECMAScript 6.0.
Siri0S

@bertrandg _.clone (значення) відрізняється angular.copy (), він не буде створювати новий екземпляр, так _.cloneDeep (значення) він по- , як і раніше створює опорний stackoverflow.com/questions/26411754 / ...
Zealitude

5
Крім того, якщо ви копіюєте масив, використовуйте:const copy = [ ...original ]
daleyjem

43

Поки у нас не буде кращого рішення, ви можете використовувати наступне:

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

EDIT: Уточнення

Зверніть увагу: вищевказане рішення повинно було бути швидко виправленим одним вкладишем, яке було передбачено в той час, коли Angular 2 перебуває в активному розвитку. Я сподівався, що ми зрештою отримаємо еквівалент angular.copy(). Тому я не хотів писати чи імпортувати бібліотеку глибокого клонування.

Цей метод також має проблеми з властивостями дати розбору (він стане рядком).

Будь ласка, не використовуйте цей метод у виробничих додатках . Використовуйте його лише в своїх експериментальних проектах - тих, які ви робите для вивчення Angular 2.


11
це руйнує ваші побачення і повільно, як пекло.
LanderV

5
Не так повільно, як імпортувати цілу бібліотеку, щоб виконати одну задачу, хоча поки те, що ви робите, досить просте ...
Ian Belcher

1
це жахливо, ніколи цього не використовуй
Мурхаф Суслі

1
@MurhafSousli, будь ласка, спробуй зрозуміти контекст цієї відповіді. Це було надано, коли Angular 2 розроблявся, і сподівалося, що ми зрештою отримаємо еквівалент функції angular.copy (). Щоб скоротити час очікування, я виклав це рішення як тимчасовий варіант, поки у нас не буде кращого рішення. Це однолінійний з глибоким клонуванням. Це жахливо , я згоден ... Але, враховуючи експериментальний контекст на той час, це не так вже й погано.
Мані

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

22

Альтернативою для глибокого копіювання об'єктів, які мають вкладені об'єкти всередині, є використання методу cloneDeep lodash.

Для Angular ви можете це зробити так:

Встановіть lodash за допомогою yarn add lodashабоnpm install lodash .

Імпортуйте cloneDeepта використовуйте у своєму компоненті :

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

Це лише 18 кб додано до вашої збірки, що вартує переваг.

Тут я також написав статтю , якщо вам потрібно більше зрозуміти, чому використовується клонадаDeep для lodash.


2
"Усього 18 кбіт" додано до виводу, щоб просто можна було глибоко копіювати об'єкти? JavaScript - безлад.
Ендрю

Прочитавши вашу згадану статтю, я розумію, як cloneDeepметод створює новий об'єкт. Чи варто все-таки використовувати його, якщо у нас вже є об’єкт призначення?
Стефан

17

Для дрібного копіювання ви можете використовувати Object.assign, який є функцією ES6

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

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


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

15

Використовуйте лодаш, як зазначено в бертандгу. Причина кутового більше не має цього методу, тому що кутова 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

Як вже вказували інші, використання краватки або підкреслення - це, мабуть, найкраще рішення. Але якщо ці бібліотеки вам не потрібні ні для чого іншого, ви, ймовірно, можете використовувати щось подібне:

  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
Про це вже вичерпно сказано, що це найгірший спосіб лікування!
Алессандро

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.