The bug in the program reveals a poor understanding of object lifecycles by whoever wrote it. The `obj` argument to `simple` is not globally unique and so it makes a poor location to store global state information (a count of how often `simple` is called, in this example).
Never tie global state information to ephemeral objects whose lifetime may be smaller than what you want to track. In this case, they want to know how many times `simple` is called across the program's lifetime. Unless you can guarantee the `obj` argument or its `counter` member exists from before the first call to `simple` and through the last call to `simple` and is the only `obj` to ever be passed to `simple`, it is the wrong place to put the count information. And with those guarantees, you may as well remove `obj` as a parameter to both `simple` and `complex` and just treat it as a global.
State information needs to exist in objects or locations that last as long as that state information is relevant, no more, no less. If the information is about the overall program lifecycle, then a global can make sense. If you only need to know how many times `simple` was invoked with a particular `obj` instance, then tie it to the object passed in as the `obj` argument.
> it is the wrong place to put the count information.
I'd argue this is the case regardless of lifetime. It's trying to squash two unrelated things into one object and should have been two different arguments.
Way more obvious if "obj" is replaced with some example object instead of an empty one:
let person = { name: "Foo Bar", age: 30, counter: counter };
My JS is terrible, but it seems like once you make the counter a global variable it is just better to change it to have an atomic dedicated count function. So instead of incrementing the counter in simple, a globalCount() function gets called that isolates the state. Something like
{
let i = 0;
var counter = function (){
console.log(++i);
}
}
Then call counter() to count & log and document that something horrible and stateful is happening. I wouldn't call that a global variable although the article author disagrees.
Because there is no intent, need or reason to vary it.
Nearly everything in RAM is technically a global variable to someone who is keen enough. Programming depends on a sort-of honour system not to treat things like variables if it is hinted that they shouldn't and it doesn't make sense to. Otherwise we can all start making calls to scanmem & friends.
Right but closing over a state with a function and giving that closure a name seems to quack like a duck to me. Maybe it's just because I'm a scheme guy and we're more upfront about this.
The situation is ugly because there is global state by design. But I don't see why the fact that the closure is stored in a mutable location would be a concern for you. Can you think of any conditions where someone would modify it? I'm not really seeing it, and I don't know what your alternative suggestion is going to be to stop them technically.
I can tell you when someone would modify a counter variable - every time it needs to be incremented. It leaves open a lot of room for bugs in unexpected parts of the code. Gating access behind a stateful function makes doing the wrong thing more expensive.
I think we're talking past each other here. The issue is that the enclosed state of counter is exposed globally and can be accessed without synchronization. I suppose yes one could also reassign counter to a different function (and you'd need to be aware of that) but my point is that you still need some kind of sync if you're calling counter all over the place, just like you would with a global variable
Could you tell me where this was posted? I thought no one would see this after I got no comments the first day
No one I showed this to complained about the first example but online many people did. I wrote a completely different article which I think is much better that uses examples I would have used in the follow up. I'll post that article next week
Second chance pool. This post is, per your submission history, from 2 days ago. HN has a second chance pool that lets articles continue collecting upvotes (more easily than digging through the full history). Some of those articles will get their timestamp updated to artificially inflate their ranking. This brings them to the front page again and gives them their "second chance". After a few hours or whatever time, the timestamp is reverted and they'll start falling back into their natural place in the rankings.
In this case, yes. Its scope should be the lowest necessary scope. Does JS provide static variables in functions? If not, then that forces you to lift it to some other scope like file or module scope or the surrounding function or class if that's viable.
Never tie global state information to ephemeral objects whose lifetime may be smaller than what you want to track. In this case, they want to know how many times `simple` is called across the program's lifetime. Unless you can guarantee the `obj` argument or its `counter` member exists from before the first call to `simple` and through the last call to `simple` and is the only `obj` to ever be passed to `simple`, it is the wrong place to put the count information. And with those guarantees, you may as well remove `obj` as a parameter to both `simple` and `complex` and just treat it as a global.
State information needs to exist in objects or locations that last as long as that state information is relevant, no more, no less. If the information is about the overall program lifecycle, then a global can make sense. If you only need to know how many times `simple` was invoked with a particular `obj` instance, then tie it to the object passed in as the `obj` argument.