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 lodashnpm 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

大型数组的 angular.copy 面临非常糟糕的性能

为啥以及何时使用 angular.copy? (深拷贝)

angular.copy() 和 JSON.parse(JSON.stringify()) 之间的区别?

AngularJS方法 —— angular.copy

Angular.js对复制的文件对象的非法调用