如何在Angular中复制对象并丢失其引用?
使用AngularJS,我可以使用angular.copy(object)
,但是在Angular中使用它会出现一些错误。
例外:ReferenceError:
angular
未定义
.copy()
但实际上并不需要它。在我所看到的各种AngJS1项目中,这是一个过大的杀伤力,如果要手动编写相关子结构的副本,就可以获得更清晰的代码。也许这是Angular团队不实施此决定的一部分。
如何在Angular中复制对象并丢失其引用?
使用AngularJS,我可以使用angular.copy(object)
,但是在Angular中使用它会出现一些错误。
例外:ReferenceError:
angular
未定义
.copy()
但实际上并不需要它。在我所看到的各种AngJS1项目中,这是一个过大的杀伤力,如果要手动编写相关子结构的副本,就可以获得更清晰的代码。也许这是Angular团队不实施此决定的一部分。
Answers:
假设您正在使用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的项目。
深复制内部具有嵌套对象的对象的替代方法是使用lodash的cloneDeep方法。
对于Angular,您可以这样操作:
使用yarn add lodash
或安装lodash npm install lodash
。
在您的组件中,导入cloneDeep
并使用它:
import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);
它仅增加了18kb,值得物有所值。
cloneDeep
方法实例化一个新对象。如果我们已经有目标对象,我们应该仍然使用它吗?
按照bertandg的指示使用lodash。angular不再使用此方法的原因是因为angular 1是一个独立的框架,并且外部库经常遇到与angular执行上下文有关的问题。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 !
正如其他人已经指出的那样,使用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或更高版本的服务,它使用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);
}
}
我和您都遇到了angular.copy和angular.expect工作的问题,因为它们在不添加一些依赖项的情况下不会复制对象或创建对象。我的解决方案是这样的:
copyFactory = (() ->
resource = ->
resource.__super__.constructor.apply this, arguments
return
this.extendTo resource
resource
).call(factory)
您可以像这样克隆数组
this.assignCustomerList = Object.assign([], this.customerList);
并像克隆对象
this.assignCustomer = Object.assign({}, this.customer);
如果您尚未使用lodash,则我不建议仅为此一种方法安装它。我建议改用更狭specialized的专业库,例如“ clone”:
npm install clone