JavaScript/TypeScript: Skillnaden mellan `Object.assign({}, myClass)`/`Object.create(myClass)` och konstruktorn `new MyClass()`


Jag önskar att jag kunde posta mitt projekt ord för ord för denna fråga, men det kan jag inte.

I princip har jag följande klasser:

class Lowest {
    someValue: string
    
    constructor(someValue: string) {
        this.someValue = someValue
    }
}
class Middle {
    lowest: Lowest

    constructor(someValue: string) {
        this.lowest = new Lowest(someValue)
    }
}
class Highest {
    private _middle: Middle
    otherValue: SomeOtherClass

    // normal getter
    public get middle(): Middle {
       return this._middle
    }

    public set middle(next: Middle) {
       this._middle = next
       // notice: when `_middle` is set, I update another property!
       otherValue.doSomething(this._middle)
    }

    constructor(config: { someValue: string }) {
        this.middle = new Middle(config.somevalue)
    }
}

På vissa ställen i mitt program har jag en referens till en Highest-instans och behöver ändra dess middle.lowest.someValue-subegenskap. Nu, av arkitektoniska skäl som jag egentligen inte kan beskriva här, behöver jag uppdatera Highest.otherValue-egenskapen när Highest.middle ändras. Eftersom jag använder TypeScript utför jag helt enkelt denna operation i settern för Highest.middle. Som sådan kan jag inte bara direkt sätta Highest.middle.lowest till något värde.

Mitt första tillvägagångssätt var:

const nextMiddle = Object.assign({}, highestInstance.middle)
nextMiddle.lowest.someValue = "some other thing"
highestInstance.middle = nextMiddle

Men detta slutade med att orsaka några mycket konstiga problem. Nu hade jag egentligen inget tekniskt behov av att utföra en djup kloning av nextMiddle, så jag övervann det med följande kod:

const nextMiddle = highestInstance.middle
nextMiddle.lowest.someValue = "some other thing"
highestInstance.middle = nextMiddle

Medan jag experimenterade med den bästa lösningen implementerade jag en Middle.copy()-metod som i princip bara anropar new Middle() och new Lowest() med värdena från den tidigare instansen. Detta löste också mina tekniska problem, men gjorde mig ännu mer förvirrad.

Jag förstår att det är en stor skillnad mellan att helt enkelt återtilldela highestInstance.middle och att använda Object.assign() för att klona det, men jag förstår inte varför det inte verkar finnas någon skillnad mellan Object.assign() och new Middle()

Vilka är de verkliga skillnaderna mellan dessa tre metoder för kloning/omfördelning?

Svar #1

hade inget verkligt tekniskt behov av att utföra en djup kloning av nextMiddle, så jag övervann det med följande kod:

Object.assign({}, highestInstance.middle) skapar en ytlig kopia och inte en djup kopia.

Problemet här är att använda Middle Setter tvingar dig att utföra otherValue: SomeOtherClass endast när middle uppdateras.

det kommer att uppdatera det även om du bara gör bara :

high.middle.lowest.someValue = 'new value'
high.middle = high.middle; // setter triggered

En möjlig lösning är att skapa en återanropskedja istället för att använda en setter:

    type voidFun = () => void;

class Lowest {
    private _someValue: string
    
     public get someValue(): string {
       return this._someValue
    }

    private  update?: voidFun; // create an optional callback

    public set someValue(next: string) {
       this._someValue = next
       this.update?.call(undefined);
    
    }

    constructor(someValue: string, fun?: voidFun) {
        this._someValue = someValue
        this.update = fun;// pass down
    }
}

class Middle {
    lowest: Lowest;

    constructor(someValue: string, fun?: voidFun) {
        this.lowest = new Lowest(someValue, fun)
    }
}

class Highest {
    private _middle: Middle
    otherValue: any = {};

    
    // normal getter
    public get middle(): Middle {
       return this._middle
    }

    // now you dont need middle setting so it cant be updated by anyone directly 
    // if you still want to have you can have it too
    private callBack = () => {
        console.log('lower was update');
        this.otherValue.myMiddle  = this._middle.lowest.someValue;
    }

    constructor(config: { someValue: string } = { someValue : 'constr' }) {
        this._middle = new Middle(config.someValue, this.callBack) 
    }
}



 let high = new Highest()
 high.middle.lowest.someValue = 'new value'
// high.middle = high.middle; // now not possible unless you create setter
console.log('updated middle high',high)