{"action":"created","issue":{"url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/79","repository_url":"https://api.github.com/repos/tc39/proposal-class-fields","labels_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/79/labels{/name}","comments_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/79/comments","events_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/79/events","html_url":"https://github.com/tc39/proposal-class-fields/issues/79","id":292283786,"number":79,"title":"Preserve all current syntax, but add support for dynamicity","user":{"login":"EthanRutherford","id":5589147,"avatar_url":"https://avatars0.githubusercontent.com/u/5589147?v=4","gravatar_id":"","url":"https://api.github.com/users/EthanRutherford","html_url":"https://github.com/EthanRutherford","followers_url":"https://api.github.com/users/EthanRutherford/followers","following_url":"https://api.github.com/users/EthanRutherford/following{/other_user}","gists_url":"https://api.github.com/users/EthanRutherford/gists{/gist_id}","starred_url":"https://api.github.com/users/EthanRutherford/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/EthanRutherford/subscriptions","organizations_url":"https://api.github.com/users/EthanRutherford/orgs","repos_url":"https://api.github.com/users/EthanRutherford/repos","events_url":"https://api.github.com/users/EthanRutherford/events{/privacy}","received_events_url":"https://api.github.com/users/EthanRutherford/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":8,"created_at":"2018-01-29T04:49:20Z","updated_at":"2018-02-04T20:09:41Z","closed_at":"2018-02-04T20:09:41Z","author_association":"NONE","body":"(Branched from https://github.com/tc39/proposal-class-fields/issues/75, to avoid having multiple conflicting conversations in one thread any longer)\r\n\r\nThe following syntax:\r\n```js\r\nclass Foo {\r\n    x = 0;\r\n    #y = 5;\r\n    method() {\r\n        this.#z = 10;\r\n    }\r\n    #privateMethod() {\r\n        return this;\r\n    }\r\n    #arrow = () => this;\r\n    addNewMethod() {\r\n        const name = Math.random().toString();\r\n        this.#[name] = function() {return this.x;};\r\n    }\r\n}\r\n```\r\nTransformed into current-js (i.e. via babel):\r\n```js\r\n// method which wraps methods such that, if \"this\" is the private container,\r\n// swap it out with the instance of the public class\r\n// (this could probably be improved, but interpret as a POC)\r\nfunction setValue(privateThis, trueThis, name, value) {\r\n    if (value instanceof Function) {\r\n        return privateThis[name] = function(...args) {\r\n            if (this === privateThis) {\r\n                return func.apply(trueThis, args);\r\n            }\r\n\r\n            return func.apply(this, args);\r\n        }\r\n    }\r\n\r\n    return privateThis[name] = value;\r\n}\r\n\r\nconst Foo = (() => {\r\n    const privates = new WeakMap();\r\n\r\n    return class {\r\n        constructor() {\r\n            //if there had been a constructor with super, everything up until the super call would go here\r\n            //create the private container\r\n            const fooPrivates = {\r\n                y: 5,\r\n                arrow: () => this,\r\n            };\r\n            setValue(fooPrivates, this, \"privateMethod\", function() {\r\n                return this;\r\n            });\r\n            privates.set(this, fooPrivates);\r\n            //property initialization\r\n            this.x = 0;\r\n            //if there had been a constructor, the rest of the body would go here\r\n        }\r\n        method() {\r\n            // we lexically have access to privates, so if whatever \"this\" is is in there,\r\n            // accessing and even writing new values is trivial\r\n            if (!privates.has(this)) {\r\n                throw new IllegalAccessException();\r\n            }\r\n\r\n            setValue(privates.get(this), this, \"z\", 10);\r\n        }\r\n        addNewMethod() {\r\n            // adding a new method is also simple\r\n            const name = Math.random().toString();\r\n            if (!privates.has(this)) {\r\n                throw new IllegalAccessException();\r\n            }\r\n\r\n            setValue(privates.get(this), this, name, function() {return this.x;});\r\n        }\r\n    }\r\n})();\r\n```\r\n\r\nFor the implementation in JSNext, an exotic type of container, which natively has \"this\" refer to the instance of the class (as opposed to itself), is installed in a private slot in the class instance.\r\n\r\nThe rest of the behavior falls out naturally from parallels to the babel implementation. It is also fairly trivial (syntax up for debate) to allow means to destructure, iterate, or even dynamically access private properties.\r\n\r\nPersonally, I would have them provided thusly:\r\n\r\n```js\r\nfunction foo() {\r\n    // destructure\r\n    const {x, y} = this.#;\r\n    // iterate\r\n    for (let z of this.#) {}\r\n    //dynamic access\r\n    this.#[\"foo\"];\r\n}\r\n```\r\n\r\nAs far as I can tell, the sole difference between this and the proposal as-is in terms of behavior is the addition of dynamic read/write, which some users will want. There should be no reductions in the safety of this when compared to the proposal as-is, but we have the benefit of providing a class author the option to work with private fields dynamically, instead of restricting them to a static interface."},"comment":{"url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/comments/362935462","html_url":"https://github.com/tc39/proposal-class-fields/issues/79#issuecomment-362935462","issue_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/79","id":362935462,"user":{"login":"littledan","id":189835,"avatar_url":"https://avatars3.githubusercontent.com/u/189835?v=4","gravatar_id":"","url":"https://api.github.com/users/littledan","html_url":"https://github.com/littledan","followers_url":"https://api.github.com/users/littledan/followers","following_url":"https://api.github.com/users/littledan/following{/other_user}","gists_url":"https://api.github.com/users/littledan/gists{/gist_id}","starred_url":"https://api.github.com/users/littledan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/littledan/subscriptions","organizations_url":"https://api.github.com/users/littledan/orgs","repos_url":"https://api.github.com/users/littledan/repos","events_url":"https://api.github.com/users/littledan/events{/privacy}","received_events_url":"https://api.github.com/users/littledan/received_events","type":"User","site_admin":false},"created_at":"2018-02-04T20:09:41Z","updated_at":"2018-02-04T20:09:41Z","author_association":"OWNER","body":"For one, the original proposal added private fields to the instance based on the original prototype chain as the class was set up, rather than respecting prototype chain mutations as it does now.\r\n\r\nIf you're OK with this being explored as a follow-on proposal, I'll close this issue and the discussion can continue in another repository."}}
{"actor":{"display_login":"littledan"}}