In other words, if module X returns a value x of unexported type T to module Y, code in module Y is free to do x.foo = 42.
In other words, if module X returns a value x of unexported type T to module Y, code in module Y is free to do x.foo = 42.
To preempt the obvious: yes, I know _everything_ (nearly) in JavaScript is an object, but a module exporting a `Function` can expect the caller to use the function, not enumerate it for methods. And the function can use a declaration in the module that wasn't exported, with the caller none the wiser about it.
But in defence of JavaScript -- since it enjoys routine bashing, not always undeserved -- it now has true runtime-enforced private members (the syntax is prefixing the name with `#`, strictly as part of an ES6 class declaration), but yeah -- this doesn't invalidate the statement "kind of got there 32 years after Ada, stumbling over itself".
class Age {
#value;
constructor(value) {
if(typeof value != "number") throw new Error("Not a number");
this.#value = value;
}
} let x = new Age();
x.notSoOpaque = 42;
console.log(x.notSoOpaque);
We can all agree to layer conventions on top of the language so we just don’t do stuff that violates the opacity. But the same is true of assembly language.