{"action":"created","issue":{"url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/14","repository_url":"https://api.github.com/repos/tc39/proposal-private-fields","labels_url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/14/labels{/name}","comments_url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/14/comments","events_url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/14/events","html_url":"https://github.com/tc39/proposal-private-fields/issues/14","id":114516425,"number":14,"title":"Why not use the \"private\" keyword, like Java or C#?","user":{"login":"melanke","id":1337983,"avatar_url":"https://avatars.githubusercontent.com/u/1337983?v=3","gravatar_id":"","url":"https://api.github.com/users/melanke","html_url":"https://github.com/melanke","followers_url":"https://api.github.com/users/melanke/followers","following_url":"https://api.github.com/users/melanke/following{/other_user}","gists_url":"https://api.github.com/users/melanke/gists{/gist_id}","starred_url":"https://api.github.com/users/melanke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/melanke/subscriptions","organizations_url":"https://api.github.com/users/melanke/orgs","repos_url":"https://api.github.com/users/melanke/repos","events_url":"https://api.github.com/users/melanke/events{/privacy}","received_events_url":"https://api.github.com/users/melanke/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":31,"created_at":"2015-11-02T03:14:48Z","updated_at":"2016-06-15T09:57:50Z","closed_at":"2015-11-19T16:49:24Z","body":""},"comment":{"url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/comments/226142282","html_url":"https://github.com/tc39/proposal-private-fields/issues/14#issuecomment-226142282","issue_url":"https://api.github.com/repos/tc39/proposal-private-fields/issues/14","id":226142282,"user":{"login":"epicmiller","id":7856443,"avatar_url":"https://avatars.githubusercontent.com/u/7856443?v=3","gravatar_id":"","url":"https://api.github.com/users/epicmiller","html_url":"https://github.com/epicmiller","followers_url":"https://api.github.com/users/epicmiller/followers","following_url":"https://api.github.com/users/epicmiller/following{/other_user}","gists_url":"https://api.github.com/users/epicmiller/gists{/gist_id}","starred_url":"https://api.github.com/users/epicmiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/epicmiller/subscriptions","organizations_url":"https://api.github.com/users/epicmiller/orgs","repos_url":"https://api.github.com/users/epicmiller/repos","events_url":"https://api.github.com/users/epicmiller/events{/privacy}","received_events_url":"https://api.github.com/users/epicmiller/received_events","type":"User","site_admin":false},"created_at":"2016-06-15T09:57:50Z","updated_at":"2016-06-15T09:57:50Z","body":"@zenparsing, I'm not entirely sure I see why we can't achieve what @yanickrochon proposed above with the `private` keyword. \r\n\r\nLets entertain the thought for just one more moment and play with this, can we? :)\r\n\r\nGood 'ol C++ take an approach that many programming languages seem to take, (Java and Ruby do something pretty similar, no?). Contrived example:\r\n``` c++\r\n#include <iostream>\r\nusing namespace std;\r\n\r\n// Base class Person with a private `name` and `sayHello` property.\r\n// Public class `talk` calls `sayHello` on the instance, and another passed Person.\r\n// All methods defined in this class will *ALWAYS* use the private `this->name`, regardless of the instance's public properties.\r\nclass Person\r\n{\r\npublic:\r\n   Person(string n){\r\n     this->name = \"Person \" + n;\r\n   }\r\n   void talk(Person a){ \r\n     this->sayHello();\r\n     a.sayHello(); \r\n   }\r\n\r\nprivate:\r\n   void sayHello() const { cout << this->name << \" Says Hello World\\n\"; }\r\n   string name;\r\n\r\n};\r\n\r\n// Class Client extends Person and has a public `name` property.\r\nclass Client : public Person \r\n{\r\n    public:\r\n        Client(string n) : Person(n) {\r\n          this->name = \"Client \" + n;\r\n        }   \r\n        string name;\r\n\r\n};\r\n\r\n// SecretClient once again makes `name` private.\r\nclass SecretClient : public Client\r\n{\r\n    public:\r\n        SecretClient(string n) : Client(n) {\r\n          this->name = \"SecretClient \" + n;\r\n        }\r\n\r\n    private:\r\n        string name;\r\n};\r\n\r\nint main()\r\n{\r\n   Person person (\"Adam\");\r\n   Client client (\"Melissa\");\r\n   SecretClient secretClient (\"Trevor\");\r\n\r\n   /* \r\n      Log: \r\n         Person Adam Say Hello World\r\n         Person Melissa Says Hello World\r\n   */\r\n   // Note that it uses the private Person.name value in the Person.sayHello method.\r\n   person.talk(client);\r\n\r\n   /* \r\n      Log: \r\n         Person Melissa Say Hello World\r\n         Person Trevor Says Hello World\r\n   */\r\n   // Note that it uses the private Person.name value in the Person.sayHello method.\r\n   client.talk(secretClient);\r\n\r\n   // Attempting to access Person.name results in an Error\r\n   // cout << person.name << \"\\n\";\r\n\r\n   // Accessing Client.name returns the public Client.name value\r\n   cout << client.name << \"\\n\"; // Log: \"Client Melissa\"\r\n\r\n   // Attempting to access SecretClient.name results in an Error\r\n   // cout << secretClient.name << \"\\n\";\r\n}\r\n```\r\n\r\nC++ Classes seem to abide by these rules:\r\n - Private property may be overridden by Public values on Classes that extend it.\r\n - Public property may be overridden by Private values on Classes that extend it.\r\n - Inside of the Class scope if a private value exists, it is *always* used.\r\n - If a private property is the highest on the inheritance chain, accessing it on the instance throws.\r\n\r\n(I haven't worked heavily in C++ in a long time, does anyone know if there is a way to access the instance's value instead of the private value in a Class with a private value definition?)\r\n\r\nI propose using something like C++'s lookup scheme, but with a couple tweaks:\r\n 1) Private properties defined in a class are *not* stored on the prototype chain, but in a separate \"private slots\" map. The class's prototype has `undefined` set as this key's value – enumerable, not-writable, and non-configurable.\r\n 2) As such, for all property accessors running outside of a class scope, accessing a private variable will just return `undefined`, as though the property is not present – which it isn't. The majority of lookups remain unchanged. \r\n 3) Because the prototype and property lookup remain unchanged, any classes extending this class with a public variable of the same name will see that public variable available to the public.\r\n 4) If the property access is happening inside of a class declaration, first check to see if this is a private variable name in this context. If so, use the private value, else fall back to prototype chain value – accessors in class declarations will always use the private slot value over a prototype chain value.\r\n\r\nLike @yanickrochon, I unfortunately don't know much about implementation of the JS engines (working on it!), so I'm not sure how difficult determining \"if the property access is happening inside of a class declaration\" is, but barring a slow solution to that, I think we're looking at a fast private property lookup here because it is backed by a similar \"private slot\" method as the current proposal.\r\n\r\nSo this will give us:\r\n - Fast property lookup times\r\n - Attractive private property syntax\r\n - Parity with other industry standard languages\r\n\r\nOutstanding questions:\r\n - Should there be a way to force regular prototypical lookup inside of a class scope? (And how do you do that in C++? I'm curious!)\r\n - Would it be better to allow regular prototypical lookup to just fall through to previously defined public values rather than setting it to `undefined`? What are the pros/cons?\r\n - How difficult is it to detect if a property accessor on the instance is happening inside of the class declaration?\r\n\r\nIn JavaScript the above example would look like this:\r\n``` javascript\r\n\r\n// Class Person has a private property `name`. This private property will *always* be used for methods defined in this class, regardless of the instance's public variables.\r\nclass Person {\r\n   constructor (n) {\r\n     this.name = \"Person \" + n;\r\n   }\r\n   talk (a) { \r\n     this.sayHello();\r\n     a.sayHello(); \r\n   }\r\n\r\n   private var name;\r\n   private sayHello() { \r\n      console.log(this.name + \" Says Hello World\\n\"); \r\n   }\r\n\r\n};\r\n\r\n// The Client class extends Person and makes `name` public.\r\nclass Client extends Person {\r\n   constructor (n) {\r\n      super(); \r\n      this.name = \"Client \" + n;\r\n    }   \r\n\r\n    var name;\r\n};\r\n\r\n// SecretClient makes `name` private again, overriding anything set by Client.\r\nclass SecretClient extends Client {\r\n   constructor (n) {\r\n      super(); \r\n      this.name = \"SecretClient \" + n;\r\n   }\r\n\r\n    private var name;\r\n};\r\n\r\n   var person = Person(\"Adam\");\r\n   var client = Client(\"Melissa\");\r\n   var secretClient = SecretClient(\"Trevor\");\r\n\r\n   /* \r\n      Log: \r\n         Person Adam Say Hello World\r\n         Person Melissa Says Hello World\r\n   */\r\n   // Note that it uses the private Person.name value in the Person.sayHello method.\r\n   person.talk(client);\r\n\r\n   /* \r\n     Log: \r\n         Person Melissa Say Hello World\r\n         Person Trevor Says Hello World\r\n  */\r\n   // Note that it uses the private Person.name value in the Person.sayHello method.\r\n   client.talk(secretClient);\r\n\r\n   // Attempting to access private Person.name returns undefined\r\n   console.log(person.name); // Log: \"undefined\"\r\n\r\n   // Accessing public Client.name returns the public Client.name value\r\n   console.log(client.name); // Log: \"Client Melissa\"\r\n\r\n   // Attempting to access private SecretClient.name returns undefined\r\n   // console.log(secretClient.name); // Log: \"undefined\"\r\n}\r\n```\r\n\r\nTo address some previous concerns above:\r\n > You'd have to somehow encode a \"private key\" into each lexical environment.\r\n\r\nUses same proposed private slot hash, just different differentiator for changing between private slots and prototype lookup.\r\n\r\n > You'd have to change the semantics of each and every property access (because any property access might result in a private field). Engines are highly optimized around the current property lookup semantics.\r\n\r\nCurrent property lookup would remain largely the same. Only lookups inside of class definitions scopes would be different, and it should be (hopefully) no slower than the proposed private slot solution.\r\n\r\n > Would for-in enumerate over these properties?\r\n\r\nPresumably not.\r\n\r\n > Can someone shadow a private property with a normal property lower on the prototype chain? What about the reverse?\r\n\r\nNope, uses same private slot solution with different lookup criteria.\r\n\r\n > How do you prevent leaking the names of private fields to clients that shouldn't know that information? This is probably a fatal information leak.\r\n\r\nNo leakage! External clients see the normal prototypical lookup like nothing is different\r\n\r\nI probably just re-hashed something that you've already beaten to death in a less public venue, but I thought I'd take one more stab at making what sounds like the generally preferred syntax work. Eager to hear what you think!"}}
{"actor":{"display_login":"epicmiller"}}