1

I once again stumbled over a behavior in Javascript that I don't understand. I need to update properties of an object as soon as a variable outside of the object is changed. The external variable is referenced in the object properties so I thought all I had to do is to change the variable externally and automatically have the property values changed.

Here's a simplified version of what the code looks like:

var serverPath = "123/";

var GetCurrentProductionApiConfig = {
  URL: {
    GetStart: serverPath + 'GetCurrentProduction?returnValue=start&',
    GetEnd: serverPath + 'GetCurrentProduction?returnValue=end&',
    Get: serverPath + 'GetCurrentProduction?returnValue=start&'
  }
};

serverPath = "456/";

console.log(GetCurrentProductionApiConfig.URL.GetStart);

This will result in:

123/GetCurrentProduction?returnValue=start&

Is it because the variable has been copied (passed by value) rather than having a pointer on it (passed by reference)? And which way would be the correct one to update the properties?

SaschaM78
  • 4,376
  • 4
  • 33
  • 42
  • 2
    There is no pass by reference in JS, everything is pass by value. However, the value of an *object* is it's reference. The value of a primitive is a straight primitive. So you cannot dynamically re-calculate stuff based on primitives, e.g, `a = 1; b = 2 + a; a = 4` would not change `b`. Same with strings. You can have dynamic properties but that's a fancy function underneath. – VLAZ Sep 18 '19 at 12:04
  • @VLAZ thanks for the info. AFAIK object variables passed into a function will be references to the outside object and not a copy of the object so there is at least something like "pass by reference". How would you solve the above problem? Extend the object with a prototype function to update the properties? – SaschaM78 Sep 18 '19 at 12:09
  • Again, the *value* of an object is its reference. So JS is always pass by value, even for objects. But if you wan to have the latest value, then you can make your properties actually methods, so every time you execute them, they will re-calculate the return value. You can hide that using a [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) which is *the same thing* but it's transparent for consuming code `obj.dynamicValue` will actually execute a function instead of reading a static property. – VLAZ Sep 18 '19 at 12:26
  • Also relevant: [Is it possible to implement dynamic getters/setters in JavaScript?](https://stackoverflow.com/questions/7891937/is-it-possible-to-implement-dynamic-getters-setters-in-javascript) – VLAZ Sep 18 '19 at 12:28
  • @VLAZ thanks a lot for the explanation, especially the linked SO looks really promising. If you could phrase an answer from your comments I'd accept this as solution. – SaschaM78 Sep 18 '19 at 12:39

1 Answers1

2

Everything in JavaScript is pass by value however, it happens that the value of an object is its reference. However, the important thing here is that for primitives, will not get changes when a referenced variable changes:

var a = "world";
var obj = {
  b: "hello" + a //evaluated once
}

a = "universe"; //does not modify obj.b which references a

console.log(obj.b); //helloworld

In order to have a dynamically evaluated string, you need to call a function or a method:

var a = "world";
var obj = {
  b: function() { 
    return "hello" + a //evaluated every time the function is executed
  }
}
console.log(obj.b()); //helloworld

a = "universe"; //will influence obj.b

console.log(obj.b()); //hellouniverse

However, that looks a bit "dirty" since it forces the caller to know to evaluate the property every time. It can also introduce inconsistency if some properties are plain strings, others functions and it's especially annoying if a property has to change from one to the other - you need to modify every place that calls this code to change, say, obj.c to obj.c().

Instead, using ES6+ you can define a getter for a property that will do the same as before but will hide the function call, so any time you read a property you actually evaluate code to return the result:

var a = "world";
var obj = {
  c: "plain property"
}

Object.defineProperty(obj, 'b', {
  get: function() {
    return "hello" + a //evaluated every time the property is read
  }
});

console.log(obj.b); //helloworld

a = "universe"; //will influence obj.b

console.log(obj.b); //hellouniverse
console.log(obj.c); //plain property
VLAZ
  • 26,331
  • 9
  • 49
  • 67