Skip to content

Instantly share code, notes, and snippets.

@rauschma

rauschma/scoped-private.md

Last active Apr 13, 2021
Embed
What would you like to do?

Sketch: scoped private fields

So far, private fields are introduced as follows:

class MyClass {
  #privateField = 123;
}

I’d like to propose a small extension: scoped private fields.

Functional style (the proposal)

The following style is becoming popular in the JavaScript world. It benefits from scoped privacy:

private #data;

function createStringBuilder() {
  return {
    #data: '',
  };
}

function add(sb, str) {
  sb.#data += str;
}

function toString(sb) {
  return sb.#data;
}

New in this code:

  • The keyword private in the first line.
  • The ability to use private fields in object literals.

OOP style

As a slight downside, you now always need to use the keyword private:

class StringBuilder {
  private #data = ''; // keyword is required
  add(str) {
    this.#data += str;
  }
  toString() {
    return this.#data;
  }
}

On the upside, this gives you the freedom to widen the scope of privacy:

private #data;

class StringBuilder {
  #data = ''; // no keyword!
  add(str) {
    this.#data += str;
  }
}
function toString(stringBuilder) {
  return stringBuilder.#data;
}

Alternative syntax

Alternative syntax has been proposed:

  • Keyword private not needed for “normal” private fields.
  • Two keywords if you want to widen the scope of privacy: private and outer.

Example:

private #data;

class StringBuilder {
  outer #data = ''; // keyword is now required
  add(str) {
    this.#data += str;
  }
}
function toString(stringBuilder) {
  return stringBuilder.#data;
}

FP example:

private #data;

function createStringBuilder() {
  return {
    outer #data: '', // keyword is now required
  };
}

function add(sb, str) {
  sb.#data += str;
}

function toString(sb) {
  return sb.#data;
}
@littledan

This comment has been minimized.

Copy link

@littledan littledan commented Sep 12, 2018

Good writeup, Axel.

To be clear, this gist is proposing a change in the Stage 3 proposal https://github.com/tc39/proposal-class-fields , to require the private keyword in private field and method declarations where it was previously implicit, implied by the #. Although it should be easier on a first read to have private be explicit, I was thinking to leave that keyword out to avoid repetition (once you learn what # means, it may be annoying to keep repeating private). The private declaration proposal might make this extra verbosity "pay for itself".

For a contrast, I've suggested that we use a different syntax for this purpose in https://github.com/tc39/proposal-static-class-features/blob/master/FOLLOWONS.md#private-names-declared-outside-of-classes , which would not require adding the private keyword in the common case of a class declaration.

@aminpaks

This comment has been minimized.

Copy link

@aminpaks aminpaks commented Sep 12, 2018

Should we consider be explicit at this stage and keep the private keyword to preserve it for the future? It’s possible to remove the keyword at anytime but keeping the backwards compatible the other way around is more complicated.

@miguel-lattuada

This comment has been minimized.

Copy link

@miguel-lattuada miguel-lattuada commented Oct 31, 2018

Little late. As littledan mentioned it, it's annoying if you have to define several private class members. But it can be kept, and add in a near future, something like grouped private members (C++)

class StringBuilder {
    private #data = '',
            #internalCounter = 0;
}

Same for outer

private #data,
        #internalCounter;

class StringBuilder {
    outer #data = '',
          #internalCounter = 0;
}

Actually is not an uncommon syntax, as we today, can use something like:

const a = 12,
b = 13,
c = 14;
@simon-robertson

This comment has been minimized.

Copy link

@simon-robertson simon-robertson commented Nov 4, 2018

The private keyword feels more natural, especially when coming from other languages such as TypeScript, Java, C#, etc.

I don't see the point of keeping the # character if the private keyword is used though; when both are used one of them is redundant.

class StringBuilder {
    private data = '',
            internalCounter = 0;
}

The private keyword could essentially be syntax sugar for the # character.

@sebastianwolfea

This comment has been minimized.

Copy link

@sebastianwolfea sebastianwolfea commented Nov 8, 2018

I'd like to second @simon-robertson's comment. private is already a reserved word in the language, and is used for declaring private props/methods in C#, Java, Ruby and more. So why choose to use a symbol (which does not explain itself and whose purpose cannot be inferred by reading) instead of the same reserved keyword used for the same purpose in other languages?

Is it too late to just throw the inscrutable '#' character idea in the bin (or deprecate its use) and just use the private keyword by itself?

@icui

This comment has been minimized.

Copy link

@icui icui commented Nov 17, 2018

I'd like to second @simon-robertson's comment. private is already a reserved word in the language, and is used for declaring private props/methods in C#, Java, Ruby and more. So why choose to use a symbol (which does not explain itself and whose purpose cannot be inferred by reading) instead of the same reserved keyword used for the same purpose in other languages?

Is it too late to just throw the inscrutable '#' character idea in the bin (or deprecate its use) and just use the private keyword by itself?

I personally agree with the explanation in the FAQ, eliminating '#' could cause confusion because you won't get any error if you try to access attributes that are intended to be private.

@ctsstc

This comment has been minimized.

Copy link

@ctsstc ctsstc commented Mar 19, 2019

I'm dying to have a private modifier for class properties and methods, but I really don't care for JS going with this prefix of # I would much rather it be a keyword like private.

I have never seen a language go with a prefix of # before. I do have a C#/Java/TypeScript background so I'm surely biased with the choice of private - it just reads easier, it's not implicit, and many other languages already use it. Coming to Javascript to explain "Oh by the way if you want a private method/property you'll need to prefix it with #" I also understand why we cannot just start going with a prefix of: _ - because it currently works one way, so we cannot break that current functionality.

I do wonder how this will affect TypeScript which is a superset of JS. I'm sure they'll just have tslint autofix # --> private, but then they should behave the same so that nothing breaks.

At this point I feel like I'm choosing TypeScript not for its typing but for its well featured classes. All I want for Christmas is to have these standard class features that many other languages have had for a long time; I just want TypeScripts classes to become part of the standard JS classes. I know the class keyword is still fairly new in JS, but it's like it was half implemented while people write code that breaks encapsulation and doesn't allow the language to provide these features that other languages commonly have.

@jkomyno

This comment has been minimized.

Copy link

@jkomyno jkomyno commented Mar 22, 2019

@ctsstc I have the exact same opinion, I'd rather keep TypeScript's private keyword instead of using the esoteric and counterintuitive # symbol. By the way, # is also the symbol used in UML to identify protected methods/fields, which can be a source of ambiguity.

@giraffesyo

This comment has been minimized.

Copy link

@giraffesyo giraffesyo commented Mar 28, 2019

Is there an explanation for why the proposal is opting to include the # at all instead of just having the explicit private keyword?

@MikeMcl

This comment has been minimized.

Copy link

@MikeMcl MikeMcl commented Apr 14, 2019

@supertonsky

This comment has been minimized.

Copy link

@supertonsky supertonsky commented May 20, 2019

@giraffesyo

FAQ

JavaScripts new private class fields

Not sure if I get it but does it really say that the reason hash sign was chosen over private keyword is that there's no way to tell if the parameter passed is of the same class because JavaScript has no static typing?

equals(otherPoint) {
  return this.x === otherPoint.x && this.y === otherPoint.y;
}

Now we have a problem: How do we know what otherPoint is?

JavaScript doesn't have a static type system, so otherPoint could be anything.

  1. Why do we need to make an assumption about the argument "otherPoint"?
  2. Why do we need hash sign to make an assumption? Why not just assume it belongs to the same class without hash sign if we're going to make assumption anyways?
  3. TypeScript was able to do it then why JavaScript can't?
  4. If it's because JavaScript doesn't provide static type system as the reason for choosing hash sign, what if in the future JavaScript includes static typing? Isn't that somehow already making a decision of closing the doors for introducing static typing for JavaScript?
@krotovic

This comment has been minimized.

Copy link

@krotovic krotovic commented Aug 2, 2019

I think this proposal is going against the reasons of introducing the # symbol as private class accessor. Consider the following example:

private #foo;
var bar = {
    #foo: 'foo',
};

class Baz {
    #foo = 'blah';
}

function abc(val) {
    return val.#foo;
}

abc(bar) // == 'foo'
abc(new Baz()) // ?? Cannot access private field but valid

Now on the other problem.
I personally disagree with # symbol and I think that implementing either new symbol or new keyword is same for all platforms (interpreters).
As @supertonsky pointed correctly about making those assumptions, I would like to add that those will be same in either way.
I (personally) would implement that as not being able to see those fields from anywhere else than from the same scope it is defined in. With this you can avoid the conflict when some inherited class exposes its field with the same name as the private one in the parent (according to spec it cannot access the private one anyway) and you may be able to extend this behavior to any scope (not just class definition). The only problem is cross-instance access. But from my point of view it only makes sense to use it in places such as Object.equals(...) (as illustrated in FAQ) and even when you pass something that has such field and will result into something false-positive only you as a developer are responsible for that mistake not interpreter.

I know that this feature is in stage 3 and it's highly unthinkable that would somehow change. But if you want to make this proposal I'm giving you some thoughts and other possible approach.

@Ignas2526

This comment has been minimized.

Copy link

@Ignas2526 Ignas2526 commented Feb 7, 2020

I disagree with using the # symbol. It is counter-intuitive. Static fields and methods are declared using static keyword. Using private keyword is more intuitive and fits nicely with existing static keyword. Using private keyword is also easier to remember and to read.
It is equally weird that you have to specify that keyword when you access the variable. That also goes against existing conventions.

@devsnowy

This comment has been minimized.

Copy link

@devsnowy devsnowy commented May 19, 2020

I would like to know as well as many others why we need a new symbol # when we all know that private only means one thing.
At least add in the specification clearly why private naming was not used in favor of a new character. Makes everyone's life harder to approve such concept for the sake of it.

@jsilvao

This comment has been minimized.

Copy link

@jsilvao jsilvao commented Sep 20, 2020

# is blatantly awful, non-intuitive and will only fuel the JavaScript is a flawed language adage (which I have to agree to if this # symbol is introduced). private should be the way to declare private fields. The fact we're having a year-long discussion about this thing is what sometimes makes me question the reliability of the process (and people) upgrading JavaScript. Please, use private.

@rauschma

This comment has been minimized.

Copy link
Owner Author

@rauschma rauschma commented Sep 20, 2020

@ashleybooniphone

This comment has been minimized.

Copy link

@ashleybooniphone ashleybooniphone commented Dec 31, 2020

Using # is just awful, it does not follow standard JavaScript declarative syntax. It feels more like a PHP-style declaration (using symbols for declaration, $ for example) than a standard JavaScript-style declaration (using keywords for declaration, let for example).

@yashsway

This comment has been minimized.

Copy link

@yashsway yashsway commented Feb 12, 2021

@rauschma's comment and the FAQ on the proposal make it clear as day to be honest - it makes sense for the language. Yes, it looks ugly at first and not like standard JavaScript style declaration (which uses keywords for everything else), but I think we'll get used to it. On the other hand, I was wondering if during compile time, private field can be mapped over to #field ? I guess that means during access you'll have to remember to use the #, but during declaration it would look different. Not sure if that would be better or not actually, but it seems like a good middle ground?

~ thankful for everything OSS contributors do, and the TC39 committee as well!

@basickarl

This comment has been minimized.

Copy link

@basickarl basickarl commented Apr 13, 2021

wouldn't _ be viable if at the top of the file something tells the compiler that it is reserved so that it doesn't break current functionality, something that is like "use strict"; but for _? # is a horrible solution.

@ljharb

This comment has been minimized.

Copy link

@ljharb ljharb commented Apr 13, 2021

No existing identifier is viable, because var _ = something at the bottom of the file hoists it ti the top, and there could be silent conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment