ฉันจะคัดลอกวัตถุและสูญเสียการอ้างอิงใน Angular ได้อย่างไร
ด้วย AngularJS ฉันสามารถใช้ได้angular.copy(object)
แต่ฉันได้รับข้อผิดพลาดในการใช้ Angular
EXCEPTION: ReferenceError:
angular
ไม่ได้กำหนดไว้
ฉันจะคัดลอกวัตถุและสูญเสียการอ้างอิงใน Angular ได้อย่างไร
ด้วย AngularJS ฉันสามารถใช้ได้angular.copy(object)
แต่ฉันได้รับข้อผิดพลาดในการใช้ Angular
EXCEPTION: ReferenceError:
angular
ไม่ได้กำหนดไว้
.copy()
แต่จริงๆแล้วไม่จำเป็นต้องใช้ ในโครงการ AngJS1 ต่างๆที่ฉันเคยเห็นมันเป็นการโอเวอร์คิลซึ่งจะมีการสร้างสำเนาโครงสร้างย่อยที่เกี่ยวข้องด้วยตนเองเพื่อรหัสที่สะอาดกว่า บางทีนั่นอาจเป็นส่วนหนึ่งของการตัดสินใจที่จะไม่ใช้งานโดยทีม Angular
คำตอบ:
สมมติว่าคุณกำลังใช้ ES6 var copy = Object.assign({}, original)
คุณสามารถใช้ ทำงานในเบราว์เซอร์ที่ทันสมัย หากคุณต้องการรองรับเบราว์เซอร์รุ่นเก่าลองดูpolyfillนี้
ปรับปรุง:
ด้วย TypeScript 2.1+ สามารถใช้สัญกรณ์การแพร่กระจายวัตถุชวเลข ES6 ได้:
const copy = { ...original }
angular.copy()
Object.assign()
หากคุณต้องการสำเนาลึกให้ใช้ lodash _.cloneDeep(value)
lodash.com/docs#cloneDeep
Unresolved function or method assign()
; รายละเอียด IDE: Webstorm 2016.2 ฉันจะแก้ไขได้อย่างไร
File -> Settings -> Languages & Frameworks -> Javascript
และตั้งค่าJavascript language version
เป็นECMAScript 6.0
.
const copy = [ ...original ]
จนกว่าเราจะมีทางออกที่ดีกว่าคุณสามารถใช้สิ่งต่อไปนี้:
duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));
แก้ไข: ชี้แจง
โปรดทราบ: วิธีแก้ปัญหาข้างต้นมีไว้เพื่อแก้ไขปัญหาอย่างรวดเร็วหนึ่งซับซึ่งจัดทำขึ้นในช่วงเวลาที่ Angular 2 อยู่ระหว่างการพัฒนา angular.copy()
ความหวังของฉันคือในที่สุดเราก็อาจได้รับเทียบเท่า ดังนั้นฉันจึงไม่ต้องการเขียนหรือนำเข้าไลบรารีการโคลนแบบเจาะลึก
วิธีนี้ยังมีปัญหากับคุณสมบัติวันที่แยกวิเคราะห์ (ซึ่งจะกลายเป็นสตริง)
โปรดอย่าใช้วิธีนี้ในแอปที่ใช้งานจริง ใช้เฉพาะในโครงการทดลองของคุณ - โครงการที่คุณกำลังทำเพื่อเรียนรู้ Angular 2
อีกทางเลือกหนึ่งสำหรับการคัดลอกวัตถุแบบลึกที่มีวัตถุซ้อนอยู่ภายในคือการใช้วิธี cloneDeep ของ lodash
สำหรับ Angular คุณสามารถทำได้ดังนี้:
ติดตั้ง lodash ด้วยyarn add lodash
หรือnpm install lodash
.
ในส่วนประกอบของคุณนำเข้าcloneDeep
และใช้งาน:
import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);
เพิ่มเข้าไปในงานสร้างของคุณเพียง 18kb คุ้มค่ากับผลประโยชน์
ฉันได้เขียนบทความไว้ที่นี่ด้วยหากคุณต้องการข้อมูลเชิงลึกเพิ่มเติมว่าเหตุใดจึงใช้ cloneDeep ของ lodash
cloneDeep
วิธีการสร้างอินสแตนซ์วัตถุใหม่ เราควรใช้มันหรือไม่ถ้าเรามีวัตถุปลายทางอยู่แล้ว?
สำหรับการคัดลอกแบบตื้นคุณสามารถใช้ Object.assign ซึ่งเป็นคุณสมบัติ ES6
let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false
อย่าใช้สำหรับการโคลนลึก
ใช้ lodash ตามที่ bertandg ระบุ เหตุผลที่เชิงมุมไม่มีวิธีนี้อีกต่อไปเนื่องจากเชิงมุม 1 เป็นเฟรมเวิร์กแบบสแตนด์อะโลนและไลบรารีภายนอกมักพบปัญหาเกี่ยวกับบริบทการดำเนินการเชิงมุม Angular 2 ไม่มีปัญหานั้นดังนั้นใช้ไลบรารีอะไรก็ได้ที่คุณต้องการ
หากคุณต้องการคัดลอกอินสแตนซ์คลาสคุณสามารถใช้ 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.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
คุณสามารถโคลน Array ได้เช่น
this.assignCustomerList = Object.assign([], this.customerList);
และโคลนวัตถุเช่น
this.assignCustomer = Object.assign({}, this.customer);
หากคุณยังไม่ได้ใช้ lodash ฉันไม่แนะนำให้ติดตั้งเพียงวิธีเดียวนี้ ฉันขอแนะนำให้ใช้ไลบรารีเฉพาะที่แคบกว่าเช่น 'โคลน' แทน:
npm install clone
ฉันได้สร้างบริการเพื่อใช้กับ 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);
}
}