Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

This proposal does not address the actually existing needs for private fields #22

Closed
zocky opened this issue Nov 25, 2017 · 5 comments
Closed

Comments

@zocky
Copy link

zocky commented Nov 25, 2017

I'm aware that much of this has been covered in the FAQ, but I still want to chime in:

The only good reason for this (IMO ugly, but that's a matter of taste) syntax seems to be the complications with what is accessible from "this" and the desire to give other objects in the same class access to the private properties.

But this idea that other objects of the same class should have access to the private fields of an object is what is very non-Javascript.

JavaScript objects don't really have classes, they have inheritable prototypes. All it takes for another object to become "of the same class" is to set its __proto__ property to some class's prototype. Which could be considered "dirty", but is also very common across libraries. All it takes to add a method to a "class" is to add a function member to the class's prototype. So protection by classes is ineffective and meaningless.

What Javascript programmers traditionally do is restrict access to data by scope, not by class. This is why there is a very common pattern that goes like this:

function myFactory () {
  var foo = "bar";
  var myNewObject = {
     doSomethingWithFoo() {
        // foo is accessible here, but nowhere outside the myFactory function
        doSomething(foo);
     }
  }
  return myNewObject;
}

This is what Javascript programmers call "private" variables. It is not the same thing as in Java or other programming languages. But it is very useful for our use cases , because it prevents programmatic access to these "private" variables and thus protects the information from other, potentially malicious scripts outside our control, in the same window (if we're talking about browsers).

The pattern is very tedious to write, and cannot be easily (or at all?) converted to using classes. A proposal that would solve this problem, and not the problem of making Javascript classes more like
classes in other languages, would be preferable.

What we really need is this:

class myClass() {
  private foo = "bar";
  doSomethingWithFoo() {
     // foo is accessible here, but not accessible to methods of this or any other object
     // defined outside the class definition 
     doSomething(foo);
   }
} 
@littledan
Copy link
Member

@zocky That does seem like a possible alternative, but the private fields repository attempts to explain the important use cases of class-private rather than instance-private (e.g., an equals method). I understand that it's not common practice today, but I'm wondering, what's an example of something that will break by allowing other instances of the same class to see the private fields or methods? How could you have malicious code in the same class body? Note that manipulating__proto__ doesn't give someone else access to private fields.

@rdking
Copy link

rdking commented Jan 10, 2018

@littledan I get where you've been trying to go with the class-private names approach, though as someone who has written new languages, still think you've not thought thing through thoroughly (pardon the alliteration). An equals method can be developed even using private fields as described by @zocky while still taking into account most of what you're trying to achieve with private fields, and all without destroying the fact that class is just syntactic sugar. What if this example:

class myClass {
  private foo = "bar";
  doSomethingWithFoo() {
     // foo is accessible here, but not accessible to methods of this or any other object
     // defined outside the class definition 
     doSomething(this[foo]);
  }
  equals(other) {
    let retval = false;
    if (other instance of myClass)
      retval = this[foo] === other[foo];
    return retval;
  }
} 

translated to this:

var myClass = (function defineMyClass() {
  var privateScope = new WeakMap(); //Your private slots go here
  var privateNames = { //Your class-private names
    foo: Symbol() //From your intentions
  }
  with(privateNames) { //Ensures your class private names are available without this
    function _myClass() {
      privateScope.put(this, {
        [foo]: "bar"
      });
    }
    myClass.prototype.doSomethingWithFoo() {
      let pvt = privateScope(this);
      // foo is accessible here, but not accessible to methods of this or any other object
      // defined outside the class definition 
      doSomething(pvt[foo]);
    }
    equals(other) {
      let retval = false;
      if (other instanceof myClass) {
        let pvt = privateScope(this);
        let other_pvt = privateScope(other);
        retval = pvt[foo] === other_pvt[foo];
      }
      return retval;
    }
  }
  return _myClass; 
})();

I've spent much time trying to think of how the sigil (#) approach would work if implemented using current JavaScript. Frankly, it would probably look like the above. What's more, is that unless I missed something critical, what you're trying to do with class-private names can be in a fairly straight-forward manner be little more than a hidden implementation detail while allowing the syntax to remain something more palatable to the existing Javascript developer base.

@rdking
Copy link

rdking commented Jan 10, 2018

@littledan Let's see how my suggestion compares with your FAQ.

  • Why aren't declarations private x?
    Now they would be. Many who've taken the time to chime in have honed in on how awkward #x is as a declaration. Your response has consistently been "you'll get used to it." Isn't the same true for private x as a declaration? I think Javascript developers are more likely to quickly adopt syntax that doesn't evoke a negative visceral response. The notion that private foo declares a class-private name fits the context of a class. The notion that private foo="bar" declares the class-private name foo and sets a default value of "bar" on every instance's private scope at that name is close enough to what developers expect to be easily adopted.

  • Why isn't access this.x?
    I get this, though I don't agree with it. Ignoring that, since private x essentially declares a new Symbol x within the scope of the class, instances would not be able to use this.x unless it was public. This is already a part of how Javascript works. No changes required. The syntax for access would be this[x]. The only thing the interpreter would need to know is whether x is a private name in the current scope. This check can be done with negligible effect on performance at the end of the normal scope-chain walk to find x.

  • Why does this proposal allow a class to have a private field #x and a public field x?
    The approach I've taken doesn't remove the possibility of having both a private and a public field with the same name. While the public field would be accessible via this.x or this["x"], the private field would only be accessible via this[x] with no quotes.

  • Why not do a runtime check on the type of the receiver to determine whether to access the private or public field named x?
    As stated before, the standard scope-chain walk, with a check a type-check at the end takes care of this with negligible effect on performance or complexity.

  • Why doesn't this['#x'] access the private field named #x, given that this.#x does?
    No longer an issue given that the semantics would follow current Javascript notation.

The example and its expansion above should be in keeping with all the other points of your FAQ,, not withstanding the above comments. Put another way, you'd get the features you want without burdening us with ugly, somewhat confusing syntax, without changing the existing fact that class is syntactic sugar, and with significantly less workload for the engine developers.

@littledan
Copy link
Member

We've discussed these proposals in other threads, and my conclusion is that we should not adopt them.

@masaeedu
Copy link

@littledan Is there a "primary" issue where discussion pertaining to other-instance-visibiilty was carried out/should be carried out?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants