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

What's alternative to angular.copy in Angular

如何复制一个对象并在角度上丢失它的引用?

使用AngularJS,我可以使用angular.copy(object),但是在角度上使用它会有一些错误。

EXCEPTION: ReferenceError: angular is not defined


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

更新:

对于TypeScript 2.1+,ES6速记对象排列表示法可用:

1
const copy = { ...original }


在我们找到更好的解决方案之前,您可以使用以下方法:

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

编辑:澄清

请注意:上述解决方案仅用于快速修复一个衬垫,在Angular2处于活动开发阶段时提供。我希望我们最终能得到一个相当于angular.copy()的东西。因此,我不想编写或导入深度克隆库。

此方法在分析日期属性时也有问题(它将变为字符串)。

请不要在生产应用程序中使用此方法。只在你的实验项目中使用它——你学习角2的项目。


另一种方法是使用lodash的clonedeep方法来深度复制内部有嵌套对象的对象。

对于角度,可以这样做:

yarn add lodashnpm install lodash安装Lodash。

在组件中,导入cloneDeep并使用它:

1
2
3
import * as cloneDeep from 'lodash/cloneDeep';
...
clonedObject = cloneDeep(originalObject);

它只添加了18kb到您的构建中,非常值得。

我在这里也写了一篇文章,如果你需要更多的了解为什么使用罗达什的克隆edep。


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

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

不要将其用于深度克隆


如Bertandg所示,使用木屑。Angular不再使用此方法的原因是,Angular 1是一个独立的框架,外部库经常遇到与Angular执行上下文相关的问题。角度2没有这个问题,所以使用任何你想要的库。

https://lodash.com/docs_clonedeep


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 !


我找到的最简单的解决方案是:

1
let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

*重要步骤:您必须安装Lodash才能使用此功能(其他答案不清楚):

1
2
3
$ npm install --save lodash

$ npm install --save @types/lodash

然后将其导入TS文件:

1
import * as _ from"lodash";

正如其他人已经指出的,使用lodash或下划线可能是最好的解决方案。但是,如果您不需要这些库来进行其他任何操作,那么您可能会使用类似这样的库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  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.

  }

我们决定这么做。


我已经创建了一个与Angular5或更高版本一起使用的服务,它使用AngularJS的angular.copy ()基,它对我很好。另外还有其他功能,如isUndefined等,希望能有所帮助。像任何优化一样,很高兴知道。当做

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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);
  }
}


有同样的问题,不想使用任何插件进行深度克隆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static deepClone(object): any {
        const cloneObj = (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;
    }

学分:我使这个函数更易读,请检查下面功能的例外情况


我只需要从我的应用程序"模型"(原始后端数据转换为对象)中获得这个功能。所以我最终使用了object.create(从指定原型创建新对象)和object.assign(在对象之间复制属性)的组合。需要手动处理深度拷贝。我为这个创造了一个要点。