To answer your question about WeakMaps, I think you are talking about side effects. When using a WeakMap you can imagine that there is no userland side effect to setting a value because WeakMaps invert the usual relationship between item and collection. In a WeakMap, for reasons of garbage collection, the item maintains a hidden link to the collection (rather than the collection maintaining a reference to the item), so we can pretend there is no side-effect.
But, this question is also about privacy in JavaScript.
As it happens, JavaScript has always supplied an outstanding (and impenetrable as far as I know - unlike private fields in languages like C#) mechanism for robust privacy: closures. It's just that most developers come from a class-based object-orientation background and take time to learn the "JavaScript way".
That said, syntax for marking fields on classes as private is currently at Stage 3 of the feature development process, and is supported by transpilers.
So the answer is: there are multiple ways to ensure privacy, depending on the precise use case and the style of code your team is using.
Teams using classes will probably use the # private class field syntax.
class MyClass {
#myPrivateField = 'foo'
bar(s) {
return this.#myPrivateField + s
}
}
Teams using functions will use closures.
function createObject() {
let myPrivateVariable = 'foo'
return {
bar(s) {
return myPrivateField + s
}
}
}
You can nit-pick this and say that the function-object for bar
is created on a per-instance basis. In the vast majority of cases this won't matter.
Symbols will sometimes be used, although these offer lesser privacy because they can be viewed by reflection.
function createObject() {
let secretSymbol = Symbol('secret')
return ({
[secretSymbol]: 'foo',
bar(s) {
return this[secretSymbol] + s
}
})
}
WeakMaps and modules can also be used. For more details see the links below.
Note that you could also use a class with a closure to ensure a variable remained private.
See also, and.