Как скопировать объект и потерять его ссылку в Angular?
С AngularJS я могу использовать angular.copy(object)
, но я получаю некоторую ошибку, используя это в Angular.
ИСКЛЮЧЕНИЕ: ReferenceError:
angular
не определен
Как скопировать объект и потерять его ссылку в Angular?
С AngularJS я могу использовать angular.copy(object)
, но я получаю некоторую ошибку, используя это в Angular.
ИСКЛЮЧЕНИЕ: ReferenceError:
angular
не определен
Предполагая, что вы используете ES6, вы можете использовать var copy = Object.assign({}, original)
. Работает в современных браузерах; если вам нужно поддерживать старые браузеры, посмотрите polyfill
обновление:
С TypeScript 2.1+ доступно условное обозначение с расширением ES6:
const copy = { ...original }
Пока у нас есть лучшее решение, вы можете использовать следующее:
duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));
РЕДАКТИРОВАТЬ: Уточнение
Обратите внимание: вышеупомянутое решение предназначалось только для быстрого исправления одного лайнера, предоставляемого в то время, когда Angular 2 находилось в активной разработке. Я надеюсь, что в итоге мы получим эквивалент angular.copy()
. Поэтому я не хотел писать или импортировать библиотеку глубокого клонирования.
Этот метод также имеет проблемы с параметрами даты синтаксического анализа (он станет строкой).
Пожалуйста, не используйте этот метод в производственных приложениях. Используйте его только в своих экспериментальных проектах - те, которые вы делаете для обучения Angular 2.
Альтернативой для глубокого копирования объектов, содержащих вложенные объекты, является использование метода lodash cloneDeep.
Для Angular вы можете сделать это так:
Установить lodash с yarn add lodash
или npm install lodash
.
В свой компонент импортируйте cloneDeep
и используйте его:
import * as cloneDeep from 'lodash/cloneDeep';
...
clonedObject = cloneDeep(originalObject);
Это только 18 КБ, добавленных к вашей сборке, что хорошо для выгоды.
Я также написал статью здесь, если вам нужно больше понять, почему вы используете lodash cloneDeep.
Используйте lodash, как указано bertandg. Причина angular больше не имеет этого метода, потому что angular 1 была автономной структурой, а внешние библиотеки часто сталкивались с проблемами с контекстом выполнения angular. angular 2 не имеет этой проблемы, поэтому используйте любую библиотеку, которая вам нужна.
Для неглубокого копирования вы можете использовать Object.assign, который является функцией ES6
let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false
НЕ использовать его для глубокого клонирования
Если вы хотите скопировать экземпляр класса, вы также можете использовать 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 !
Самое простое решение, которое я нашел:
let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);
* ВАЖНЫЕ ШАГИ: Вы должны установить lodash, чтобы использовать это (что было непонятно из других ответов):
$ npm install --save lodash
$ npm install --save @types/lodash
а затем импортируйте его в файл ts:
import * as _ from "lodash";
Как уже отмечалось, использование 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.
}
Это то, что мы решили сделать.
Мне нужна была эта функция, просто формирующая "модели" моего приложения (необработанные внутренние данные, преобразованные в объекты). В итоге я использовал комбинацию Object.create (создать новый объект из указанного прототипа) и Object.assign (копировать свойства между объектами). Нужно обработать глубокую копию вручную. Я создал суть для этого.
Была такая же проблема, и я не хотел использовать какие-либо плагины только для глубокого клонирования:
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;
}
Кредиты: я сделал эту функцию более читабельной, пожалуйста, проверьте исключения из ее функциональности ниже
Я создал сервис для использования с Angular 5 или выше, он использует базу angularjs angular.copy()
, он хорошо работает для меня. Кроме того, есть другие функции, такие как 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);
}
}
Я, как и вы, столкнулся с проблемой работы angular.copy и angular.expect, потому что они не копируют объект и не создают объект без добавления некоторых зависимостей. Мое решение было таким:
copyFactory = (() ->
resource = ->
resource.__super__.constructor.apply this, arguments
return
this.extendTo resource
resource
).call(factory)
let newObj = JSON.parse(JSON.stringify(obj))
Метод JSON.stringify()
преобразует объект или значение JavaScript в строку JSON