typescript - cloning object
我有一个超类,它是许多子类(
我希望动态克隆一个在typescript中包含不同子对象的对象。
例如:一个
1 2 3 4 5 | var cust:Customer = new Customer (); cust.name ="someName"; cust.products.push(new Product(someId1)); cust.products.push(new Product(someId2)); |
为了克隆整个对象树,我在
1 2 3 4 5 6 7 8 9 10 11 | public clone():any { var cloneObj = new this.constructor(); for (var attribut in this) { if(typeof this[attribut] ==="object"){ cloneObj[attribut] = this.clone(); } else { cloneObj[attribut] = this[attribut]; } } return cloneObj; } |
当出现在javascript中时,
虽然脚本可以工作,但我想消除所发生的错误
解决具体问题
您可以使用类型断言来告诉编译器您知道得更好:
1 2 3 4 5 6 7 8 9 10 11 | public clone(): any { var cloneObj = new (this.constructor()); for (var attribut in this) { if (typeof this[attribut] ==="object") { cloneObj[attribut] = this.clone(); } else { cloneObj[attribut] = this[attribut]; } } return cloneObj; } |
克隆
请记住,有时候编写自己的映射比完全动态映射要好。但是,有一些"克隆"技巧可以让你产生不同的效果。
我将在随后的所有示例中使用以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Example { constructor(public type: string) { } } class Customer { constructor(public name: string, public example: Example) { } greet() { return 'Hello ' + this.name; } } var customer = new Customer('David', new Example('DavidType')); |
选项1:排列
属性:是的方法:NO深拷贝:没有
1 2 3 4 5 6 7 8 9 | var clone = { ...customer }; alert(clone.name + ' ' + clone.example.type); // David DavidType //alert(clone.greet()); // Not OK clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David SteveType |
选项2:object.assign
属性:是的方法:NO深拷贝:没有
1 2 3 4 5 6 7 8 9 | var clone = Object.assign({}, customer); alert(clone.name + ' ' + clone.example.type); // David DavidType alert(clone.greet()); // Not OK, although compiler won't spot it clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David SteveType |
选项3:object.create
属性:是的方法:是的深拷贝:没有
1 2 3 4 5 6 7 8 9 | var clone = Object.create(customer); alert(clone.name + ' ' + clone.example.type); // David DavidType alert(clone.greet()); // OK clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David SteveType |
选项4:深度复制功能
属性:是的方法:NO深拷贝:是的
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 | function deepCopy(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj ||"object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = deepCopy(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); } var clone = <Customer>deepCopy(customer); alert(clone.name + ' ' + clone.example.type); // David DavidType // alert(clone.greet()); // Not OK - not really a customer clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David DavidType |
1.使用排列运算符
1 2 | const obj1 = { param:"value" }; const obj2 = { ...obj1 }; |
spread运算符从obj1中获取所有字段,并将它们分布在obj2上。在结果中,您将得到具有新引用的新对象以及与原始对象相同的字段。
记住,它是浅拷贝,这意味着如果对象被嵌套,那么它的嵌套复合参数将通过相同的引用存在新对象中。
2.对象.assign())
1 2 | const obj1={ param:"value" }; const obj2:any = Object.assign({}, obj1); |
object.assign创建真实副本,但只拥有自己的属性,因此原型中的属性将不存在于复制的对象中。它也是肤浅的复制品。
3.对象.创建()
1 2 | const obj1={ param:"value" }; const obj2:any = Object.create(obj1); |
object.create的优点是原型中声明的任何函数都将在新创建的对象中可用。
关于肤浅复制的一些事情
浅复制将旧对象的所有字段都放入新对象中,但也意味着如果原始对象具有复合类型字段(对象、数组等),则这些字段将放入具有相同引用的新对象中。这种在原始物体中的突变场将反映在新物体中。
这可能看起来像一个陷阱,但真正需要复制整个复杂对象的情况很少。浅拷贝会重复使用大部分内存,这意味着与深拷贝相比,这是非常便宜的。
深拷贝
扩展运算符可以方便地进行深度复制。
1 2 | const obj1 = { param:"value", complex: { name:"John"}} const obj2 = { ...obj1, complex: {...obj1.complex}}; |
上面的代码创建了obj1的深度副本。复合字段"complex"也被复制到obj2中。突变字段"复杂"不会反映拷贝。
试试这个:
1 | let copy = (JSON.parse(JSON.stringify(objectToCopy))); |
在使用非常大的对象或对象具有不可分割的属性之前,这是一个很好的解决方案。
为了保护类型安全,可以在要从中进行复制的类中使用copy函数:
1 2 3 | getCopy(): YourClassName{ return (JSON.parse(JSON.stringify(this))); } |
或以静态方式:
1 2 3 | static createCopy(objectToCopy: YourClassName): YourClassName{ return (JSON.parse(JSON.stringify(objectToCopy))); } |
typescript/javascript有自己的用于浅克隆的运算符:
1 | let shallowClone = { ...original }; |
使用typescript 2.1中引入的"object spread"很容易得到一个浅拷贝。
这种字体:
生成此javascript:
1 2 3 4 5 6 7 8 9 | var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; var copy = __assign({}, original); |
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
你也可以有这样的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Entity { id: number; constructor(id: number) { this.id = id; } clone(): this { return new (this.constructor as typeof Entity)(this.id) as this; } } class Customer extends Entity { name: string; constructor(id: number, name: string) { super(id); this.name = name; } clone(): this { return new (this.constructor as typeof Customer)(this.id, this.name) as this; } } |
只需确保在所有
我的看法是:
我的工作是使用
因此,对于一个对象
1 | Object.assign(Object.create(foo), foo) |
我自己也遇到了这个问题,最后编写了一个小的库可克隆TS,它提供了一个抽象类,它向扩展它的任何类添加了一个克隆方法。抽象类借用Fenton在接受的答案中描述的深度复制函数,只将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import {Cloneable, CloneableArgs} from 'cloneable-ts'; // Interface that will be used as named arguments to initialize and clone an object interface PersonArgs { readonly name: string; readonly age: number; } // Cloneable abstract class initializes the object with super method and adds the clone method // CloneableArgs interface ensures that all properties defined in the argument interface are defined in class class Person extends Cloneable<TestArgs> implements CloneableArgs<PersonArgs> { readonly name: string; readonly age: number; constructor(args: TestArgs) { super(args); } } const a = new Person({name: 'Alice', age: 28}); const b = a.clone({name: 'Bob'}) a.name // Alice b.name // Bob b.age // 28 |
或者您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 | import {Cloneable} from 'cloneable-ts'; interface Person { readonly name: string; readonly age: number; } const a: Person = {name: 'Alice', age: 28}; const b = Cloneable.clone(a, {name: 'Bob'}) a.name // Alice b.name // Bob b.age // 28 |
这是我的混蛋!这里有一个Stackblitz链接。它目前仅限于复制简单类型和对象类型,但我认为可以很容易地修改。
1 2 3 4 5 6 7 8 9 10 11 | let deepClone = <T>(source: T): { [k: string]: any } => { let results: { [k: string]: any } = {}; for (let P in source) { if (typeof source[P] === 'object') { results[P] = deepClone(source[P]); } else { results[P] = source[P]; } } return results; }; |
对于Hole对象内容的简单克隆,我只需对实例进行字符串化和解析:
1 | let cloneObject = JSON.parse(JSON.stringify(objectToClone)) |
虽然我在ObjecttoClone树中更改数据,但CloneObject中没有更改。这是我的请求。
希望有帮助
如果出现此错误:
1 | TypeError: this.constructor(...) is not a function |
这是正确的脚本:
1 2 3 4 5 6 7 8 9 10 11 | public clone(): any { var cloneObj = new (this.constructor)(); // line fixed for (var attribut in this) { if (typeof this[attribut] ==="object") { cloneObj[attribut] = this.clone(); } else { cloneObj[attribut] = this[attribut]; } } return cloneObj; } |
我尝试过创建一个通用的复制/克隆服务,它保留嵌套对象的类型。如果我做错了,我会很乐意得到反馈,但到目前为止,这似乎有效…
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 | import { Injectable } from '@angular/core'; @Injectable() export class CopyService { public deepCopy<T>(objectToClone: T): T { // If it's a simple type or null, just return it. if (typeof objectToClone === 'string' || typeof objectToClone === 'number' || typeof objectToClone === 'undefined' || typeof objectToClone === 'symbol' || typeof objectToClone === 'function' || typeof objectToClone === 'boolean' || objectToClone === null ) { return objectToClone; } // Otherwise, check if it has a constructor we can use to properly instantiate it... let ctor = Object.getPrototypeOf(objectToClone).constructor; if (ctor) { let clone = new ctor(); // Once we've instantiated the correct type, assign the child properties with deep copies of the values Object.keys(objectToClone).forEach(key => { if (Array.isArray(objectToClone[key])) clone[key] = objectToClone[key].map(item => this.deepCopy(item)); else clone[key] = this.deepCopy(objectToClone[key]); }); if (JSON.stringify(objectToClone) !== JSON.stringify(clone)) console.warn('object cloned, but doesnt match exactly... object: ' + JSON.stringify(objectToClone) +" clone:" + JSON.stringify(clone)) // return our cloned object... return clone; } else { //not sure this will ever get hit, but figured I'd have a catch call. console.log('deep copy found something it didnt know: ' + JSON.stringify(objectToClone)); return objectToClone; } } } |
好的旧jquery怎么样?!这里是Deep Clone:
1 | var clone = $.extend(true, {}, sourceObject); |
我最后做了:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public clone(): any { const result = new (this.constructor); // some deserialization code I hade in place already... // which deep copies all serialized properties of the // object graph // result.deserialize(this) // you could use any of the usggestions in the other answers to // copy over all the desired fields / properties return result; } |
因为:
1 | var cloneObj = new (this.constructor()); |
来自@fenton的,给出了运行时错误。
字体脚本版本:2.4.2