What's alternative to angular.copy in Angular
如何复制一个对象并在角度上丢失它的引用?
使用AngularJS,我可以使用
EXCEPTION: ReferenceError:
angular is not defined
假设您使用ES6,则可以使用
更新:
对于TypeScript 2.1+,ES6速记对象排列表示法可用:
1 | const copy = { ...original } |
在我们找到更好的解决方案之前,您可以使用以下方法:
1 | duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject)); |
编辑:澄清
请注意:上述解决方案仅用于快速修复一个衬垫,在Angular2处于活动开发阶段时提供。我希望我们最终能得到一个相当于
此方法在分析日期属性时也有问题(它将变为字符串)。
请不要在生产应用程序中使用此方法。只在你的实验项目中使用它——你学习角2的项目。
另一种方法是使用lodash的clonedeep方法来深度复制内部有嵌套对象的对象。
对于角度,可以这样做:
用
在组件中,导入
1 2 3 | import * as cloneDeep from 'lodash/cloneDeep'; ... clonedObject = cloneDeep(originalObject); |
它只添加了18kb到您的构建中,非常值得。
我在这里也写了一篇文章,如果你需要更多的了解为什么使用罗达什的克隆edep。
对于浅复制,可以使用Object.assign,这是ES6功能
1 2 3 | let x = { name: 'Marek', age: 20 }; let y = Object.assign({}, x); x === y; //false |
不要将其用于深度克隆
如Bertandg所示,使用木屑。Angular不再使用此方法的原因是,Angular 1是一个独立的框架,外部库经常遇到与Angular执行上下文相关的问题。角度2没有这个问题,所以使用任何你想要的库。
https://lodash.com/docs_clonedeep
如果要复制类实例,也可以使用object.assign,但需要将新实例作为第一个参数传递(而不是):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 ! |
我找到的最简单的解决方案是:
1 | let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject); |
*重要步骤:您必须安装Lodash才能使用此功能(其他答案不清楚):
1 2 3 | $ npm install --save lodash $ npm install --save @types/lodash |
然后将其导入TS文件:
1 | import * as _ from"lodash"; |
正如其他人已经指出的,使用lodash或下划线可能是最好的解决方案。但是,如果您不需要这些库来进行其他任何操作,那么您可能会使用类似这样的库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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. } |
我们决定这么做。
我已经创建了一个与Angular5或更高版本一起使用的服务,它使用AngularJS的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 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); } } |
有同样的问题,不想使用任何插件进行深度克隆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static deepClone(object): any { const cloneObj = (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; } |
学分:我使这个函数更易读,请检查下面功能的例外情况
我只需要从我的应用程序"模型"(原始后端数据转换为对象)中获得这个功能。所以我最终使用了object.create(从指定原型创建新对象)和object.assign(在对象之间复制属性)的组合。需要手动处理深度拷贝。我为这个创造了一个要点。