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

[Private] yet another alternative to #-based syntax #149

@Igmat

Description

@Igmat

Disclaimer

My intent here is to show that there are good alternatives to existing private part of this proposal, that were not properly assessed. Probably it happened, because committee has a very limited time and amount of members involved, while such long time for discussion around encapsulation (years or even decades) has lead to loosing clear sight on the problem and possible solutions.

Proposal

It's based on the my proposal in #134 and Symbol.private - I won't repeat them fully here, but I hope that you may visit these links to get more background.
This comment (#147 (comment)) to my initial proposal inspired me to make another one, which dismisses problem with this having more complex semantics than it has now.

Goals:

  1. Prevent confusion with private x / this.x pair
  2. Symmetry between this.x and obj.x
  3. Symmetry between declaration and access syntaxes

Solution

Use Symbol.private with existing lookup mechanism, existing mental model for accessing properties and small syntax sugar to make use of such symbols more ergonomic, by declaring symbol variable in lexical scope of a class.

Syntax 1 (less verbose):

class A {
    // declaring of new private symbols in lexical scope of a class
    symbol somePrivatePropertyName;
    symbol somePrivateMethodName;

    [somePrivatePropertyName] = 'this is private value';
    somePrivatePropertyName= 'this is public value with a `somePrivatePropertyName` property name';

    [somePrivateMethodName]() {
        // it is a private method
    }
    somePrivateMethodName() {
        // it is a public method with `somePrivateMethodName` name
    }
    methodA(obj) {
        this[somePrivateMethodName](); // call private method
        this.somePrivateMethodName(); // call public method
        this[somePrivatePropertyName]; // access private property
        this.somePrivatePropertyName; // access public property

        obj[somePrivateMethodName](); // call private method
        obj.somePrivateMethodName(); // call public method
        obj[somePrivatePropertyName]; // access private property
        obj.somePrivatePropertyName; // access public property
    }
}

const a = new A();
// throws `ReferenceError` since `somePrivatePropertyName` doesn't exist outside of class A
a[somePrivatePropertyName];
// access public property `somePrivatePropertyName` of `a`, as expected
a.somePrivatePropertyName;

Syntax 2 (more generic):

class A {
    // declaring of new private symbols in lexical scope of a class
    const somePrivatePropertyName = Symbol.private();
    const somePrivateMethodName = Symbol.private();

    // everything else is the same as in previous example

    // additional possibility of such syntax
    let numberOfInstanses = 0;

    constructor() {
        numberOfInstanses++;
    }
}

This option is a little bit more verbose, and probably such syntax isn't obvious enough to use for declaring private instance data, but as I shown above it could be used for something else, which is probably make some sense.

Advantages:

  1. Doesn't have any problems listed in FAQ
  2. No new semantics === easy to learn
  3. Full symmetry === prevents accidental missuses
  4. Possibility to extend in future, if needed
  5. Doesn't use #-sigil, so no risk for breaking community
  6. Compatible with TypeScript's private

Disadvantages:

  1. Not yet implemented
  2. Slogan # is the new _ couldn't be used for education

Conclusion

Since I don't pretend to replace existing proposal with this one right now, because, obviously, my proposal also needs additional assessment, preparing document that reflects changes to ES spec, creating tests, implementing babel-plugin for transpiling, adding support to JS-engines and most importantly wider discussion with community (BTW, existing also needs it), I only hope that this clearly shows the fact that there are still good alternatives, which weren't taken into account.

What is your opinion on that @littledan, @zenparsing, @jridgewell, @ljharb?

P.S.

This syntax also adds a bonus usage scenario:

class A {
    symbol marked;

    markForeignObject(foreignObject) {
        if (foreignObject[marked]) {
            // do something with knowledge that `foreignObject` was already passed to this method
        } else {
            // do something with knowledge that `foreignObject` enters here first time
            foreignObject[marked] = true;
        }
    }
}

And it's only one possible scenario, there could be dozens of them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions