W wielu sytuacjach możesz chcieć użyć, .copy()ale w rzeczywistości nie potrzebujesz go. W różnych projektach AngJS1, które widziałem, była to przesada, w której ręczna kopia odpowiednich podstruktur byłaby zrobiona dla czystszego kodu. Może to była część decyzji o nie wdrożeniu go przez zespół Angular.
Zakładając, że używasz ES6, możesz użyć var copy = Object.assign({}, original). Działa w nowoczesnych przeglądarkach; jeśli potrzebujesz obsługi starszych przeglądarek, sprawdź ten polyfill
aktualizacja:
W przypadku języka TypeScript 2.1+ dostępna jest skrócona notacja rozprzestrzeniania obiektów w ES6:
Zauważ, że angular.copy()tworzy głęboką kopię w przeciwieństwie do Object.assign(). Jeśli chcesz głęboką kopię, użyj lodash _.cloneDeep(value)lodash.com/docs#cloneDeep
bertrandg
w Webstorm dostałem Unresolved function or method assign(); Szczegóły IDE: Webstorm 2016.2. Jak mogę to rozwiązać?
mihai,
2
@meorfi Idź do File -> Settings -> Languages & Frameworks -> Javascripti ustaw Javascript language versionna ECMAScript 6.0.
Siri0S,
@bertrandg _.clone (value) różni się od angular.copy (), nie utworzy nowej instancji, więc jako _.cloneDeep (wartość) nadal tworzy referencję stackoverflow.com/questions/26411754/ ...
Zealitude
5
Dodatkowo, jeśli kopiujesz tablicę, użyj:const copy = [ ...original ]
daleyjem
43
Dopóki nie mamy lepszego rozwiązania, możesz skorzystać z:
Uwaga: powyższe rozwiązanie miało być tylko jednym szybkim rozwiązaniem, dostarczonym w czasie, gdy Angular 2 był aktywnie rozwijany. Miałem nadzieję, że w końcu uda nam się uzyskać odpowiednik angular.copy(). Dlatego nie chciałem pisać ani importować biblioteki do głębokiego klonowania.
Ta metoda ma również problemy z analizowaniem właściwości daty (stanie się ciągiem znaków).
Nie używaj tej metody w aplikacjach produkcyjnych . Używaj go tylko w swoich eksperymentalnych projektach - tych, które robisz do nauki Angulara 2.
Nie tak powolne, jak importowanie całej biblioteki w celu wykonania jednego zadania, o ile to, co robisz, jest dość proste ...
Ian Belcher
1
to jest okropne, nigdy tego nie używaj
Murhaf Sousli
2
@MurhafSousli, spróbuj zrozumieć kontekst tej odpowiedzi. Zostało to zapewnione, gdy Angular 2 był w fazie rozwoju, i mieliśmy nadzieję, że ostatecznie otrzymamy odpowiednik funkcji angular.copy (). Aby skrócić okres oczekiwania, przedstawiłem to rozwiązanie jako opcję tymczasową, dopóki nie będziemy mieli lepszego rozwiązania. Jest to jednolinijkowy z głębokim klonowaniem. To okropne , zgadzam się ... Ale biorąc pod uwagę kontekst eksperymentalny w tamtym czasie, nie jest tak źle.
Mani
1
@ LazarLjubenović oczywiście tak jest w 2018 roku i dzisiaj całkowicie się z tobą zgadzam , ale w 2016 webpack nie potrząsał drzewem, więc w większości przypadków importowałbyś całą bibliotekę.
Ian Belcher,
24
Alternatywą dla głębokiego kopiowania obiektów z zagnieżdżonymi obiektami jest użycie metody cloneDeep lodash.
W przypadku Angulara możesz to zrobić w następujący sposób:
Zainstaluj lodash za pomocą yarn add lodashlub npm install lodash.
W swoim komponencie zaimportuj go cloneDeepi użyj:
import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);
Do kompilacji zostało dodane tylko 18 KB, co jest warte korzyści.
Napisałem tu również artykuł , jeśli potrzebujesz więcej informacji na temat tego, dlaczego używasz cloneDeep lodash.
„tylko 18kb” dodane do wyniku, aby móc po prostu kopiować obiekty? JavaScript to bałagan.
Endrju
Po przeczytaniu artykułu, do którego się odwołujesz, zrozumiałem, że cloneDeepmetoda tworzy instancję nowego obiektu. Czy nadal powinniśmy go używać, jeśli mamy już obiekt docelowy?
Stephane
18
Do płytkiego kopiowania możesz użyć Object. assign, który jest funkcją ES6
let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false
Użyj lodash zgodnie ze wskazaniami bertandg. Powodem, dla którego angular nie ma już tej metody, jest to, że angular 1 był samodzielnym szkieletem, a biblioteki zewnętrzne często napotykały problemy z kontekstem wykonania kątowego. Angular 2 nie ma tego problemu, więc użyj dowolnej biblioteki.
Jeśli chcesz skopiować instancję klasy, możesz również użyć Object. assign, ale musisz przekazać nową instancję jako pierwszy parametr (zamiast {}):
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 !
Tworzy to ostrzeżenie w Angular 10: OSTRZEŻENIE w /.../test.component.ts zależy od „lodash”. Zależności CommonJS lub AMD mogą powodować bailouty optymalizacji.
Adrian Madaras
7
Jak już inni zauważyli, użycie lodash lub podkreślenia jest prawdopodobnie najlepszym rozwiązaniem. Ale jeśli nie potrzebujesz tych bibliotek do niczego innego, prawdopodobnie możesz użyć czegoś takiego:
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.
}
// aby odłączyć daty, możemy dodać: if (Object.prototype.toString.call (obj) === '[object Date]') {return new Date (obj.getTime ()); }
A_J,
1
lub sprawdź datę przy użyciu typu instancji - if (obj instanceof Date) {return new Date (obj.getTime ())}
Anoop Isaac
0
Potrzebowałem tej funkcji po prostu z „modeli” mojej aplikacji (surowe dane zaplecza przekonwertowane na obiekty). Więc skończyło się za pomocą kombinacji Object.create (tworzenie nowego obiektu z określonego prototypu) oraz Object.assign (Kopiowanie właściwości między obiektami). Musisz ręcznie obsługiwać głęboką kopię. Stworzyłem do tego sedno .
Ja, jak ty, stanęliśmy przed problemem pracy kątowej. Kopie i kątowe. Oczekujemy, ponieważ nie kopiują obiektu ani nie tworzą obiektu bez dodawania pewnych zależności. Moje rozwiązanie było takie:
Jest to błędne, ponieważ angularjs.copy obsługuje funkcję deepCopy. Dlatego twoja odpowiedź jest myląca, ponieważ nie obsługuje funkcji DeepClone obiektu.
Rambou
0
Jeśli jeszcze nie używasz lodash, nie polecałbym instalowania go tylko dla tej jednej metody. Proponuję zamiast tego bardziej wyspecjalizowaną bibliotekę, taką jak „clone”:
Stworzyłem usługę do użytku z Angular 5 lub nowszym, korzysta angular.copy()z bazy angularjs, działa dobrze dla mnie. Dodatkowo są inne funkcje, takie jak isUndefineditp. Mam nadzieję, że to pomoże. Jak w przypadku każdej optymalizacji, dobrze byłoby wiedzieć. pozdrowienia
import { Injectable } from'@angular/core';
@Injectable({providedIn: 'root'})
exportclassAngularService{
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' ) { returntrue; }
else { returnfalse; }
}
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) {returntypeof 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) { returntypeof 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]));
}
} elseif (this.isBlankObject(source)) {
for (const key ofObject.keys(source)) {
destination[key] = this.copyElement(source[key]);
}
} elseif (source && typeof source.hasOwnProperty === 'function') {
for (const key ofObject.keys(source)) {
destination[key] = this.copyElement(source[key]);
}
} else {
for (const key ofObject.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) {
returnthis.stackDest[index];
}
if (this.isWindow(source) || this.isScope(source)) {
throwconsole.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]':
returnnew source.constructor(this.copyElement(source.buffer), source.byteOffset, source.length);
case'[object ArrayBuffer]':
if (!source.slice) {
const copied = newArrayBuffer(source.byteLength);
newUint8Array(copied).set(newUint8Array(source));
return copied;
}
return source.slice(0);
case'[object Boolean]':
case'[object Number]':
case'[object String]':
case'[object Date]':
returnnew source.constructor(source.valueOf());
case'[object RegExp]':
const re = newRegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
re.lastIndex = source.lastIndex;
return re;
case'[object Blob]':
returnnew 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)) {
throwconsole.log('Cant copy! TypedArray destination cannot be mutated.');
}
if (source === destination) {
throwconsole.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);
returnthis.copyRecurse(source, destination);
}
returnthis.copyElement(source);
}
}
.copy()
ale w rzeczywistości nie potrzebujesz go. W różnych projektach AngJS1, które widziałem, była to przesada, w której ręczna kopia odpowiednich podstruktur byłaby zrobiona dla czystszego kodu. Może to była część decyzji o nie wdrożeniu go przez zespół Angular.Odpowiedzi:
Zakładając, że używasz ES6, możesz użyć
var copy = Object.assign({}, original)
. Działa w nowoczesnych przeglądarkach; jeśli potrzebujesz obsługi starszych przeglądarek, sprawdź ten polyfillaktualizacja:
W przypadku języka TypeScript 2.1+ dostępna jest skrócona notacja rozprzestrzeniania obiektów w ES6:
const copy = { ...original }
źródło
angular.copy()
tworzy głęboką kopię w przeciwieństwie doObject.assign()
. Jeśli chcesz głęboką kopię, użyj lodash_.cloneDeep(value)
lodash.com/docs#cloneDeepUnresolved function or method assign()
; Szczegóły IDE: Webstorm 2016.2. Jak mogę to rozwiązać?File -> Settings -> Languages & Frameworks -> Javascript
i ustawJavascript language version
naECMAScript 6.0
.const copy = [ ...original ]
Dopóki nie mamy lepszego rozwiązania, możesz skorzystać z:
EDYCJA: wyjaśnienie
Uwaga: powyższe rozwiązanie miało być tylko jednym szybkim rozwiązaniem, dostarczonym w czasie, gdy Angular 2 był aktywnie rozwijany. Miałem nadzieję, że w końcu uda nam się uzyskać odpowiednik
angular.copy()
. Dlatego nie chciałem pisać ani importować biblioteki do głębokiego klonowania.Ta metoda ma również problemy z analizowaniem właściwości daty (stanie się ciągiem znaków).
Nie używaj tej metody w aplikacjach produkcyjnych . Używaj go tylko w swoich eksperymentalnych projektach - tych, które robisz do nauki Angulara 2.
źródło
Alternatywą dla głębokiego kopiowania obiektów z zagnieżdżonymi obiektami jest użycie metody cloneDeep lodash.
W przypadku Angulara możesz to zrobić w następujący sposób:
Zainstaluj lodash za pomocą
yarn add lodash
lubnpm install lodash
.W swoim komponencie zaimportuj go
cloneDeep
i użyj:Do kompilacji zostało dodane tylko 18 KB, co jest warte korzyści.
Napisałem tu również artykuł , jeśli potrzebujesz więcej informacji na temat tego, dlaczego używasz cloneDeep lodash.
źródło
cloneDeep
metoda tworzy instancję nowego obiektu. Czy nadal powinniśmy go używać, jeśli mamy już obiekt docelowy?Do płytkiego kopiowania możesz użyć Object. assign, który jest funkcją ES6
NIE używaj go do głębokiego klonowania
źródło
Użyj lodash zgodnie ze wskazaniami bertandg. Powodem, dla którego angular nie ma już tej metody, jest to, że angular 1 był samodzielnym szkieletem, a biblioteki zewnętrzne często napotykały problemy z kontekstem wykonania kątowego. Angular 2 nie ma tego problemu, więc użyj dowolnej biblioteki.
https://lodash.com/docs#cloneDeep
źródło
Jeśli chcesz skopiować instancję klasy, możesz również użyć Object. assign, ale musisz przekazać nową instancję jako pierwszy parametr (zamiast {}):
źródło
Najprostsze rozwiązanie, które znalazłem, to:
* WAŻNE KROKI: Musisz zainstalować lodash, aby użyć tego (co było niejasne z innych odpowiedzi):
a następnie zaimportuj go do swojego pliku ts:
źródło
Jak już inni zauważyli, użycie lodash lub podkreślenia jest prawdopodobnie najlepszym rozwiązaniem. Ale jeśli nie potrzebujesz tych bibliotek do niczego innego, prawdopodobnie możesz użyć czegoś takiego:
To właśnie postanowiliśmy zrobić.
źródło
Potrzebowałem tej funkcji po prostu z „modeli” mojej aplikacji (surowe dane zaplecza przekonwertowane na obiekty). Więc skończyło się za pomocą kombinacji Object.create (tworzenie nowego obiektu z określonego prototypu) oraz Object.assign (Kopiowanie właściwości między obiektami). Musisz ręcznie obsługiwać głęboką kopię. Stworzyłem do tego sedno .
źródło
Miałem ten sam problem i nie chciałem używać żadnych wtyczek tylko do głębokiego klonowania:
Kredyty: sprawiłem, że ta funkcja jest bardziej czytelna , sprawdź poniżej wyjątki od jej funkcjonalności
źródło
Ja, jak ty, stanęliśmy przed problemem pracy kątowej. Kopie i kątowe. Oczekujemy, ponieważ nie kopiują obiektu ani nie tworzą obiektu bez dodawania pewnych zależności. Moje rozwiązanie było takie:
źródło
JSON.stringify()
Metoda konwertuje obiekt JavaScript lub wartość na ciąg JSONźródło
Możesz sklonować Array jak
I sklonuj obiekt jak
źródło
Jeśli jeszcze nie używasz lodash, nie polecałbym instalowania go tylko dla tej jednej metody. Proponuję zamiast tego bardziej wyspecjalizowaną bibliotekę, taką jak „clone”:
źródło
Stworzyłem usługę do użytku z Angular 5 lub nowszym, korzysta
angular.copy()
z bazy angularjs, działa dobrze dla mnie. Dodatkowo są inne funkcje, takie jakisUndefined
itp. Mam nadzieję, że to pomoże. Jak w przypadku każdej optymalizacji, dobrze byłoby wiedzieć. pozdrowieniaimport { 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); } }
źródło