Angular 中 angular.copy 的替代方法是啥
Posted
技术标签:
【中文标题】Angular 中 angular.copy 的替代方法是啥【英文标题】:What's alternative to angular.copy in AngularAngular 中 angular.copy 的替代方法是什么 【发布时间】:2016-04-13 19:30:45 【问题描述】:如何在 Angular 中复制对象并丢失其引用?
使用 AngularJS,我可以使用 angular.copy(object)
,但在 Angular 中使用它时遇到了一些错误。
例外:ReferenceError:
angular
未定义
【问题讨论】:
检查此解决方案可能会有所帮助:Link 在很多情况下,您可能想使用.copy()
,但实际上并不需要它。在我见过的各种 AngJS1 项目中,这是一种矫枉过正的做法,因为手动复制相关子结构可以使代码更简洁。也许这是 Angular 团队决定不实施它的一部分。
顺便说一下,相关的(也没有回答):***.com/questions/41124528/…
What is the most efficient way to deep clone an object in javascript?的可能重复
【参考方案1】:
假设你使用的是 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
在 Webstorm 我得到了Unresolved function or method assign()
; IDE 详细信息:Webstorm 2016.2。我该如何解决?
@meorfi 转到File -> Settings -> Languages & Frameworks -> Javascript
并将Javascript language version
设置为ECMAScript 6.0
。
@bertrandg _.clone(value) 与 angular.copy() 不同,它不会创建新实例,因此 _.cloneDeep(value) 仍然会创建引用 ***.com/questions/26411754/…跨度>
另外,如果要复制数组,请使用:const copy = [ ...original ]
【参考方案2】:
在我们有更好的解决方案之前,您可以使用以下方法:
duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));
编辑:澄清
请注意:上述解决方案仅作为一种快速修复的方法,在 Angular 2 处于积极开发阶段时提供。我希望我们最终可能会得到相当于angular.copy()
。因此,我不想编写或导入深度克隆库。
这个方法在解析日期属性时也有问题(它会变成一个字符串)。
请不要在生产应用中使用此方法。仅在您的实验项目中使用它——您正在为学习 Angular 2 所做的项目。
【讨论】:
这会毁了你的约会,而且速度很慢。 虽然没有导入整个库来执行单个任务那么慢,只要您正在做的事情非常简单...... 这太可怕了,永远不要使用它 @MurhafSousli 请尝试了解此答案的上下文。这是在开发 Angular 2 时提供的,希望我们最终会得到一个等效的 angular.copy() 函数。为了缩短等待期,我将此解决方案作为临时选项提出,直到我们有更好的解决方案。这是具有深度克隆的单线。这是可怕的,我同意......但考虑到当时的实验环境,它并没有那么糟糕。 @LazarLjubenović 当然在 2018 年就是这种情况,我完全同意你的观点今天,但是在 2016 年 webpack 没有摇树,所以你要导入一个大多数情况下是整个库。【参考方案3】:深度复制具有嵌套对象的对象的替代方法是使用 lodash 的 cloneDeep 方法。
对于 Angular,您可以这样做:
使用yarn add lodash
或npm install lodash
安装lodash。
在您的组件中,导入 cloneDeep
并使用它:
import cloneDeep from "lodash";
...
clonedObject = cloneDeep(originalObject);
它只增加了 18kb 到您的构建中,非常值得。
我还写了一个article here,如果您需要更深入地了解为什么要使用 lodash 的 cloneDeep。
【讨论】:
"only 18kb" 添加到输出中以便能够深度复制对象? JavaScript 是一团糟。 在阅读了您引用的文章后,我了解到cloneDeep
方法实例化了一个新对象。如果我们已经有一个目标对象,我们还应该使用它吗?【参考方案4】:
对于 shallow 复制,您可以使用 Object.assign,它是 ES6 功能
let x = name: 'Marek', age: 20 ;
let y = Object.assign(, x);
x === y; //false
请勿将其用于深度克隆
【讨论】:
什么可以用于深度克隆?【参考方案5】:按照 bertandg 的说明使用 lodash。 Angular 不再有这个方法的原因是 Angular 1 是一个独立的框架,外部库经常遇到 Angular 执行上下文的问题。 Angular 2 没有这个问题,所以使用你想要的任何库。
https://lodash.com/docs#cloneDeep
【讨论】:
【参考方案6】:如果你想复制一个类实例,你也可以使用 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 !
【讨论】:
这正是我所需要的【参考方案7】:我找到的最简单的解决方案是:
let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);
*重要步骤: 您必须安装 lodash 才能使用它(其他答案不清楚):
$ npm install --save lodash
$ npm install --save @types/lodash
然后将其导入到您的 ts 文件中:
import * as _ from "lodash";
【讨论】:
这会在 Angular 10 中创建一个警告:/.../test.component.ts 中的警告取决于“lodash”。 CommonJS 或 AMD 依赖项可能导致优化救助。【参考方案8】:正如其他人已经指出的那样,使用 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.
这就是我们决定做的事情。
【讨论】:
// 取消链接我们可以添加的日期: if(Object.prototype.toString.call(obj) === '[object Date]') return new Date(obj.getTime()) ; 或使用实例类型检查日期 - if(obj instanceof Date)return new Date(obj.getTime())【参考方案9】:let newObj = JSON.parse(JSON.stringify(obj))
JSON.stringify()
方法将 JavaScript 对象或值转换为 JSON 字符串
【讨论】:
这个上面已经详尽的说了,这是最糟糕的处理方式! @Alessandro:想解释一下你的说法吗?原始问题不适用于任何特定情况。这可以用于许多情况,但对某些人来说可能不是最好的。这只是众多方法中的一种。解释这是最糟糕的情况?【参考方案10】:你可以像这样克隆数组
this.assignCustomerList = Object.assign([], this.customerList);
然后像这样克隆对象
this.assignCustomer = Object.assign(, this.customer);
【讨论】:
这是错误的,因为 angularjs.copy 支持 deepCopy。因此,您的回答具有误导性,因为不支持对象的 deepClone。【参考方案11】:我创建了一个与 Angular 5 或更高版本一起使用的服务,它使用 angularjs 的基础 angular.copy()
,它对我来说效果很好。另外还有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);
【讨论】:
【参考方案12】:我需要这个功能只是形成我的应用程序“模型”(原始后端数据转换为对象)。所以我最终使用了Object.create(从指定的原型创建新对象)和Object.assign(在对象之间复制属性)的组合。需要手动处理深拷贝。我为此创建了a gist。
【讨论】:
【参考方案13】:有同样的问题,不想使用任何插件来进行深度克隆:
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;
致谢:I made this function more readable,请在下方查看其功能的例外情况
【讨论】:
【参考方案14】:我和你一样面临工作 angular.copy 和 angular.expect 的问题,因为它们不会复制对象或在不添加一些依赖项的情况下创建对象。我的解决方案是这样的:
copyFactory = (() ->
resource = ->
resource.__super__.constructor.apply this, arguments
return
this.extendTo resource
resource
).call(factory)
【讨论】:
【参考方案15】:如果您还没有使用 lodash,我不建议您只为这一种方法安装它。我建议改为使用更专业的库,例如“克隆”:
npm install clone
【讨论】:
以上是关于Angular 中 angular.copy 的替代方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
angular.js 的angular.copy angular.extend angular.merge