简体   繁体   中英

Array.push performs deep copy instead of shallow copy

Context

I'm using Typescript/Angular2 RC1 for my webapp and I have two classes. In class1 (Angular2 service), I have a variable myVar = [obj1, obj2, obj3] and in class2 (Angular2 component), I get myLocalVar = class1.myVar in the constructor using viewInjector to inject the service.

Problem

In class2, I copy myVar with a push (shallow copy because it copies objects) so myLocalVar is equal to [obj1, obj2, obj3] (such that objX={number:X} ). When I change one item such as: myVar[0]={number:5} in class1 the change won't appear in my class2 myLocalVar and myLocalVar[0].number will be equal to 1 . However, myVar of class2 changes... It seems that the this.myLocalVar.push(this.myVar[int]); created a deep copy instead of a shallow copy.

Is Typescript changing the behavior of a push in Javascript? If yes, what should I do to keep the reference and do a shallow copy?

Code Sample

@Injectable()

export class Class1{

    public myVar:any[];

    constructor(){
        this.myVar = [{number: 1}, {number: 2}, {number: 3}];
    }

    changeVar(newVar):void{
        this.myVar[0] = newVar; // newVar = {number: 5}
    }
}

@Component({
    selector: 'class2',
    viewInjector: [Class1],
    templateUrl: 'someURL'
})
export class Class2{

    private myLocalVar:any[] = [];

    constructor(class1: Class1){
        this.myVar = class1.myVar;
    }

    showVars():void{
        console.log(this.myLocalVar[0]);
        console.log(this.myVar[0]);
    }

    ngOnInit(){
        for(let int = 0; int < this.myVar.length; int++){
            this.myLocalVar.push(this.myVar[int]);
        }
    }
}

The console.log will print two different values: console.log(this.myLocalVar[0]) prints 1 and console.log(this.myVar[0]); prints 5 after changeVar() has been called by a third party!

Edit

Here is a plunker that demonstrates the problem:

  • Click on show, check your console and you will see the same values
  • Click on change
  • Click on show again and you will see two different values for myVar and myLocalVar

What I need is each time I change myVar[x]=newObj I want myLocalVar to reflect that change. Please include a solution to that problem if you want your answer to be validated.

Change

ngOnInit(){
    for(let int = 0; int < this.myVar.length; int++){
        this.myLocalVar.push(this.myVar[int]);
    }
}

to

ngOnInit(){
  this.myLocalVar = this.myVar;
}

In the first only reference of the array elements is copied. In the second reference of array is getting copied?

Your intent seems to be to update an object's values, but instead you are creating a new object and inserting that new object into the original array (only).

This will do what you intended:

changeVar(newVar: Object): void {
    this.myVar[0].number = newVar.number;
}

The plunker code you linked to is not in sync with the posted code. In the plunker's case, you are passing a number, not an object. It has:

changeVar(newVar): void {
    this.myVar[0] = {number:newVar}; // newVar = {number: 5}
}

In that case, change to:

changeVar(newVar: number): void {
    this.myVar[0].number = newVar;
}

“ Object.assign”执行浅表副本。

(<any>Object).assign(this.shallowCopyObject, this.myObject);

Your have this problem because myLocalVar and myVar are two different references. Meaning, they are two different array containers .

And when you push element from myVar to myLocalVar , you push the references of the elements in myVar into myLocalVar . So if

myVar = [{name: 1}, {name: 2}] you have two object in it and both references to the objects are pushed into myLocalVar .

When you call changeVar method, you basically replace the element reference in myVar with new object reference. However, the old reference to the element being replaced is still there and referenced by myLocalVar , thus the result you see.

So the solution?

You need to change how your changeVar method set the value. Instead of doing this.myVar[0] = {number:newVar}; , you should do this.myVar[0]['number'] = newVar; This maintains the reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM