Question about scoping in JavaScript

General project discussion.
Use this as a last resort if your topic does not fit in any of the other boards but it still on-topic.
Forum rules
This General Discussion board is meant for topics that are still relevant to Pale Moon, web browsers, browser tech, UXP applications, and related, but don't have a more fitting board available.

Please stick to the relevance of this forum here, which focuses on everything around the Pale Moon project and its user community. "Random" subjects don't belong here, and should be posted in the Off-Topic board.
User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Question about scoping in JavaScript

Post by UCyborg » 2025-08-01, 19:11

JavaScript is that funny language and I'm never sure if what I'm doing is right, remembering that last time when Pale Moon's garbage collector happily purged away event listener I setup while the same didn't happen in Firefox/Chromium. So...

Code: Select all

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scope test</title>
<script type="text/javascript">
  'use strict';
  window.addEventListener('load', () => {
    let myVar = 'Lost var';

    document.querySelector('#shout').addEventListener('click', () => {
      alert(myVar);
    });
    document.querySelector('#one').addEventListener('click', () => {
      myVar = 'One';
    });
    document.querySelector('#two').addEventListener('click', () => {
      myVar = 'Two';
    });
  });
</script>
</head>
<body>
<h2>Scope test</h2>
<button id="shout" type="button">Alert</button>
<button id="one" type="button">One</button>
<button id="two" type="button">Two</button>
</body>
</html>
To the question, is the variable myVar supposed to survive for the lifetime of the page or may it be randomly eaten by garbage collector at some point?

Kris_88
Board Warrior
Board Warrior
Posts: 1168
Joined: 2021-01-26, 11:18

Re: Question about scoping in JavaScript

Post by Kris_88 » 2025-08-01, 19:44

Yes, it will work.
The myVar instance will survive because of addEventListener('click', =>).

(Although, I don't like arrow functions...)

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-01, 20:06

That's reassuring to read, but it's confusing to me as I'm not sure when it goes out of scope in this case. After the removal of the click event handler, which doesn't happen in this case? Because then nothing is referencing the variable anymore?

I like arrow functions better after I read more about them.

Kris_88
Board Warrior
Board Warrior
Posts: 1168
Joined: 2021-01-26, 11:18

Re: Question about scoping in JavaScript

Post by Kris_88 » 2025-08-01, 20:21

Yes, your click functions are kept alive by addeventlistener, and along with them is kept the context in which these functions are created, and myVar is included in this context. If "load" is called twice, two such contexts will be created and there will be two different myVar variables.

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-01, 20:33

Some decent write-ups that might help you understand a bit better:
https://www.geeksforgeeks.org/javascrip ... avascript/
https://javascript.info/garbage-collection

In general you have to keep in mind that anything between { and } is a scope.
In your example, myVar is defined inside one scope, so are the document-attached EventListeners, so the context object (attached to window) remains referenced as long as the attached eventlisteners are alive, so the object remains alive, and in turn the myVar variable as well. The moment you'd remove the eventlisteners, the context becomes unreferenced and GC will clean it up.
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-02, 05:19

Thanks, so I got it right for the most part.

The following somewhat different case with notifications permission status tracking still strikes me as unusual. Considering the following snippet:

Code: Select all

class NotificationsManager {
  static #notificationsPermissionStatus;

  constructor() {
    throw new TypeError('NotificationsManager is not constructable.');
  }

  static init(changeNotificationsPermissionCallback) {
    if (navigator.permissions) {
      navigator.permissions.query({ name: 'notifications' }).then(p => {
        //p.onchange = changeNotificationsPermissionCallback;
        // Or rather:
        p.addEventListener('change', changeNotificationsPermissionCallback);
        // the callback won't be functional for long if the following line is commented out
        NotificationsManager.#notificationsPermissionStatus = p;
      });
    }
}

// Some other place

NotificationsManager.init(someCallbackFuncToUpdateGUI);
One could assume since you get to the object through special call, browser tracks it internally. Is that supposed to be an internal implementation detail? I'd just like to understand why is there a difference here between Goanna and other engines where commenting out that line doesn't seem to stop callback function from being called. Is it the case that garbage collector is lazier and would still sweep the event handler at some point or is there internal tracking?

I'd assume the latter, at least nothing changes on Firefox after you use those buttons for garbage collecting / minimizing memory usage on about:memory page.

User avatar
Nuck-TH
Project Contributor
Project Contributor
Posts: 331
Joined: 2020-03-02, 16:04

Re: Question about scoping in JavaScript

Post by Nuck-TH » 2025-08-02, 09:48

It is entirely possible that in Chrome/Firefox GC or object tracking are broken and nobody cares. Why should they? - just kill the content process on memory usage threshold overflow and all is fine.

Kris_88
Board Warrior
Board Warrior
Posts: 1168
Joined: 2021-01-26, 11:18

Re: Question about scoping in JavaScript

Post by Kris_88 » 2025-08-02, 12:16

UCyborg wrote:
2025-08-02, 05:19
One could assume since you get to the object through special call, browser tracks it internally. Is that supposed to be an internal implementation detail?
Most likely...
Although, in my understanding, the object pointed to by "p" is not attached to anything and should be freed by garbage collection. Yes, in a bad case circular references can occur, but this should not be a problem.

Of course, the situation changes if you store the reference in a static variable (notificationsPermissionStatus = p).
Note that in your previous example the Node object is attached to the DOM.

There are many similar examples in JS, when there is no need to care about removing the event listener and it is removed automatically along with the element it is assigned to. For example,

Code: Select all

(function() {
let img = document.createElement('img');
img.onload = function () { alert(img); };
img.src = ...
}) ();
This example works because img will not be freed while it is in the loading state. There are similar examples for XMLHttpRequest.

As for the PermissionStatus object, I don't know if its behavior is described in the spec (concerning the garbage collector). Maybe it's some special case.
But, logically, this object should be freed if there are no references to it, and addEventListener should not prevent this.

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-02, 12:44

Additionally, when merely defining a class you're not instantiating it. The scope is only created as part of the document if you instantiate and reference it. So ideally you want to instantiate it and reference it to a variable with new, and then manipulate the created class instance through that reference.

Additionally, event listeners are never strong references because they create a dependence on the object observed. This is also an essential premise for GC to work that is often misunderstood: Just because you are observing something doesn't mean it will remain alive (it's not a Schrödinger's object ;) ). If that was the case, then GC wouldn't be able to do much work as that is a very common situation.
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-04, 16:48

Moonchild wrote:
2025-08-02, 12:44
Additionally, when merely defining a class you're not instantiating it. The scope is only created as part of the document if you instantiate and reference it. So ideally you want to instantiate it and reference it to a variable with new, and then manipulate the created class instance through that reference.
I'm aware, but all members in that snippet are static. I just used class to bring logically related things together, even if static classes may not be the most natural from JavaScript's perspective.
Moonchild wrote:
2025-08-02, 12:44
Additionally, event listeners are never strong references because they create a dependence on the object observed. This is also an essential premise for GC to work that is often misunderstood: Just because you are observing something doesn't mean it will remain alive (it's not a Schrödinger's object ;) ). If that was the case, then GC wouldn't be able to do much work as that is a very common situation.
Ah, good to remember!

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-04, 17:35

UCyborg wrote:
2025-08-04, 16:48
I'm aware, but all members in that snippet are static.
no... no they aren't. The function is a static method, but the inline-declared p is not static or global, nor referenced by anything else. So the moment p goes out of scope (exiting Init()), the entire instance can be GCed; having an eventlistener attached to p, once again, doesn't keep p alive (even if the listener is referenced elsewhere it will simply "stop working" because no events will be occurring without p). So yes, it's required to at least assign p to the static class member to make the reference needed to keep it alive. (all to the best of my understanding how JS classes work -- they are really at odds with the paradigms of JavaScript and should honestly never have been specced.)
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-09, 14:05

I mean, I made an assignment from non-static p to static #notificationsPermissionStatus of that class. That part I do understand why it can work without a problem and doesn't need instantiating a class. I was just curious about why event listener survives anyway on ChromeZilla without that assignment.

Since I didn't actually comment out that assignment to static variable in the snippet and considering I don't have normal members in that class (assigned in constructor) and constructor explicitly throws an error, that's why I can't tell where instantiating a class would come into play in this case. I just used class in this example the similar way Console class is done in C#. It's the first class you'll come across when you start to study that language to output Hello World! to the console:

Code: Select all

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
There are also wildly differing views on using var, let and const in JavaScript. I've seen and admit to copying in the past the approach "use as much const as you can". But when you think about it, the author of You don't know JS has more balanced approach and it may be more sensical (see this page).

Recently, it started to strike me like an unhealthy obsession. Are we even making anything easier for the JavaScript interpreter in most (all?) cases when religiously using it for everything that won't be reassigned?

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-09, 16:37

Off-topic:
The "const disease" is something I see in other languages too, including a lot of it in C++ coming out of Mozilla. No, using const religiously isn't better, per se. In fact, it may make it harder for run-time operation because you're basically bloating the variable space. IMHO it's better to use var in more places, and let when you're using in-scope-only variables you're happy to throw away immediately. i.e. use var "by default" and only use const or let when you're explicitly in need of them or think it has a specific purpose.
I didn't check your link but to me it seems that has always been the premise for most of these classes of languages.
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-09, 16:43

UCyborg wrote:
2025-08-09, 14:05
I just used class in this example the similar way Console class is done in C#. It's the first class you'll come across when you start to study that language to output Hello World! to the console:
I don't know anything about C#, but to me it seems Main() is always a special case, for starters, in c-like languages. So it will implicitly instantiate Program to execute the Main()?
If that's the first example you get though, it is a pretty poor example. You have to always instantiate a class one way or another before you can use it; relying on implicit behaviour/instantiation isn't a good start for people learning how to work with them. But, maybe that's just how C# works?
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

Kris_88
Board Warrior
Board Warrior
Posts: 1168
Joined: 2021-01-26, 11:18

Re: Question about scoping in JavaScript

Post by Kris_88 » 2025-08-09, 17:58

UCyborg wrote:
2025-08-09, 14:05
I was just curious about why event listener survives anyway on ChromeZilla without that assignment.
Kris_88 wrote:
2025-08-02, 12:16
As for the PermissionStatus object, I don't know if its behavior is described in the spec (concerning the garbage collector). Maybe it's some special case.
Indeed, this is a special case. It is, of course, idiotic, but what can we do...
They break the regular structure of behavior and turn the standard into a bunch of exceptions.

6.3.5 Garbage collection
A PermissionStatus object MUST NOT be garbage collected if it has an event listener whose type is change.
https://www.w3.org/TR/permissions/#permissionstatus-gc

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-10, 23:45

Moonchild wrote:
2025-08-09, 16:43
I don't know anything about C#, but to me it seems Main() is always a special case, for starters, in c-like languages. So it will implicitly instantiate Program to execute the Main()?
If that's the first example you get though, it is a pretty poor example. You have to always instantiate a class one way or another before you can use it; relying on implicit behaviour/instantiation isn't a good start for people learning how to work with them. But, maybe that's just how C# works?
At least where I went to school, they didn't start with classes, you just realize at some later point that you were calling static methods of some built-in classes. WriteLine method of class Console is one such method. According to documentation, classes declared as static aren't common, but non-static classes can still have static methods and variables.

Class Program in that example is normal class with static method Main. Main must be static for runtime to be able to invoke it to start the program. Everything happens to be wrapped in classes in C#, you can't have Main outside of class. Non-static methods can only work from instantiated class instance. Public / private status determines whether they're callable only from inside the class (some class method) or from outside as well. If I'm not mistaken, the main difference with classes declared as static is that they're forbidden from having anything non-static declared in them.

So static methods and variables don't need instantiated class instance to be accessible / usable and they're accessed by referencing class name rather than instantiated class instance. They can be private or public. Private ones will be only accessible from another method of the class they're part of while public ones are accessible from outside code.

Static members are just another aspect of classes.

User avatar
Moonchild
Project founder
Project founder
Posts: 38829
Joined: 2011-08-28, 17:27
Location: Sweden

Re: Question about scoping in JavaScript

Post by Moonchild » 2025-08-11, 01:02

UCyborg wrote:
2025-08-10, 23:45
static methods and variables don't need instantiated class instance to be accessible / usable and they're accessed by referencing class name rather than instantiated class instance.
Yes, that makes sense, because of static. It just becomes tricky when you expect variable storage to be usable as well because it has to be referenced in that case either by instance or by having the variable be declared static too (i.e. something defined inside a static method isn't necessarily available outside of it and references can be lost...)
It gets fragile quickly.
"There is no point in arguing with an idiot, because then you're both idiots." - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
UCyborg
Astronaut
Astronaut
Posts: 721
Joined: 2019-01-10, 09:37
Location: Slovenia

Re: Question about scoping in JavaScript

Post by UCyborg » 2025-08-15, 08:53

What about user scripts? Is there a reason to wrap script code in a function?

Code: Select all

(function() {
// script code
})();