Open mhofman opened 2 years ago
In my view, no. Other than the return override trick - which is a dumb edge case no one should even be aware of, much less use - private fields are installed when an object is created, and that consistency is an important part of reasoning about them.
I have no problem with restricting private declarations to object literals, especially now that we've finally conceded that { __proto__: null }
is a part of the language.
which is a dumb edge case no one should even be aware of, much less use
This syntactic feature has singlehandedly prevented new additions from being second class again and again. She's a lone warrior fighting off whole armies and succeeding. There are currently at least six primitive capabilities tied to this, and it's dumb, sure ... but it ain't her fault they ended up that way.
Given they can be allocated at any time already, I don't know what "that consistency is an important part of reasoning about them" could mean.
(I don't think it's essential for this syntax to support it as well, but I don't agree with the reasoning given for it.)
Since it's possible to install a private field on any object with the return override trick, I think it must be possible to do so with objects, altho it's fine if it's unergonomic to do so. Object literals must not be second-class to class instances.
Given they can be allocated at any time already, I don't know what "that consistency is an important part of reasoning about them" could mean.
It means if you're looking at a code base, you can reasonably start from the assumption that private fields are added at creation, so you just need to find the place that declares the field and that will tell you what kind of object you're dealing with. This is an entirely reasonable assumption because only extremely unusual code code will violate it, even though it's technically possible to do so; adding an explicit, not-obviously-bad way of adding private fields after creation would make this property not hold.
This is like how you can reasonably assume that, for example, Object.getOwnPropertyDescriptor(obj, "field")
isn't going to change obj
, even though with a Proxy that might not be the case.
I am very strongly opposed to reasoning that just because something is technically possible it must guide the design of the language as if this is a thing users should be thinking about about.
@ljharb
Since it's possible to install a private field on any object with the return override trick, I think it must be possible to do so with objects
I don't know what this means. As you note, "it's possible to install a private field on any object with the return override trick", and therefore "it [is] possible to do so with objects" already.
Should it be possible to add a private field to an existing object after it has been created?
When I argued for Private Symbols instead of the current Private Fields, several of the delegates explained that class field's only-during-creation design is explicitly required to prevent bad usage (I disagree, but oh well).
If I remember right, it's possible that we could add an explicit "initialize" syntax, but it must not be obj.#priv = 1
set access. Since we're only adding syntatic forms, we can't use normal Object.defineProperty(obj, #priv)
to define the property.
FWIW, we would be very interested in finding a mechanism which objects could use to block return-override shenanigans, in particular private field additions. What that mechanism looks like, or if there would be committee interest in specifying it is TBD, but for us it would be moot if a proposal introduces a "blessed" way to add private fields to an object without the object's creator having a say about it.
@bakkot a class private field, yes - but would that remain true with the private declarations added by this proposal?
@ljharb Yes, since you can refer to those in classes (if I understand the proposal correctly):
private #x;
function addPrivateXToArbitraryObject(o) {
class EvilReturnOverride {
constructor(o) {
return o;
}
}
class Child extends EvilReturnOverride {
outer #x; // refers to the `private #x;` declaration
constructor(o) {
super(o);
}
}
new Child(o);
}
Sounds good, then i'm fine with only allowing it on object literal creation (since return override allows it later)
I would appreciate a method of adding after creation. I'm currently using a WeakMap to store private data on functions, but would rather use private fields.
Simplified example:
private #ref
function connect(emitter: (callback: () => void) => void, ref: object) {
emitter.#ref = ref;
}
There would be no way to initialize the emitter (a function) with the #ref
private field, even if all creation of emitters was limited to the same scope of the private declaration.
I no longer think my use case of initializing a private field after creation is necessary or desirable. If I were still in need of such a pattern then using a stamper class to initialize the private field would work (with or without this proposal).
Should it be possible to add a private field to an existing object after it has been created? If yes, should there be a mechanism for the object to "opt-out" of getting a private field added? If no, that would restrict private declarations to be added only syntactically through something like the
outer
in the current description, and not through "Set" semantics.