-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What would it mean to drop branding? #10
Comments
Again, this isn’t just an issue with Proxy - all the builtins (except Array) that have internal slots will cause some prototype methods to throw when .call-ed on them. Putting “pre-existing” in quotes doesn’t change that it does, in fact, exist already. Would this object itself be exotic? Would it be sealed, but with all of its fields writable? What happens when that object is passed around? This seems like it would add a lot of complexity over the current proposal. |
I thought one of the reasons for branding was for cases where it's possible that duck typing could give misleading results. If it weren't for cross-realm issues I would say this could just be implemented in user-land and isn't something the language needs to provide, but without the ability to create a cross-realm constant I can understand why existing solutions are considered unsatisfactory. |
@ljharb I'm confused. Did I not make it clear already that the combination of Proxy and anything using private slots is a "pre-existing" problem? I put it in quotes because the simple fact that there's a hole in your path doesn't require you to fall in it if you know its there. Yet that's what class-fields is doing. I don't wish to discuss that subject further as it is not the point of this thread. Please direct your comments toward the actual issue being presented. That's why I tagged you in. You have a perspective that is valuable to me (especially because its an educated opinion that differs from my own). |
@mbrowne Can you help me understand how branding affects cross-realm issues? I already get the idea that if an object in another realm, it's prototype will be a different object from it's match in the current realm. What I don't get is why branding is of any benefit in that case. Can you give an example of how duck typing leads to a problem? |
Branding allows nominal typing, which is different than structural (duck) typing. If you want nominal typing, you can’t rely on a public shape/interface, nor can you rely on |
Yes, exactly what @ljharb said. I believe the reason for the term "duck typing" in the first place comes from the idea, "if it quacks like a duck, then it's a duck". But what if it's a person making quacking noises? The goal is to create a mechanism for determining what class an instance belongs to that can't be fooled. |
I don't think it would need to be but I'm open to other arguments.
This is what I'm currently anticipating.
It can't be passed around. The only access to its properties is via operator
That's true. Compared to the current class-members, this does introduce a bit of complexity. However, Proxy needs to be able to work regardless of whether or not there are private fields. The proper solution would be to allow Proxy to unilaterally tunnel private slots that are not [[ProxyHandler]] and [[ProxyTarget]]. But I hope the fact that this isn't already the case means there was a very good reason for not doing so from the beginning. |
Here's a more academic perspective, for what it's worth... As far as the early object-oriented thinkers were concerned, objects should be like black boxes—total encapsulation of instances. Alan Kay defined objects as being like mini-computers sending messages to each other on a very fast network.* So from that perspective, brand checking is technically a violation of encapsulation. It would probably be better if there were a way to accomplish it without breaking encapsulation of instances, but in practice I don't think this is a real concern. I think a more proper solution would need to be built into the language from the beginning, i.e. I think it's too late to avoid cross-instance access for JS. * This is a metaphor, but interestingly the model can be applied to real network communication as well, with microservices behaving like an object graph. Incidentally there has been some recent exploration in this area—applying the actor model to microservices. |
I think the expression is: "If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck." So a human making duck-like quacks doesn't work. What if it's a duck in a thin, clear plastic suit that doesn't prevent the duck from doing things? Does that still qualify as a duck? That's the Proxy case. With branding, unless we do something, we won't be able to recognize the duck just because it's wearing a suit. Poor Donald.
That would be true if we were working with the original model of object-oriented, where all properties of an object were always what we're calling "own properties". Prototype-based OO is like having a computer that, when it doesn't know the answer, consults another computer close at hand and returns whatever that computer said as its own answer. This wasn't within the considerations of the original OO thinkers. |
You're right of course. I realize it wasn't a flawless analogy but I think you got the point ;-)
What do prototypes have to do with private instance variables? |
I don't recall saying that prototypes had anything to do with private instance variables at all. What I said is that the concept of OO as originally conceived didn't include the possibility of a prototype-based language. Please don't get distracted. Are there any problems with the solution that I've presented above? Are there any issues I need to consider? |
I think you’re forgetting smalltalk when you make claims about how OO was originally conceived. |
Likely, but even Smalltalk didn't fully reflect the original concept of OO. It was colored heavily by the developers' then-present understanding of general programming. In either case, what I'm looking for is an understanding of what you think about my attempt to create Proxy-safe branding. |
To me, branding is a requirement, and as I’ve said, “proxy-safe” imo is something that should be solved for all language values, and until then, none. |
While I understand your opinion on the issue of "proxy-safe", that means little to nothing to developers who want to be able to use both private data and Proxy on the same object. TC39 failing to implement internal slot tunneling is what prevents proxy safety. So if we avoid using internal slots to implement private, we can avoid the pothole in the road. This doesn't require modifying Proxy at all. So, since there is no modification to how Proxy works, the goal of fixing Proxy can be put off until the TC39 members who don't want Proxy to tunnel internal slots either leave the board or change their minds. As long as Proxy isn't being modified, there's nothing wrong with making private data proxy safe. Certainly you can agree to this much. |
It’s not about internal slots being the implementation of private, it’s about the conceptual precedent they set. No, i do not agree - i think it would be wrong to add tunneling for private fields when internal slots did not tunnel. iow, imo no matter how they are specified or described or implemented, they should both behave the same wrt Proxy. |
Sorry, but that seems illogical to me. If the private container is a property of the object (even if the name is hidden), then there is no reason that it should behave like a slot. Further, not interfering with Proxy ensures private data will remain the usefully non-interfering thing we want it to be. This approach doesn't require Proxy to unwrap at any time other than where it currently does. That means we're not "tunneling" anything. That's the point of this idea. Skip the thought of tunneling altogether. |
Ok, I'll put it a different way: I don't think prototypes should have anything to do with brand checking. I'm not sure how relevant that is. Here's a separate question that's probably more relevant: should a brand check return true for both instances and proxies wrapping instances? What if you want to be sure that you're dealing with the original class and not a proxy? (I'm not sure if that would be an important distinction in practice, I'm just raising the issue for discussion.) |
Just realized that my statement was probably confusing:
I was referring only to the mechanism of doing the brand checking. Obviously the whole point is to validate the class and attached prototype. Anyway let's set this aside; sorry for distracting on this point. |
It’s not a property, it’s a field. Properties are public. |
To be clear: prototypes have absolutely nothing to do with either the mechanism behind brand checking or this idea.
Are Daffy, Donald, Daisy, Hewey, Dewey, Louie, Scrooge, and Launchpad still ducks even though they wear clothes? Proxy, while not intended to be 100% transparent, are still intended to be mostly translucent. Unless the behavior of the object involves internal slots, it should just work when Proxy-wrapped.
If you want to do that, then in the var assert = require("assert");
class X {
let self = null;
let noProxy = () => { assert(this === this::self); }
constructor() {
this::self = this;
}
someFunction() {
this::noProxy();
//Other actions
}
} So it's definitely possible to be sure you're not being proxied. It's impossible to spoof the value of |
This comment has been minimized.
This comment has been minimized.
I get what you're thinking but you going down a road I don't intend to follow. ES currently has no concept of what a "field" is. This proposal doesn't include the concepts contained in class-fields, and that "instance property declared in the |
This comment has been minimized.
This comment has been minimized.
@rdking not sure why you hid the very on-topic comments - private instance data is not acceptable unless it behaves like an internal slot. Regular properties of a magically private container object stored on the instance, to me, is a much more complicated mental model that simply isn't worth exploring. |
I deleted my previous comment. @rdking I realized afterwards that when you said "instance property", you were talking specifically about the idea described in this issue description, not just the existing class-members proposal as-is. So then, I gather that this new concept of a private instance property would replace the currently-documented concept of instance variables, is that correct? (For some reason I previously thought you meant to introduce this as an additional feature, i.e. "in addition to" not "instead of" instance variables.) |
Exactly. This would be a replacement of the core mechanism for this proposal. |
@ljharb The only reason I didn't hide your latest comment is because you related it back (albeit poorly) to the topic at hand.
You are just as free to feel that way as you are to exit the discussion. I would feel somewhat disheartened to find you so inflexible though. "Magically private"? Not at all. Re-read the description in the OP. Private by means of a private Symbol as discussed by @zenparsing. The difference is in how the private Symbol is being used. Where he wanted to make it a publicly create-able key type like Symbol, I see it as an implementation detail to remove the private container from public accessibility |
As for issues of the mental model, this may actually be even less complicated than the original model of this proposal. The idea is that private members are just properties of an object that is itself a property of the owning object. Essentially, the model is this: If that's complicated, then ES may already be too hard of a language to learn. |
I just realized something...isn't it already possible to use WeakMaps for brand checking? It's nice that you offered duck typing as a workaround, but (1) it's not equivalent to nominal typing (as we discussed) and (2) maybe WeakMaps are already sufficient. (Convenience is nice and all, but is it worth it in this case?) |
One other thing: JS currently has both regular properties and symbols. The current mental model (or at least my current mental model) is that both of these belong directly to objects, even though the reflection methods group them separately. Introducing a new container concept for private members would probably cause confusion...so are public members in a container too, or are they at the top level and only privates are in a container? Are symbols their own container? etc. But I suspect that all of this has more to do with how you would choose to explain the proposal than the spec of the proposal itself. I suspect that there would be no problem going with the idea in the OP and that it could be explained either way, for better or worse. |
@mbrowne duplicate names can't be prevented, because if you choose |
@ljharb I'm not following. When I first brought this up in class-fields, all the technical arguments given against duplicate names depended on the hard privacy requirement, i.e. those arguments would no longer hold without it. Suppose for the sake of argument that private members were always soft private (meaning available to some core reflection API), with no way to make them hard private. In that case there could be a policy ensuring it wouldn't be possible to "stick a |
@mbrowne In the presence of private data, the mental model for objects changes, regardless of the proposal. In general, objects will appear to have 2 distinct property bags. Both bags equally contain properties of the object. The difference is that while the object is willing to share one bag, the other bag is held in secret. |
Do you believe this is necessarily true for any OO language with private instance state, or only for JS? "2 distinct property bags" implies a structure, e.g.:
That's not my mental model of Java or C#, and I doubt that thinking about it that way is very common. But now that I think about it, C++ syntax probably encourages such a mental model, so perhaps it depends in part on experience with / exposure to C++. |
Only for JS. I'm not particularly worried about the mental model of other languages. My only concerns are to find a way to map the concept of data privacy into ES in a way that:
For any ES proposal I provide, these are the primary constraints I follow. I rail as hard as I do against class-fields because it violates all but the 1st and 4th of these constraints. If I were a TC39 member, I would have openly vetoed this proposal reaching stage 3 on these grounds. I get how much work has been put into it, and I understand the pressure the board is under to release some version of private. However, there should be some minimum agreed-upon standard for quality of a proposal. Given that there are members of the board who are still vocal about the deficiencies of that proposal, its obvious that the minimum standard has not been met, even if it hasn't been clearly defined. |
As a point of reference, the suggestions I made in the OP brings me closer to the 3rd and 4th constraint.
As for the remainder of the proposal:
|
@ljharb, since |
The default should be a balance of what’s most common, but also what’s safest. In this case, the safety of a brand check by default (and the danger of lacking one by default) imo is the most important. It’s also useful to note that part of the reason these checks aren’t common is likely because they’re not ergonomic to achieve. |
Have you ever though that your use-cases could be less common for other developers? |
@ljharb When you refer to "brand-checking", do you mean only the strict, WeakMap/WeakSet-like check that is capable of determining whether or not the object is distinctly a product of the constructor? Or does your meaning afford a slightly weaker variant that is merely capable of ensuring that the needed private members are in existence? |
BTW: To everyone, please review the updated README.md as it exists in the "private-symbol-approach" branch of this repository. |
@rdking it's a bit of both; the primary goal is ensuring that the needed private members are present, the secondary assurance is that it means that the constructor, and the superclass chain's constructors, have all ran as part of setting up the initial instance. |
@ljharb So, unless I missed something, it really is only about ensuring that the target object has all the necessary required members. Since the only initialization that cannot be done outside the constructor is the staging of the private members, then branding is only meant to ensure that all required members (even from base classes) are present. Would you say that's a fair summary? If so, then why has there been chatter about the notion of branding being more strict than this? It's made my understanding of the subject less clear. |
@rdking I just re-read this whole thread and I don't understand why it's necessary to create a new container object for the private properties. Why couldn't you use a separate private symbol for each property, and keep everything at the top level of the instance? So an object would simply be a collection of slots with the same structure as it currently has, with the only difference being that in addition to regular properties and public symbols, keys could also be private symbols—just as in the private symbols proposal (unless I'm misunderstanding something about how that proposal solves the proxy issue). I think that would be a much simpler model that would also adhere more closely to JS's underlying object model as inspired by the Self language—what you've often referred to as JS's prototypal heritage, that you want to remain true to. |
Regarding brand checking, I also want to say that I was mixed up about what was being discussed when I made some of my earlier comments on this thread (as was probably obvious). In case anyone else is not crystal clear on it, first of all it's important to note that "brand checks" have previously been discussed in different contexts. As someone who hasn't been following the most recent discussions on brand checking in class-fields, it took me until about halfway through this thread to realize that you were only discussing what happens natively, not anything related (at least not directly) to user-land brand checks. I went back and looked at an earlier discussion about this; here's a quote from @ljharb:
@ljharb, I assumed at the time that you meant that literally (although given the different usage more recently of the term "brand checking" now I'm less sure)...I guess something like the following (using class-fields syntax since that was the original context): (Brand-checking example 1) (() => {
const BRAND = Symbol('MyClass')
class MyClass {
#brand = BRAND
static isMyClass(obj) {
return obj.#brand === BRAND
}
}
return MyClass
})(); It can alternatively be implemented using a WeakMap (a downside is that this increases memory usage of course): (Brand-checking example 2) (() => {
const instances = new WeakSet()
class MyClass {
constructor() {
instances.add(this)
}
static isMyClass(obj) {
return instances.has(obj)
}
}
return MyClass
})(); In more recent discussions, the usage of the term has been quite different. As @Igmat put it in tc39/proposal-class-fields#134:
I now realize (and hopefully my understanding is correct) that this is the only meaning @rdking had in mind in the OP of this thread, i.e. ensuring that the following code would throw an error: class A {
let x
foo(obj) {
obj::x;
}
}
class B {}
const a = new A()
a.foo(new B()) // error A somewhat related topic is binary methods, for example: class User {
let socialSecurityNum
...
isSamePerson(user) {
return user::socialSecurityNum === this::socialSecurityNum
}
} Note how my "Brand-checking example 1" above relies on this same ability to access private members of other instances of the same class. This is why some of my earlier comments in this thread talked about that. And I suppose that the increased memory usage in "Brand-checking example 2" was the reason for @ljharb's comment (from the much earlier thread) that isArray()-like brand-checking methods "can't be done in user land easily/performantly/elegantly otherwise". I'm not bringing all of this up to re-discuss all of it (and I realize this is a long post) but rather to try to be very precise about what we're discussing, because it can get quite confusing especially when you bring proxies into the mix. I think @rdking's updated readme in the "private-symbol-approach" branch is probably sufficiently clear about the intended meaning of "brand checking", but I would just like to remind everyone that it's always helpful to clarify the context when discussing this, especially for the sake of people who might have heard the term "brand checking" before and already have past associations with the term. But I would like to establish for certain whether or not the cross-instance private member access in "Brand-checking example 1" is seen as a requirement for '"isArray"-like methods that brand check', even if it's not directly relevant to this thread, because it's certainly still relevant to this proposal. |
And while I'm writing long comments, I might as well write them all at once, so I just wanted to point out one more thing which is that although a suit is probably still a good analogy for a proxy, it's important to recognize that their are scenarios in which the different identity matters. Just to give one of many possible examples: const someSet = new Set()
class MyClass {}
const obj = new MyClass()
const proxy = new Proxy(obj, {})
someSet.add(obj)
someSet.has(obj) // true
someSet.has(proxy) // false Personally I think of a proxy as just a classic example of delegation, just supported by the language rather than done manually. The proxy receives messages (property access or method calls) and delegates them (or not) to the original object as it sees fit. Yes, it's accurate to think of the proxy as a wrapper, but it's still two separate (albeit closely related) objects communicating with each other. But as I said, I still think the spacesuit is a pretty good analogy overall. |
@mbrowne, could you please clarify your position. |
Ultimately I don't think brand-checking should filter out proxies, no (although I might be open to reconsidering my opinion if there were some convincing argument I haven't seen yet). In other words, in the long term, I agree that the proxy spec should be modified so that calls to internal slots are forwarded just like they are for public slots. I defer to @ljharb on the question of when and how to make that adjustment to the spec; I'm not informed enough on all the relevant considerations to form an opinion on that. And BTW as far as the class fields proposal is concerned, I think this proxy issue is an acceptable temporary downside given @ljharb's comments. It's an edge case that won't even necessarily affect everyone currently relying on proxies, as far as I understand it. In the meantime, if property-like semantics for private members solves the proxy issue, great, but please let's not unnecessarily group all private properties in a nested object if a simpler model is possible. The more important question is whether a private symbol implementation is a good fit for private members generally, and I think it is. But I'm still not entirely convinced that implicit brand checks are an absolute requirement. While I think duck typing isn't a strong enough guarantee and agree with brand checking from that perspective, it seems to me that the whole need for implicit brand checks would go away if instance encapsulation were absolute, i.e. if cross-instance private member access were impossible. (Not sure if I'm correct about that though.) WeakMaps and WeakSets might be acceptable workarounds for the use cases that would otherwise require class-level instead of instance-level encapsulation. However, assuming that private symbol / property semantics are a workable solution, this point is irrelevant. And I'm not really opposed to class-level encapsulation anyhow; I was bringing it up mainly in case removing this requirement could somehow help with the proxy issue. |
@mbrowne if cross-instance access were impossible, how could i write a static comparison method, that used private data? How could i write any static methods that used private data? Even without cross-instance access, what about .call-ing instance methods? |
The same technique that Babel uses to transpile private fields: const _ssn = new WeakMap()
class Person {
name
constructor(name, ssn) {
this.name = name
_ssn.set(this, ssn)
}
// writing this as a static method rather than instance method since
// that's what you asked about
static checkIfSameSSN(person1, person2) {
return _ssn.get(person1) === _ssn.get(person2)
}
} Sure, it's a lot less ergonomic but it's all about tradeoffs. I'm not convinced myself that it's a good idea to require an inconvenient workaround like this, but in this example and many others, the private member (
I'm not sure what you had in mind; here's something totally contrived: class X {
let privateProp
foo(x1, x2 /* x1 and x2 are instances of X */) {
x1.bar.call(x2)
}
bar {
this::privateProp;
}
} I can't think of any real use cases, so this seems irrelevant to me (and I don't see why this would require implicit brand checking to be applied generally, only in the case of call/apply), but I'm sure you have some. But let's set aside further discussion of cross-instance access, at least for now—it's probably a moot point. |
Private symbol keys can be shared. Whether on purpose or by accident, that makes the property keyed by that symbol public. The approach I've taken hides the key names in the ES engine. That removes the need for name sugar and still successfully avoids death by Proxy without giving up branding. As I understand it, these hurdles aren't things Self had to take into consideration. As for brand checking: all instances of the same As for the suit analogy: of course it's sometimes important that the suit is not the wearer. It's the very fact that the suit is not the wearer that affords the wearer the additional capabilities (like surviving in space, being able to see despite unfiltered sunlight, being able to talk in a vacuum, etc...). That's what makes it a good analogy. ;-) |
I'm not sure which keys you mean when you say, "The approach I've taken hides the key names"--the key for each private property, or the private symbols for the container objects (one per class)? I don't get how you can guarantee that the private symbol and the keys on the object it points to are completely hidden and inaccessible and yet you wouldn't be able to do this in the case of multiple private symbols and without a private member container object. But if that's absolutely the only way you can do it without causing significant problems, I'll take your word for it; this isn't a deal-breaker for me. But you might consider moving the technical specification details to a separate section at the bottom of the readme and focus the rest of the readme on usage and the developer mental model. I think the average developer is much more concerned about the outward behavior than the implementation details, and I think explaining how there's a private symbol that's internally used to access a container object of private properties is an advanced topic that should be avoided in introductory explanation. And as I said above, I think multiple container objects are the wrong metaphor...at the very least don't use the word "objects" (outside of explanations of the internals)..."property bags" is better at least. |
In any given inheritance chain, each
Unfortunately, unless private symbols are completely hidden behind syntax in such a way that they cannot be set into a public property or variable, it's possible to unintentionally leak the private key, making the private data public. Likewise, even if syntax is used to hide the individual property keys behind friendly names, monkey patching can still be used to gain access. The only way to guarantee that the private names remain safe from tampering is to prevent them from being exposed. As for the advice about the developer mental model being in the readme, I didn't realize the readme was used for that purpose! Thanks for the advice. |
That's how I see it at least. Not just the mental model, but any introductory info for the unacquainted. Notice how the readmes for class-fields, decorators, and classes 1.1 use the readme in this way and provide supplemental documents for more technical (or other) details. But I see no reason why you couldn't have a brief technical summary of the spec changes in the readme if you wanted, as long as it were in its own section and linked to other documents if needed to keep it concise. All of this is just my personal opinion of what makes a good readme for a spec proposal :) |
@mbrowne About cross-instance access: The point of that feature isn't simply for copy/comparison operations. Consider inheritance. Most uses of this feature come up due to polymorphism. If you have 2 distant cousin instances and one of them requires the other to perform a given internal operation, it can't be done if the cousins can't access each other's internal data. It's a weak example, but try this: class TextFile {
let source = null;
let data = null;
let convert = (encoding, text) => {
//returns (text || data) in the appropriate encoding
}
constructor({source, text}) {
if (source) {
this::source = source;
data = fs.readFileSync(source);
}
else {
data = convert(`utf-16`, text);
}
}
clear() {
source = null;
data = null;
}
save(file) {
if (typeof(file) == "string") {
source = file;
}
if (typeof(file) == "string") {
fs.writeFileSync(source, data);
}
}
update(overwrite, offset, content) {
//inserts content into data at offset if !overwrite
//otherwise overwrites content at offset in data
}
//shared === protected. Will exist in a side proposal.
shared extract(offset, length, encoding) {
//retrieves a fragment of the text in a given encoding
}
}
class Document extends Text {
/* Adds support for arbitrary markup */
}
class PDF extends Document {
static from(text){
//Reads data in from other text formats and converts it to PDF
}
/* Adds pdf markup to the document */
}
class MarkDown extends Document {
/* I think you get where I'm going */
}
class SourceCode extends Text {
/* Adds code completion and syntax highlighting generic support. */
}
class ESCode extends SourceCode {
/* Adds support for ECMAScript */
}
var src = new ESCode("someFile.js");
var pdf = PDF.from(src); How do you suppose we do that last line without cross-instance access? Protected implementations are at there most useful when inheritance trees run deep. Similar issues also come up with composition-based designs, especially when it takes more than 1 instance of a given type to perform an operation. |
If you want As an aside, I'm not sure that "property" is the best term for the And while I'm discussing terminology, I still think |
Not really protected members are private members with slightly relaxed constraints.
I get it, but it's almost the same question. Protected properties declared by some base present them selves to derived classes as private members. A protected member is conceptually a private member that's been shared with it's descendants. It's still a member of the private container. That's why I used it as an example. If siblings can't access each other, then the code I mocked up won't work either.
I understand your reasoning, but try mine. Private properties need to be understood as being as much properties of the object as their public counterparts, the only difference between them being the need to use a special new operator( It's not so much that they are
but rather that they're properties that have been added to the scope chain via "with". Not something easily explained if the properties of the context object don't include the private members. This is also one of the reasons I started using the word "member" to describe all the things accessible from an instance. Properties that aren't publicly accessible don't feel much like the properties we're all used to. |
This one is a tough question for me. The reason some people think branding is an issue may look like this:
The code above throws if
other
isn't an instance ofX
. That's not really a problem. If the developer intends to allow the function to accept duck typed objects, then it could easily be re-written as such:and everything would work fine. The real problem is Proxy. Since Proxy doesn't tunnel internal slots, a Proxied instance of
X
would look no different than a duck typed object, except that the Proxy would bethis
, causing a failure on the first line ofsum
. It should be noted that this is an issue for all but theSymbol.private
approach, which keeps private data as properties of the instance object.Here are my initial thoughts on this matter.
I like branding, as I don't see a good reason to ever expect that a non-instance will ever have any of the private fields declared by X. At the same time, Proxy is a ridiculously big issue that class-fields brushes aside as a "pre-existing" issue. This is not something I'm willing to do as Proxies are an extremely useful tool even if I'm not trying to create a Membrane.
What if there was a single, guaranteed, non-writable, non-configurable, non-enumerable, non-reflecting property that always exists on every object with an object as its value. Using this as the private container (as opposed to a closure) would guarantee that Proxy could continue to work in its currently (imo)crippled state without interfering at all with this proposal. Access would still be through operator
::
. The key here is that every unique "class" would have it's own "private Symbol" name for that property. This means eachclass
would have a "private Symbol" name for the private property that would be shared by its instances. Object literals, however, would each have a distinct "private Symbol" of their own, only accessible by functions defined in the literal's declaration.You might think of this as an integration of private Symbols and this proposal to remove the Proxy limitation without needing to remove branding. There's no reason why the 2 concepts can't exist together. It all just works if we make private Symbols an implementation detail for this proposal. Since the private Symbol for the container is referenced by operator
::
, and operator::
is only valid within the lexical scope of the Object/class declaration, it's impossible to share the name. But because it's a property reference, Proxy doesn't cause an issue, and we get what we want.Done this way, we get to keep branding, but since the check just looks at a named property of the object, we get private without losing Proxy.
Side notes
Other capabilities with this approach include:
So what are your thoughts?
@hax @Igmat @mbrowne @ljharb @shannon @zenparsing @trusktr
The text was updated successfully, but these errors were encountered: