Skip to content

Instantly share code, notes, and snippets.

@rauschma
Last active April 15, 2021 13:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rauschma/a4729faa65b30a6fda46a5799016458a to your computer and use it in GitHub Desktop.
Save rauschma/a4729faa65b30a6fda46a5799016458a to your computer and use it in GitHub Desktop.

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;
}
@aminpaks
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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

@MikeMcl
Copy link

MikeMcl commented Apr 14, 2019

@supertonsky
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link
Author

@abiphone
Copy link

abiphone 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
Copy link

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
Copy link

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
Copy link

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.

@basickarl
Copy link

basickarl commented Apr 15, 2021

But the whole point of the comment/code at the top was to tell the compiler to act in a specific way? Like bash's #!/usr/bin/env bash, am I missing something?

@ljharb
Copy link

ljharb commented Apr 15, 2021

That would be a new mode (like strict mode), and the committee is pretty firmly against ever adding another one. In addition, the refactoring hazard would be large, as code that works fine in one file would suddenly work differently in another.

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