Accessing private fields without the need of a sigill #52
Comments
Implies a run-time cost on all private property access. Non-starter. |
Apologies, that we dismissive. Continue on! : ) |
It really would be nice if people read the frequently asked questions though: https://github.com/tc39/proposal-private-fields#frequently-asked-questions |
@domenic the two FAQ topics point to very long and exhaustive debate on the matter. There are people that actually try to suggest ideas in separate threads to help make things easier to follow. As far as this ides goes, it is actually compatible with Crockford's Private Members in JavaScript. Where class A {
private buffer, pos;
method () {
buffer[pos++] = 'hello'; // instead of this.buffer[this.pos++] = 'hello'.
}
} behaves somehow similar to function A() {
var buffer, pos;
this.method = function method() {
buffer[pos++] = 'hello'; // instead of this.buffer[this.pos++] = 'hello'.
};
} Except that |
I disagree that starting new threads to rehash old grounds is in any way an improvement. |
@domenic I've understood the reasons. But this idea will not affect property access algorithm at all. So that FAQ does not apply. @zenparsing It's one single check against a Set. And it's only done from within the getter and setter functions. And in 99% of the cases, private properties can be accessed without |
If you add a hash-set lookup to every property access in JavaScript, you're going to make the web much slower. This is indeed affecting the property access algorithm, and this is the same argument as before. It's the same non-starter as before. But indeed, continue on! |
@domenic Not affecting any existing property access. |
Seriously, what is the difference between class A {
private buffer, pos;
method () {
buffer[pos++] = 'hello'; // instead of this.buffer[this.pos++] = 'hello'.
}
} and const A = function A() {
var buffer, pos;
return new class A {
method () {
buffer[pos++] = 'hello'; // instead of this.buffer[this.pos++] = 'hello'.
}
};
} and function A() {
var buffer, pos;
this.method = function method() {
buffer[pos++] = 'hello'; // instead of this.buffer[this.pos++] = 'hello'.
};
} if all of them need the lookup to bubble up to the parent context? The second example could clearly be a potential implementation of the proposal, here. |
@yanickrochon wrote:
That is essentially the pattern I have always used for private members in JS. But it has a problem. You can't access the private members of |
Let's say that adding a private field means adding a getter/setter pair to the prototype, which does some kind of access control magic. Some issues:
The last one kills it for sure, because one of the goals of the proposal is to allow the full implementation of built-in classes and platform classes directly in Javascript itself. |
@shelby3 I personally use Personally, this is a minimal argument, as private fields are accessible via reflection in pretty much all programming languages anyway. But I believe most people would rather have hard privates than soft privates... Again, personally, I'd be alright to have |
@zenparsing I see the downside with sharing namespace with public properties even though I'm not yet convinced it's a showstopper. You may be right as you usually are. But I hope you understand that there are some of us that are concerned about the implications of introducing a new sigill. What we're trying to achieve is to offer a surface for the end user developer that is in line with the user expectation, is uncomplicated, self-explanatory and is trying to avoid new syntax rules as much as ever possible. The idea is to combine @eggers proposal with this-less access as an optimized block-level shortcut to the private properties. This-less access would solve your current concerns, but there seem to be a requirement to allow methods like
I see. But this-less access will shortcut the lookup and make them as fast sigill-based this-less access. Dot-based access only optional.
Also here, it only applies to dot-based access. The use case for dot-based access is primarly a for accessing
True.
Why couldn't they? Does the standard ever forbid the addition of reserved property names? |
@dfahlander Yeah. I'm really leaning toward this-less access. It mirrors how people do private fields in js today so is a small context switch. You could even let it be prefixed with |
Disclaimer: I'm new to the entire proposal, been reading this repository for the last hour. Not sure if this is the right place to put this but perhaps it contributes something to the discussion. So, I was wondering, if we'd rather not introduce a sigill and may even like using the Have been tinkering with this idea for a while before I came across this proposal. A quick draft of what it might look like: class Foo {
/* Use `private` keyword to define a property only accessible from within this class */
private x;
/* Supports initial value */
private y = 5;
constructor(x, y) {
/* Access from anywhere within the class via the `private` keyword */
private.x = x;
private.y = y;
}
hello(name) {
/* In standard methods as well */
console.log('x equals ' + private.x + ' and we\'re saying hello to ' + name);
}
/* Normal (non-private) getter we'll use later */
get y() {
return private.y;
}
/* We could implement `private` for methods as well */
private multiply(a, b) {
return a * b;
}
/* Private getter, would be accessible as `private.xtimesy` */
private get xtimesy() {
/* Accessing private method, private field and normal instance field */
return private.multiply(private.x, this.y);
}
}
|
@DvdGiessen this has been suggested before in #14 "private" cannot be used for access since "private" is a valid property name. |
@DvdGiessen The problem with the |
@dalexander01 That only seems to be a problem when considering how we access private fields of other instances (as @littledan noted)? In the discussion at #14 you mentioned a solution like @littledan Perhaps we could use the keyword in a language construct, parametrizing the target instance? Doing so wouldn't introduce any new keywords and does not overlap with any existing valid syntax. class Bar {
private x;
constructor(x) {
/* `private` is in this case equivalent to `private(this)` */
private.x = x;
private(this).x = x;
}
equals(other) {
/* Accessing private fields of other instance */
return private(other).x == private.x;
/* `private(obj)` construct checks if(!(obj instanceof Bar)) throw new TypeError(); */
}
}
let a = new Bar(5);
let b = new Bar(10);
let c = new Bar(10);
a.equals(b); // false
b.equals(c); // true |
@bakkot Both points mentioned in your comment in #14 (as well as related comments in that thread) are addressed by the notion that The return value is known. As noted that And, regarding nesting, it would look like |
This is not a minor issue; we shouldn't be introducing things which aren't functions which look like functions.
Having
My point was that this is aesthetically displeasing and difficult/confusing to read (compare what you've written to |
@bakkot There is a stage 2 proposal for |
// while(...) looks like a function call:
while(0)
console.log(1);
// for(in) looks like a function call:
for(p in {})
console.log(p in {});
// super() looks like a function call:
super(1)
console.log(1); |
This would actually make a lot of sense. And is consistent with the language. A lot more than a sigill. However, I'd rather have it be more consistent, always using parenthesis. class Bar {
private x;
constructor(x) {
private().x = x; // same as private(this).x = x;
}
equals(other) {
/* Accessing private fields of other instance */
return private(other).x == private().x;
/* `private(obj)` construct checks if(!(obj instanceof Bar)) throw new TypeError(); */
}
}
let a = new Bar(5);
let b = new Bar(10);
let c = new Bar(10);
a.equals(b); // false
b.equals(c); // true |
The only issue with this is to avoid these cases class Bar {
private x;
a() {
// should throw TypeError (or the non-standard InternalError)
return private();
}
b() {
// should throw TypeError (or the non-standard InternalError)
const p = private();
return p;
}
} Basically, a variable cannot be assigned whatever |
It's not a bad alternative, but there are two usability issues:
|
I don't necessary agree with this. Even the current source code is using only but a few underscored variables. Private variables should not be overused anyhow. They represent a state, and there is so much states an object can hold. |
I think we're discussed this issue pretty thoroughly, and I don't see a way we can make this work consistently from a technical perspective. Let's stick with the sigil. |
As discussed in #14, many people advocates for using the
private
keyword for declaring the private fields. But that discussion has went on further into how we should access the properties. It seems that the sigill (# or @) is something that could also be questioned for accessing the fields. So let's bring that up in this separate issue.@eggers came with a very interesting solution that I've been examiniting a little. It allows private access without a sigill and without changing the property lookup algorithm in any way. My own working example can be found here - uses a WeakMap to store the properties. You need a modern browser to open it!
This way, it would be possible to:
...but also:
EDIT: this-less access would also make optimization possible because it would be obvious at parse time what property is being accessed and the parser already knows that it is allowed and can omit having to check against the white-list
Another sample:
The text was updated successfully, but these errors were encountered: