'How to update a variable that depends on another variable in a function?

In the sample code below, note that the value of the variable dependent depends on the variable prereq. When the function changePrereq is called, it changes the value of prereq. This change is shown if logged, but it isn't reflected in the value of dependent. Instead, when dependent is put into a span, it shows Some text - undefined.

How can I have dependent change depending on the value of prereq?

P.S. Thanks everyone for the advice. For myself, I chose the answer from "ztcollazo" as the right decision.

"outis" - Thank you for your explanations. I will definitely pay attention to your recommendations and study them in more detail!

var display = document.getElementById('text'),
    prereq,
    message = "Some text - " + prereq;

function updateDisplay() {
    display.innerHTML = message;
    console.log(prereq);
}

function changePrereq() {
    prereq = "I am some text.";
    updateDisplay();
}
<p><span id="text"></span></p>
<button onclick="changePrereq()">Click</button>


Solution 1:[1]

The problem is that changeMyText doesn't update when someText does. You need to define changeMyText inside of the changeTextFunc function and then pass someText as a parameter.

Example:

var myText = document.getElementById('text');
var someText1;

var m1 = 1;

function changeTextFunc(someText, m) {
  var changeMyText = "Some text - " + someText;
  if (m > 0) {
    myText.innerHTML = changeMyText;
  } else {
    console.log('m < 0');
  }
}

function changeText() {
  someText1 = "I a'm some text.";
  changeTextFunc(someText1, m1);
}
<div>
  <button onclick="changeText()">Click</button>
  <p id="text"></p>
</div>

Solution 2:[2]

move changeMyText variable into changeTextFunc function. the code will looks like this

    function changeMyText(m){
          var changeMyText = "Some text - " + someText1;
          if (m > 0) {
             myText.innerHTML = changeMyText;
          } else {
             console.log('m < 0');
          }
    }

Solution 3:[3]

If you want a variable to change based on another, you must set its value after the other is changed. There are various approaches, with various implementations. You can break it down into different aspects for the change:

  1. what:
    1. globals (note: avoid global variables in production, and use sparingly in other contexts for the practice)
    2. locals
    3. objects
      1. plain methods
      2. getter/setter methods
  2. where:
    1. inline, at the site a variable is referred to
    2. in a separate function created for the responsibility
  3. when:
    1. the prerequisite variable is changed
    2. the dependent variable is used

Some of the above options from different aspects aren't intended to be combined, or can't be combined. For example, 1.3 (objects) is intended to go with 2.2 (separate functions), and 1.3.2 (getters/setters) requires 2.2, since object getters & setters are functions (2.2 basically means "use a getter or setter", though not necessarily using getter/setter syntax). You might be able to think of other aspects, or other possibilities for the above aspects.

ztcollazo shows a solution that uses a global for the prerequisite (1.1), a local for the dependent (1.2) and updates inline (2.1) when the dependent is used (3.2). If the line in changeTextFunc setting changeMyText were instead move to changeText (and a global used), you'd have 1.1 + 2.1 + 3.1.

For some more example implementations, examine the following. It illustrates four different options from the above, noted by comments.

var display = document.getElementById('output'),
    dependent, prereq;

/* options 1.1, 2.2, 3.1: global, separate function, prereq change */
function setPrereq(value) {
    prereq = value;
    dependent = "global prereq setter: " + prereq;
}

function updateDisplayFromVariable() {
    display.innerText = dependent;
}

function changePrereq_updateWhenSet(value="setter") {
    setPrereq(value);
    updateDisplayFromVariable();
}

/* options 1.1, 2.2, 3.2: global, separate function, dependent used */
function getDependent(value) {
    return dependent = "global dependent getter: " + prereq;
}

function updateDisplayFromGetter() {
    display.innerText = getDependent();
}

function changePrereq_updateWhenUsed(value="inline, no setter") {
    prereq = value;
    updateDisplayFromGetter();
}

/* options 1.3.2, 2.2: (local) object getter/setter */
/* wrapped in self-called function to prevent polluting global namespace */
var dependency = (function () {
    let display = document.getElementById('output'),

        /* options 1.3.2, 2.2, 3.2: (local) object getter, dependent used */
        inGetter = {
            prereq: 'initial',
            /* note: `dependent` is entirely synthetic */
            get dependent() {
                return "object's dependent getter: " + this.prereq;
            },
        },

        /* options 1.3.2, 2.2, 3.1: (local) object setter, prereq changed */
        inSetter = {
            /* note: when using setter, can't use same name for the 
             * backing property; instead, must also define getter. */
            _prereq: 'initial',
            get prereq() {
                return this._prereq;
            },
            set prereq(value) {
                this._prereq = value;
                this.dependent = "object's prereq setter: " + value;
            },
        };

    function updateDisplay(from) {
        display.innerText = from.dependent;
    }
    
    /* expose 1.3.2, 2.2, 3.1 */
    function whenSet(value) {
        inSetter.prereq = value;
        updateDisplay(inSetter);
    }
    
    /* expose 1.3.2, 2.2, 3.2 */
    function whenUsed(value) {
        inGetter.prereq = value;
        updateDisplay(inGetter);
    }

    return {whenSet, whenUsed};
})();
<button onclick="changePrereq_updateWhenSet('thimbles')">Use global setter</button>
<button onclick="changePrereq_updateWhenUsed('care')">Use global getter</button>
<button onclick="dependency.whenSet('forks')">Use object setter</button>
<button onclick="dependency.whenUsed('hope')">Use object getter</button>
<p><span id="output"></span></p>

As with any design, the above have advantages and disadvantages, but using object setters/getters (1.3.2) should be preferred, as it's the most robust approach. Using standalone functions (not getters/setters) and updating inline are both more brittle as a programmer may fail to use them somewhere, instead assigning & referencing variables directly. Updating inline is also less maintainable, as any changes will have to be made on every line that performs the update. Global variables have their own issues, such as:

  • limited code reuse in a module (note how updateDisplay(from) works for two different cases, whereas changePrereq_updateWhenSet and changePrereq_updateWhenUsed each require different display functions), and
  • inflexibility (i.e. composing existing functions for new behaviors is much more limited; in other words, this limits code reuse for client code),
  • naming collisions when different modules use the same globals, causing them to stomp on each other.

As for whether to update the dependent in the prerequisite's setter (1.3.2 + 2.2 + 3.1) or to use a getter for the dependent with no backing property (in some circles, this is known as a "synthetic property") depends on other requirements (basically, whether the dependent be allowed to be assigned values independent of the prerequisite). You could, for instance, use getters & setters for both properties.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ztcollazo
Solution 2 Eric Pradana
Solution 3 outis