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.

Alternative approach: make everything private but prototype properties #89

Description

@drpicox

The idea is close to the approach of ES5 of "strict": it changed the behaviour of Javascript but just in functions or files.

For example, imagine that we can do something like:

strict class Point() {
  constructor(x = 0, y = 0) {
    this.x = +x;
    this.y = +y;
  }

  get x() { return this.x; }
  set x(value) { this.x = +value; }

  get y() { return this.y; }
  set y(value) { this.y = +value; }

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

  toString() { return `Point<${ this.x },${ this.y }>`; }
}

This example would be translated in exactly the same presented in the introduction. Note that functions, getters and setters are present in the prototype, so they are public, but values for x and y are in this, so they would be private.

Considering something like:

strict class Animal {
  constructor(name) {
    this.name = name;
  }
  getName() { return this.name; }
  setName(name) { this.name = name; }
}

Using this would be something like:

let savio = new Animal('savio');
savio.getName(); // 'savio'
savio.name; // undefined

Something remaining to decide is what to do if there is an assignation:

// option A: assigns it to a virtual middle object
savio.name = 'rico';
savio.name; // 'rico'
savio.getName(); // 'savio'
// option B: throw an exception
savio.name = 'rico'; // Uncaught ReferenceError: savio.name is not defined
savio.foobar = 'rico'; // Uncaught ReferenceError: savio.foobar is not defined

I bet for B, because is more in the line of the philosophy of this solution. If you want to add metadata, please use a WeakMap or similar approach.

Philosophy of the solution

The idea is that private variables are great for encapsulation. Closures are also great, but you cannot access to other instances privates. That is the reason why privates are great.

Usually it is considered bad practice to expose class internal representation. Make some properties private and some properties public drives to bad practices. Making all private but prototype properties effectively promote good practices.

You can expose some internal state through getters and setters (they are defined in the prototype). It ensures that you can effectively control manipulations to your class internal state.

It is not bad to force all properties to be private, you can always use legacy classes and legacy objects. So you can get rid of private limitations, if you need (for example because of performance), just using legacy classes.

It keeps Javascript very similar to the one that we know. Even better, we can port code to the new implementation as easily as adding the strict keyword in front of the class definition. Betting for the option B we should also be able to catch easily legacy parts accessing to the internal state.

Addendum

I am aware that strict is not a keyword, but I love it. It fits very well: we are limiting what we are able to do like it happened before in ES5.

But we have other keywords suitable, like public. It might be funny to see public class Point { ... }.

I also have designed it all taking into account most work done with the current specification, so it could be reused. Semantics should be very similar. It is close to replacing all this.that by this.#that , except for the ones that are in the prototype (which require a fallback for the prototype) and option B that would be ideal for this proposal (note that option A is the current).

Other considerations

You can also define private functions:

strict class Point {
  constructor(x = 0, y = 0) {
    this.x = +x;
    this.y = +y;
    this.cabMagnitude = () => {
       return Math.abs(this.x) + Math.abs(this.y);
    };
    
}

or:

strict class Point {
    
  cabMagnitude = () => {
    return Math.abs(this.x) + Math.abs(this.y);
  };
}

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