Search code examples
javascriptparameter-passingjavascript-objectspass-by-reference

How to pass Javascript properties correctly such that they can be modified by a function


I understand that JavaScript objects, including arrays, are automatically passed by reference, so I have something like this:

class Thing {
    constructor( ) {
        this.letters = [ "A", "B" ];
        this.numbers = [ 1, 2, 3 ];

        this.change( this.letters, this.numbers );
    }

    change( _letters, _numbers ) {
        let l = _letters;
        let n = _numbers;

        l = [ ];
        n = 6;
    }
}

var t = new Thing( );
console.log( t );

The log request shows that there has been no change to the properties of Thing. And yet, under different conditions that I can not recognise, I have accidentally modified passed parameters inside a function, and have had to use something like n = Array.from( _numbers ) to avoid modifying the source property. Would someone be able to explain what is happening, and why?

I would like to pass object properties by reference such that I can modify more than one related property in a single function call, and thereby reserve the return value for debugging purposes.

Thank you.


Solution

  • The issue is the scoping. When you declared with let, the declaration is scoped to the function change and you are also reassigning the block-scoped variables here:

    l = [ ];
    n = 6;
    

    If you want to change the class properties, you can do:

    change(_letters, _numbers) {
        this.letters = _letters; // `this` scoped to class
        this.numbers = _numbers; // `this` scoped to class
      }
    

    See the example:

    // simple demo start
    let numbers = [1, 2, 3];
    
    let n = numbers;
    n.push(4)
    console.info(n, typeof n);
    
    n = 6; // reassign n
    console.info(n, typeof n, numbers);
    // simple demo end
    
    // refactored class
    class Thing {
      constructor() {
        this.letters = ["A", "B"];
        this.numbers = [1, 2, 3];
    
        this.change(this.letters, this.numbers);
      }
    
      change(_letters, _numbers) {
        let l = _letters; // `let` is scoped to `change()`
        let n = _numbers; // `let` is scoped to `change()`
    
        l = []; // only changes scoped l
        n = 6; // only changes scoped n
      }
    
      changeFix(_letters, _numbers) {
        this.letters = _letters; // `this` scoped to class
        this.numbers = _numbers; // `this` scoped to class
      }
    }
    
    var t = new Thing();
    console.log(t);
    
    t.change(['C'], [4]);
    console.info(t);
    
    t.changeFix(['C'], [4]);
    console.info(t);