Angular中angular.copy的替代方法是什么

时间:2016-01-09 00:56:34

标签: angular

如何在Angular中复制对象并丢失其引用?

使用AngularJS,我可以使用angular.copy(object),但我在Angular中使用它时会出现一些错误。

  

EXCEPTION:ReferenceError:angular未定义

14 个答案:

答案 0 :(得分:156)

假设您使用的是ES6,则可以使用var copy = Object.assign({}, original)。适用于现代浏览器;如果您需要支持旧浏览器,请查看此polyfill

<强>更新

使用TypeScript 2.1+,可以使用ES6速记对象扩展符号:

const copy = { ...original }

答案 1 :(得分:44)

直到我们有更好的解决方案,您可以使用以下内容:

duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));

编辑:澄清

请注意:上述解决方案仅仅是一个快速修复的衬垫,在Angular 2正在积极开发时提供。我希望我们最终可能得到相当于angular.copy()。因此,我不想编写或导入深度克隆库。

此方法在解析日期属性时也存在问题(它将成为字符串)。

请不要在生产应用中使用此方法。仅在您的实验项目中使用它 - 您正在学习Angular 2的那些项目。

答案 2 :(得分:14)

使用lodash作为bertandg指示。 angular不再使用此方法的原因是因为angular 1是一个独立的框架,并且外部库经常遇到角度执行上下文的问题。 Angular 2没有那个问题,所以请使用你想要的任何库。

https://lodash.com/docs#cloneDeep

答案 3 :(得分:14)

对于复制,您可以使用Object.assign这是一个ES6功能

let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false

请勿将其用于深度克隆

答案 4 :(得分:14)

深层复制具有嵌套对象的对象的替代方法是使用lodash的cloneDeep方法。

对于Angular,你可以这样做:

使用npm install lodashcloneDeep安装lodash。

在您的组件中,导入import * as cloneDeep from 'lodash/cloneDeep'; ... clonedObject = cloneDeep(originalObject); 并使用它:

{{1}}

它只为您的构建添加了18kb,非常值得获益。

如果您需要更多地了解使用lodash的cloneDeep的原因,我还写了article here

答案 5 :(得分:8)

如果要复制类实例,也可以使用Object.assign,但是需要将新实例作为第一个参数(而不是{})传递:

  <div id="logmodal" class="modal fade" role="dialog">
                              <div class="modal-dialog">

                                <!-- Modal contenido-->
                                <div class="modal-content">
                                  <div class="modal-header">
                                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                                    <h4 class="modal-title"><i class="fa fa-user-circle"> </i> Login</h4>
                                  </div>
                                  <div class="modal-body">

                                    <%--<form id="Conectar">--%>
                                         <div class="form-group">
                                            <input type="text" id="txtUsuario" class="form-control" runat="server" placeholder="Usuario Web"/>

                                        </div>
                                        <div class="form-group">
                                            <input type="password" id="txtPassword" class="form-control" runat="server" placeholder="Password"/>
                                        </div>
                                        <div class="form-group">
                                            <asp:Button ID="Button1" runat="server" Text="LOGIN" CssClass="btn btn-default" OnClick="Button1_Click" />
                                        </div>
                                    <%--</form>--%>
                                    <asp:Label ID="LblErrors" runat="server" Text=""></asp:Label>
                                  </div>
                                  <div class="modal-footer">
                                    <button type="button"  class="btn btn-default" data-dismiss="modal" >Cerrar</button>
                                  </div>
                                </div>
                              </div>
                            </div>

答案 6 :(得分:5)

正如其他人已经指出的那样,使用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.

  }

这就是我们决定做的事情。

答案 7 :(得分:1)

您可以像克隆数组一样

 this.assignCustomerList = Object.assign([], this.customerList);

然后像克隆对象一样

this.assignCustomer = Object.assign({}, this.customer);

答案 8 :(得分:0)

我需要此功能只是构成我的应用程序“模型”(原始后端数据转换为对象)。因此,我最终使用了Object.create(从指定的原型创建新对象)和Object.assign(对象之间的复制属性)的组合。需要手动处理深层副本。我为此创建了a gist

答案 9 :(得分:0)

发生了同样的问题,并且不想使用任何插件进行深度克隆:

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,请在下面检查其功能的例外情况

答案 10 :(得分:0)

我已经创建了一个用于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);
  }
}

答案 11 :(得分:0)

我以及您都遇到了angular.copy和angular.expect工作的问题,因为它们在不添加某些依赖项的情况下不会复制对象或创建对象。我的解决方法是这样:

  copyFactory = (() ->
    resource = ->
      resource.__super__.constructor.apply this, arguments
      return
    this.extendTo resource
    resource
  ).call(factory)

答案 12 :(得分:0)

let newObj = JSON.parse(JSON.stringify(obj))

JSON.stringify()方法将JavaScript对象或值转换为JSON字符串

答案 13 :(得分:0)

如果您还没有使用lodash,我不建议您仅为此一种方法安装它。我建议改用更狭specialized的专业库,例如'clone':

npm install clone