-
Notifications
You must be signed in to change notification settings - Fork 19
Why not use the "private" keyword, like Java or C#? #14
Comments
Consider this scenario: class X {
private a;
constructor(a) { this.a = a }
swap(otherX) {
let otherA = otherX.a;
otherX.a = this.a;
this.a = otherA;
return otherA;
}
} Let's call let x1 = new X(1);
let x2 = new X(2);
x1.swap(x2); // --> 2 In this case, the reference to What if we call let x1 = new X(1);
let obj = { a: 3 };
x1.swap(obj); // --> TypeError or 3? Without a type specifier on the
We could just say that within the body of That's why we need to syntactically distinguish between private field lookup and regular property lookup. There are a couple of different ways that we can distinguish them:
I think the second option is the most natural. Unfortunately, either option uses up one of the two last remaining operator characters (the other being the ugly-duck I hope that helps explain the rationale. I'll follow up with a comment exploring the other option that I mentioned above. |
I actually don't see anything wrong with the example you provided, even after reading a few times, there's nothing worng with using Consider the last snippet
In the code, since it's all being used inside the class and either the However, if such function would be given :
Then a You are not giving a convincing example to reject this proposal, you are just assuming that there should be a difference in the interpretation of The only valid argument to reject In the README, you are giving parsing directives, but do not describe how these properties are represented at run-time. Currently, properties are defined as |
@yanickrochon If I understand your proposal correctly, you'd change normal property lookup to something like this: Given
I believe this idea was considered early on and rejected, although the rationale probably isn't captured anywhere. Think of what you might need to do to make that work.
You could perhaps come up with answers for all of these questions (except for the last), but you'd end up with something highly complex and confusing. Instead, we're going to use the idea of private slots, which are completely different from JS object properties, and we're going to use a new slot lookup syntax. It's all very simple and easy to think about. And it requires no essential extension to the JS object model. Built-in JS objects like Promise are already specified to have just the same sort of private slots that we are proposing here. We're just providing a way for classes written in JS to have the same kinds of slots. Also, see zenparsing/es-abstract-refs#11 for more information on why we don't want to use a "private" version of Symbols here. |
Yes, you got what I meant right. And I do understand the extra overhead with adding extra property definition attributes. While I know JavaScript quite well, I have not worked under the hood, on the engine itself, so I am not fully aware on how it is actually implemented in details. I was sure that it was possible to tell if a given property was being accessed from within the same context where it was declared without relying on the property's definition, just like
Well, yes, however pretty much any information can be known through "reflection" anyway, even if not exposed or allowed direct access. Java, for example, makes it possible to modify private fields through reflection... While this is not a good thing, it is nonetheless possible and does allow better DI. I am not a great fan block-boxing everything, since JS is not compiled and anyone have access to source code anyway. Makes the effort pointless for run-time. The idea of "private" properties is not essentially to hide them, because this can already be achieved through Classes are supposed to be a semantic feature and should not de-nature the language itself with OO paradigm. |
Says you! : ) One of the explicit goals of this private state proposal (or any other that has been brought forward) is to allow the creation of "defensive classes", which are indeed secure at runtime. As a subgoal, we want to allow things like DOM classes (and JS built-ins) to be expressible in JS itself. |
class X {
private a;
constructor(a) { this.a = a }
swap(otherX) {
let otherA = otherX.a;
// if otherX.a is private then otherA is undefined
otherX.a = this.a;
//if otherX.a is private it will create another variable named a only for this scope
this.a = otherA;
//if otherX.a was private then this.a is undefined now
return otherA;
}
}
let x1 = new X(1);
let obj = { a: 3 }; //a is public here, isn't possible to define private fields this way
x1.swap(obj); // --> 3 |
This looks awful. I associate hash symbols with commented-out code. JavaScript has quite a clean syntax – please don't ruin that. C++, C#, Java, PHP, TypeScript (and probably others) all support I can't comment on the technical details of the implementation, but there must be a way of solving this in an elegant and efficient manner. |
@glen-84 Thanks for commenting. I agree that a leading hash looks terrible. We can't use a private keyword, but maybe there are other leading characters that would look better. What do you think about @? |
At signs are already "reserved" for decorators, so you'd end up with: @dec @num = 5; I want to write: @dec
private num = 5; // or private static num Your description in this comment is how I would have imagined it to work. |
I don't want to assume anything about decorators (their syntax or semantics) at this point... Thanks for linking to my comment : ) I think it's a good documentation of why that solution won't fly. |
Perhaps you're right, but they're already being used via Babel and TypeScript, so I'd be very surprised if the basic syntax changed.
Like I said, I can't really comment on the implementation, but I still don't believe that this is the only solution. |
For the record, decorators are at stage 1, meaning that proposal is just as likely to change as this proposal. |
Agree with @zenparsing on almost everything. Disagree about Although we should strive for syntax which is initially more intuitive when possible, when it is not, don't underestimate how quickly novel syntax becomes natural on repeated exposure. |
This is sad. Sure, we can get used to anything (I "got used" to writing PHP for like a decade, because I had to), but that doesn't mean that it's not ugly AF. I sincerely hope that one or more of the implementers agree, and that a solution can be found that doesn't result in the syntax being butchered. |
Please excuse me for being late to the discussion. |
Verbosity. Btw, speaking of verbosity, I prefer |
Can there be a block level class Stack {
const stack = [];
let top = -1;
constructor() {}
push(value ){
stack.push(value);
top += 1
}
pop() {
stack.pop(value);
top += 1
}
isEmpty() {
return top === -1
}
} |
Funny you should mention that. I like @sebmarkbage 's https://github.com/sebmarkbage/ecmascript-scoped-constructor-arguments proposal. However, since it leverages the intuitions around lexical variable capture, it should only be used to express instance-private instance state, not class-private instance state. Thus it would enhance both private fields and class properties, rather than replacing private fields. For both, it would solve the otherwise unpleasant issues #25 , tc39/proposal-class-public-fields#2 , and wycats/javascript-private-state#11 , by allowing constructor-parameter-dependent initialization expressions. Starting with https://github.com/sebmarkbage/ecmascript-scoped-constructor-arguments , it would seem to be a natural extension to also allow |
What about class Stack {
static const base = 100;
static let increment = 0;
const stack = [];
let top = -1;
constructor() {}
push(value ){
stack.push((base + value) * increment);
top += 1
increment += 1;
}
pop() {
stack.pop(value);
top -= 1
increment += 1;
}
isEmpty() {
return top === -1
}
} |
What would it mean? |
Seriously? ...well, here's another example class Thing {
// static private field
static let count = 0;
static create(name) {
return new Thing(String(name));
}
// private field bound to instance
let originalName;
constructor(name) {
this.name = originalName = name;
count += 1;
}
getName() {
if (this.name !== originalName) {
return count + '@' + this.name + ' (' + originalName + ')';
} else {
return count + '@' + this.name;
}
}
}
new Thing('foo').getName();
// -> "1@foo"
new Thing('bar').getName();
// -> "2@bar"
let t = Thing.create('test');
t.name = 'something';
t.getName();
// -> 3@something (test)" |
@yanickrochon two quick questions
|
@sarbbottam I ran out of ideas 😛 It's only meant to prove a point. The method is useless in itself, but only show usage in syntax. As for |
This use would be equivalent to just placing the Nevertheless, if we allow instance |
@erights indeed, the |
I cannot think of a worse way to design a language. |
I just wonder why do we want two properties with the same name, one public and one private. Just disallow two properties with same name in one class and bury this ugly # syntax. |
@DaSchTour please see the FAQ where that's addressed. |
this.#proposal sucks!!! |
@MarcoMedrano TypeScript definitely has these problems, and its "private" is fully public at runtime - proper privacy, I'm told, is one of their most requested features. Please see the FAQ for why "hard private" is the choice that's been made. |
@zenparsing Well, I must say that @ is marginally better than #. And, I am afraid this thread goes way above my pay grade, so I won’t even bother trying to persuade you to use private, but please please please, pretty please, try and make private work :) |
@zenparsing Just out of interest could we use var to prefix private variables like:
|
Private fields are operational in the very browser you're using. The ship has sailed and suggesting new syntax at this point is pointless except as a thought exercise. |
That ship has not reached international waters yet (reached stage 4). It can still be recalled, though it's unlikely. Speaking of which, why? If TC39 is in such a hurry to get this ... proposal, warts and all, into the language, why hasn't it reached stage 4? |
Nobody’s “in a hurry”; this proposal has taken many, many years. Stage 4 requires at least two shipping web browsers, and i suspect that this proposal’s champions will want as much experience as possible before advancing. Separately, things can be “recalled” after stage 4, and can be “un-recallable” before - web compatibility is what’s most important here, which is why stage 3 is the time for implementations. If there’s sites that depend on a feature, browsers are unlikely to break them, and that includes this one. |
The day this |
@1valdis ECMAScript isn't just JavaScript though. TBH I've been using the new hash now and tbh it's relatively clean and easy to type. I've changed my mind and the sigil now, it's less intrusive than using private keyword. Plus less rewrite of the meaning of "this" |
Though somewhat disagreeable, either that sigil or something else would need to be used even if "private" were the declarative keyword. That is a fixed issue. That TC39 has implicated '.' for "private" declarations is another. Using the BTW, the "intrusiveness" of the "private" keyword is intentional. It makes it clear at a glance that something different is going on with this key, as opposed to |
@CavidM In those examples, there is no absolute hard privacy. In Java, having a private property X means that you can not have a public property X that is accessible in public scope, which means in certain cases the existence of the private property can be detected (putting aside Java reflection which allows accessing private properties). In the PHP example, it is trivial to detect the private field because trying to access it from the public side leads to a detectable runtime error. So in Java and PHP, public code can detect the presence of, or even use, private properties. But TC39 wanted absolute hard privacy, meaning that at runtime it should be 100% impossible to rely on being able to detect the private properties of an object, and definitely impossible to read them. How this proposal achieves true hard privacy, is that running something like This makes it impossible to detect private variables like you can in PHP by for example detecting an error by trying a property access and catching the error (and perhaps reading the message for information). Try running this in your console: class Foo { #foo = 123 }
const f = new Foo
f.#foo In this new proposal, the existence of That's why, we can't simply allow You'd need to propose something that adheres to the constraints if absolute hard privacy, but |
Although I don't like |
End of story: |
To be honest, I would have preferred the option of both hard and soft privacy. Hard privacy could have used the The ship has sailed, the cat is out of the bag, the bed is made, etc. What's done is done and I guess TS will always have the In any case I am sorry for the notif on this as well, it's a little annoying to get notifs on this dead horse |
I like your thinking, except I would use |
Any would work - the real feature would be making the class a block scope for variables or scripting logic |
@sndst00m that already exists, and just like this proposal, is stage 4. https://github.com/tc39/proposal-class-static-block |
@ljharb If memory serves me, the point of the |
Essentially the braces of |
Ah, I misunderstood. I'm pretty confident that kind of design has been explicitly rejected on multiple occasions, but I don't have notes off hand. |
That's a real shame considering that design not only would likely have been the easiest to implement, but also the easiest to understand given that it would have just been binding a closure to an instance and giving developers something they already conceptually understood as the implementation of private. |
Can functions called with |
No function that did not get defined within the scope of the class definition will ever have access to private members. Trying to late-bind them in will not work. |
In my opinion, It is terrible that a private field is marked with a # prefixes. A |
No description provided.
The text was updated successfully, but these errors were encountered: