# Variable hygiene in JS

Gold Member
TL;DR Summary
Making sure I'm not using any more global variables than necessary
To skip the humble-bragging, jump down past the screenshots and descriptions to "Anyway"...

For my client, I made this one-page vanillaJS app that shows a heat map of counties serviced by their three professions (if they add a fourth I'm screwed):

The Niagara county is serviced by one PNSW and one Doula, so its hotspot is a pale blue-green:

If you want to only see Registered Nurses, you can turn the others off using the trefoil-like legend:

There's also a "just show me the data" overlay:

That's it. That's the whole app.

Behind the curtain, it has JSON objects (so, read-only and manual update) to serve as database tables, with about 100 data points, and is growing enough that soon I may have to redo the app in a proper database. (I hate that. My app is entirely contained in a half-dozen files and can be moved anywhere without an IT guy to set it up.)

Anyway...

When I built v1.1, it was simpler, and my JSON object(s) were to be read directly. As I add more data, I'm adding functionality, and now my raw data needs to be "massaged" before use. For example:

The Greater Toronto Area - a conglomeration of several counties is very very often used, so I've added some "sugar". Instead of this clunky array:
Code:
var workers = [
["Alice", "RN", ["Toronto", "Peel", "York"]],
["Betty", "RN", ["Toronto", "Peel", "York", "Niagara"]]
];
it is now
Code:
var GTA = ["Toronto", "Peel", "York"]];

var workers = [
["Alice", "RN", ["GTA"]],
["Betty", "RN", ["GTA", "Niagara"]]
];
So, I've got a utility in the JS that expands ["GTA"] to its various counties and then strips out any inadvertent duplicates.

Unfortunately, when I wrote this, didn't care about using global variables. So workers array is global and is accessed directly whenever it is needed. I can't do that anymore, and I've got to make sure my code never takes advantage of global variables.

The obvious solution is "use strict".

But if I put that at the top of my js file, all it will do is ensure I don't use any undeclared variables; it won't ensure I don't use global variables. I think what I need is to have all data get pulled in through functions. I guess if I only allowed data to be accessed via getData() functions, then that goes a long way. But is there a way of preventing access to global variables as a form of code-checking i.e. my code will break if I miss one and try to access some global variable somewhere?

berkeman

Homework Helper
Gold Member
I think it's time to move away from monolithic javascript. Split your code up into modules and use a bundler (I recommend Rollup) to produce production bundles. This opens up the possibility of using Typescript which will make everything much more robust and easier to maintain, and/or also introducing unit and integration testing to the build process.

Or you might want to move straight to a front end framework (React, Vue, Svelte) which will come with a whole development tool chain, and also preferred engines for state management (Redux, Pinia, Svelt Store) which will be much more robust than the POJO (Plain Old Javascript Object) solution you have acknowledged is straining its limits without the burden of introducing a RDBMS.

jim mcnamara
Homework Helper
Gold Member
But is there a way of preventing access to global variables as a form of code-checking i.e. my code will break if I miss one and try to access some global variable somewhere?
Oh I forgot to address this: https://eslint.org/.

Gold Member
I think it's time to move away from monolithic javascript. Split your code up into modules and use a bundler (I recommend Rollup) to produce production bundles. This opens up the possibility of using Typescript which will make everything much more robust and easier to maintain, and/or also introducing unit and integration testing to the build process.

Or you might want to move straight to a front end framework (React, Vue, Svelte) which will come with a whole development tool chain, and also preferred engines for state management (Redux, Pinia, Svelt Store) which will be much more robust than the POJO (Plain Old Javascript Object) solution you have acknowledged is straining its limits without the burden of introducing a RDBMS.
Oh God no!
All those things are the reason I got OUT of JS dev.

Me: "I like to build one-off classic automobiles. How can I ensure I use the right rivets?"

Henry Ford: "OK, build a giant factory that takes up 6,129 times more space, takes 37 times more time to set up, 678 times more work to ensure it keeps working, and 4,521 times longer to figure why it isn't working. You now live here in this factory town and will never move because it's way too much effort to move. Also, you're no longer an automobile builder; now you are a factory manager, and managing the factory is now what you will spend the rest of your hours doing. You might get down on the factory floor and pick up a wrench once or twice, but don't count on it."

Filip Larsen
Homework Helper
Gold Member
Me: "I like to build one-off classic automobiles. How can I ensure I use the right rivets?"
But a one-off classic car is not a good analogy for the software lifecycle. It's more like

"I want to start off with a bicycle, then I need to go faster so turn it into a motorbike but keep the same saddle because it's really comfortable, now I need to carry more stuff so I need a van but I still want to keep all the things I have attached to the handlebars, now I need to go zero carbon so convert it to all-electric...."

So you either have a process that builds a bicycle, and a different one for a motorbike, and another one for a van (but this one doesn't work with handlebar attachements at all so how are we going to deliver that?)... Or you have a process that lets you start simple with wheels, frame, handlebars, saddle and refine or replace each of the components over time as the needs change.

Anyway I didn't say you have to go straight to an all-singing all-dancing framework, my suggestion was to simply to break up your code into modules something like the below and use Rollup to bundle them together.
JavaScript:
// workers.js

// Note store each worker as an object, not an array, e.g.
// { name: 'Alice', qual: 'RN', regions: ['GTA', 'Mannitoba'] }.
// This is MUCH more future-proof.
const workers = [];

const gtaRegions = ['Toronto', 'Peel', 'York'];

const getWorkersInRegion = (region) => {
if (gtaRegions.includes('GTA')) {
return workers.filter(({ regions }) => {
return regions.includes('GTA') || regions.includes(region);
});
}
return workers.filter(({ regions }) => regions.includes(region));
};

export { getWorkersInRegion, getRegionsForWorker, addWorker };

Homework Helper
Gold Member
And I don't agree with some of your numbers:
Henry Ford: "OK, build a giant factory that takes up 6,129 times more space,
So what? RAM is cheap.

takes 37 times more time to set up,
Oh at least - you are not doing any set up at all (and are paying the price down the line).

678 times more work to ensure it keeps working,
If that is the case you are doing it wrong. It should take 1/10 of the time to keep it working.

and 4,521 times longer to figure why it isn't working.
If that is the case you are really doing it wrong. If a functional test worked 20 minutes ago and now it doesn't you know that the code you wrote in the last 20 minutes broke it.

You now live here in this factory town and will never move because it's way too much effort to move.
You don't need to move, you can just do a bit of home remodelling. Compare that with what you have got - you daren't even put up a picture in case it puts a hole in a pipe.

Also, you're no longer an automobile builder; now you are a factory manager,
Not at all. Becuase your automobiles come together quicker and work first time you can spend MORE time, not less, on making them better rather than hitting them with a hammer and kicking the tyres until they work (for a while). Better product, more reliable, less time, more fun for you and better value for the client.

Gold Member
I do appreciate your sentiment, but I reiterate: I did this for 25 years, and I lost interest because it became all about framework work. I like my little one-man car shop; I don't want to manage a factory.

And no, that doesn't mean I have to forego good code hygiene.

(Truth be told, I did build my last site in Angular, so it's not like I'll get struck by lightning if I set foot in a factory.)

pbuk
Gold Member
Regarding your pattern for constructing data that relies on evaluation of global variable (I agree, yuck!), perhaps you can build your data inside a function and then actually insert the common parts where needed.

Sort of (handwaving some JS here):
JavaScript:
function buildWorkers() {
var gta = ["Toronto", "Peel", "York"]
var workers = [
["Alice", "RN", gta],
["Bob", "RN", gta.concat(["Niagra"])
]
return workers
}

DaveC426913
Homework Helper
Gold Member
(Truth be told, I did build my last site in Angular
Oh no wonder you hate the idea of frameworks! My last word on this is to suggest you try something less opinionated, perhaps Vue? You don't even need a build process, you can load it in page from a CDN.

But frameworks !== modular code: separating presentation logic from business logic is still vital to creating a robust application in any language.

And if you want to avoid mistakes with variable scoping then you at least need eslint in your toolkit.

Homework Helper
Gold Member
JavaScript:
function buildWorkers() {
// ...
return workers;
}
No, that's just as bad. If you expose the workers object to the UI logic then you can't make any changes to that object without inspecting every line of code to see if it is affected. UI logic should only access this data via accessors like getWorkersForRegion, getRegionsForWorker, addRegionToWorker etc.

Staff Emeritus
"I want the features of X but don't want to use X" is a story that does not always have a happy ending.

pbuk and berkeman
Gold Member
It was a suggestion for a simple change compared to the OP starting point on how to avoid a web of global variables, and its based on the common javascript mechanism of using a function to "hide" internal structures. Take a look at https://www.dofactory.com/javascript/design-patterns for more examples.

Yes but the problem is that by exposing the workers object you haven't "hidden" anything, you have simply replaced a global object with a global function that returns that object. Instead of this you should be exposing getter and setter methods, and methods to persist the data to and from localstorage/files/remote APIs etc.
by exposing the workers object you haven't "hidden" anything