Search code examples
javascriptvariablesshadowing

JS globalThis is returning a value when it should return undefined or null


I'm getting a value from a shadowed global variable when I think that it should respond with undefined.

I've rewritten and condensed the original code because I could not believe that it should work but it still does. The input has an id of "num1". The JS was written incorrectly and asks for the id of "number1". When the code loads, num1 is null yet the num1 in the show function gets the value from the input element and it is displayed in the label with the id of output. How can this be? I've tried chrome and edge and got the same result.

"use strict";
const num1 = document.getElementById("number1");
const outp = document.getElementById("output");

function show() {
  const num1 = parseFloat(globalThis.num1.value);
  outp.innerHTML = num1;
}
<div>
  <label for="num1">Number 1</label>
  <input type="number" id="num1">
</div>
<button type="button" onclick="show()">Show</button>
<div>Output <label id="output"></label></div>


Solution

  • This works because elements in your HTML code that have an id attribute set are automatically created as properties on the global window object (ie: globalThis in your case). This behavior is sometimes referred to as "named elements":

    "use strict";
    console.log(window.test); // `test` is a named element
    console.log(test); // ends up accessing window.test
    console.log(globalThis.test); // also same as accessing window.test
    <input type="text" id="test" />

    Above, just because we gave our text input an id of "test", it now creates a global property on window called test that we can access. While this works, it's considered best practice to avoid accessing elements this way and instead use methods such as .querySelector() and .getElementById()

    In your case, you're declaring your num1 variable with const so your num1 variable doesn't become a property on the window (globalThis) object like it would if you omitted const or used var instead. As a result, the automatically added num1 property on the window object remains as is and isn't overwritten by your variable declaration. That means that when you do globalThis.num1, you're not accessing the variable you've declared in the surrounding scope, but instead, are accessing the automatically added window property that you naturally get when you give your element an id (like in the above snippet). That's why your code will work even without the const num1 declaration:

    "use strict";
    // const num1 = document.getElementById("number1");
    const outp = document.getElementById("output");
    
    function show() {
      const num1 = parseFloat(globalThis.num1.value);
      outp.innerHTML = num1;
    }
    <div>
      <label for="num1">Number 1</label>
      <input type="number" id="num1">
    </div>
    <button type="button" onclick="show()">Show</button>
    <div>Output <label id="output"></label></div>


    My suggestion to avoid all of this is to stop shadowing the outer global variable if you need to access it within your function. You can do this by simply calling your variable within your function something other than num1:

    "use strict";
    const num1 = document.getElementById("num1"); // update to the correct value
    const outp = document.getElementById("output");
    
    function show() {
      const num1Float = parseFloat(num1.value);
      outp.innerText = num1Float; // use innerText or textContnt if the content you're setting is just text, not HTML
    }
    <div>
      <label for="num1">Number 1</label>
      <input type="number" id="num1">
    </div>
    <button type="button" onclick="show()">Show</button>
    <div>Output <label id="output"></label></div>