-
Notifications
You must be signed in to change notification settings - Fork 113
[Private] yet another alternative to #
-based syntax #149
Description
Disclaimer
My intent here is to show that there are good alternatives to existing private
part of this proposal, that were not properly assessed. Probably it happened, because committee has a very limited time and amount of members involved, while such long time for discussion around encapsulation (years or even decades) has lead to loosing clear sight on the problem and possible solutions.
Proposal
It's based on the my proposal in #134 and Symbol.private
- I won't repeat them fully here, but I hope that you may visit these links to get more background.
This comment (#147 (comment)) to my initial proposal inspired me to make another one, which dismisses problem with this
having more complex semantics than it has now.
Goals:
- Prevent confusion with
private x
/this.x
pair - Symmetry between
this.x
andobj.x
- Symmetry between declaration and access syntaxes
Solution
Use Symbol.private
with existing lookup mechanism, existing mental model for accessing properties and small syntax sugar to make use of such symbols more ergonomic, by declaring symbol variable in lexical scope of a class.
Syntax 1 (less verbose):
class A {
// declaring of new private symbols in lexical scope of a class
symbol somePrivatePropertyName;
symbol somePrivateMethodName;
[somePrivatePropertyName] = 'this is private value';
somePrivatePropertyName= 'this is public value with a `somePrivatePropertyName` property name';
[somePrivateMethodName]() {
// it is a private method
}
somePrivateMethodName() {
// it is a public method with `somePrivateMethodName` name
}
methodA(obj) {
this[somePrivateMethodName](); // call private method
this.somePrivateMethodName(); // call public method
this[somePrivatePropertyName]; // access private property
this.somePrivatePropertyName; // access public property
obj[somePrivateMethodName](); // call private method
obj.somePrivateMethodName(); // call public method
obj[somePrivatePropertyName]; // access private property
obj.somePrivatePropertyName; // access public property
}
}
const a = new A();
// throws `ReferenceError` since `somePrivatePropertyName` doesn't exist outside of class A
a[somePrivatePropertyName];
// access public property `somePrivatePropertyName` of `a`, as expected
a.somePrivatePropertyName;
Syntax 2 (more generic):
class A {
// declaring of new private symbols in lexical scope of a class
const somePrivatePropertyName = Symbol.private();
const somePrivateMethodName = Symbol.private();
// everything else is the same as in previous example
// additional possibility of such syntax
let numberOfInstanses = 0;
constructor() {
numberOfInstanses++;
}
}
This option is a little bit more verbose, and probably such syntax isn't obvious enough to use for declaring private instance data, but as I shown above it could be used for something else, which is probably make some sense.
Advantages:
- Doesn't have any problems listed in FAQ
- No new semantics === easy to learn
- Full symmetry === prevents accidental missuses
- Possibility to extend in future, if needed
- Doesn't use
#
-sigil, so no risk for breaking community - Compatible with TypeScript's
private
Disadvantages:
- Not yet implemented
- Slogan
# is the new _
couldn't be used for education
Conclusion
Since I don't pretend to replace existing proposal with this one right now, because, obviously, my proposal also needs additional assessment, preparing document that reflects changes to ES spec, creating tests, implementing babel-plugin for transpiling, adding support to JS-engines and most importantly wider discussion with community (BTW, existing also needs it), I only hope that this clearly shows the fact that there are still good alternatives, which weren't taken into account.
What is your opinion on that @littledan, @zenparsing, @jridgewell, @ljharb?
P.S.
This syntax also adds a bonus usage scenario:
class A {
symbol marked;
markForeignObject(foreignObject) {
if (foreignObject[marked]) {
// do something with knowledge that `foreignObject` was already passed to this method
} else {
// do something with knowledge that `foreignObject` enters here first time
foreignObject[marked] = true;
}
}
}
And it's only one possible scenario, there could be dozens of them.