-
Notifications
You must be signed in to change notification settings - Fork 110
Make # an object on the instance #75
Description
Update at bottom of description
After reading through the FAQ and issues on the various related repos regarding the sigil I just wanted to run something else by the champions of this proposal.
Please forgive me if this has already been discussed but as I said there are various repos and lots of threads so I may have missed it.
I'm wondering if just making # be a an object property of this makes this proposal more consistent with existing paradigms in Javascript.
For example:
class {
private x = 'x';
method() {
console.log(this.#.x);
}
}Would be sugar for:
(function(){
const __private__ = new Weakmap();
return class {
constructor(){
__private__.set(this, {
x: 'x'
});
}
method() {
console.log(__private__.get(this).x);
}
}
})();Why?
Well I get the need for the # sigil for assesor efficiency but there are a couple of points of this proposal that I find unnessarily restricitve.
- No variable accessors (e.g.
this['#x']orthis[somevar]). This would seem like any use case where you would need this for public properties could also be applied to private properties. - No private property iteration. To me there shouldn't be any reason I shouldn't be able to iterate over private properties without breaking encapsulation. I can think of one use case in particular for a ECS game engine I am working on.
- No destructuring. To avoid having to type
this.#repeatedly it would be nice to be able to use destructuring at the beginning of a method to access private properties.
With this adjustment all of these should be possible without losing any encapsulation.
Variable accessors:
class {
private x = 'x';
private y = 'y';
method() {
const prop = condition ? 'x' : 'y';
this.#[prop] = somecomplexcalc(this.#[prop]) ;
}
}vs
class {
#x = 'x';
#y = 'y';
method() {
if(condition) {
this.#x = somecomplexcalc(this.#x);
} else {
this.#y = somecomplexcalc(this.#y);
}
}
}Private property iteration:
class {
private x = 'x';
private y = 'y';
private z = 'z';
method() {
for(const [prop, value] of Object.entries(this.#)){
this.#[prop] = somecomplexcalc(value);
}
}
}vs
class {
#x = 'x';
#y = 'y';
#z = 'z';
method() {
this.#x = somecomplexcalc(this.#x);
this.#y = somecomplexcalc(this.#y);
this.#z = somecomplexcalc(this.#z);
}
}Destructuring:
class {
private x = 'x';
private y = 'y';
private z = 'z';
method() {
const { x, y, z } = this.#;
}
}vs
class {
#x = 'x';
#y = 'y';
#z = 'z';
method() {
const x = this.#x;
const y = this.#y;
const z = this.#z;
}
}Why private in declaration? (I know it's been talked about but I have to try)
First consistency and readability:
class {
#x = 'x';
@protected #y = 'y';
static method (){}
}vs
class {
private x = 'x';
protected y = 'y';
static method() {};
}Secondly, with # being a property of this instead of part of the name of the property it makes more sense here.
Conclusion
To me, this makes this proposal seem a little less strange compared to the rest of Javascript. It's a simple mental model. All private properties are stored on this.# which is only accessible from within the class methods.
Keeping the sigil for accessors means it's non breaking for any existing code so I'm ok with it there but I really hope we reconsider using private in the declaration just to keep the syntax consistent and readable.
Edited: typo and broken formatting in github
Updated proposal
I wanted to provide an update to my proposal based on the discussions in the thread. I believe that the # should be moved to before this to make it clearer to developers that #this is a lexically scoped variable.
class {
private x = 'x';
method(obj) {
console.log(#this.x, #obj.x);
}
}Would be sugar for:
(function(){
const __private__ = new Weakmap();
return class {
constructor(){
__private__.set(this, {
x: 'x'
});
}
method(obj) {
console.log(__private__.get(this).x, __private__.get(obj).x);
}
}
})();I have also taken the comments of @ljharb and @bakkot into consideration and I have a further addition that will prevent any leakage to mutations.
To avoid any leakage we can make it a syntax error to assign #this to any variable, property, return value, or function parameter. This is actually a really simple syntax check. # must be immediately followed by variable identifier which must be immediately followed by a . or [. With the only exception being for destructuring.
Syntax error example:
const x = #this;
const x = { #this };
const x = { x: #this };
const x = [#this];
somefunction(#this);
return #this;I'm sure there tons I have missed here but as long as they follow those rules there shouldn't be any leakage.
Valid syntax examples:
const x = #this.x;
#this.method();
#this.x++;
#this['x']++;
const { x } = #this; // exception to the rule but doesn't cause leakage
const x = { ...#this }; //exception to the rule but doesn't cause leakageThis prevents any leakage for mutations.
This doesn't fully address the issue with private keyword implying this.x access but I think it comes pretty close.
Edit: I accidentally left out or [ syntax check that I had in my notes here