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.

Dynamic Access of Private FIelds #104

Description

@robbiespeed

It's stated in the FAQ that this.#x !== this['#x'] this makes sense in the same way that this.x.y !== this['x.y'], however that means we currently have no way of dynamic access.

That answer also gives the argument that dynamic access "is contrary to the notion of 'private'". This is not true at all, of course private fields shouldn't be dynamically accessed outside of the class body, but inside there are many reasons it can be valuable.

Not Possible Without Dynamic Access

I want to be clear here, I'm not advocating that private fields should be settable or retrievable from outside the class body, unless the library maintainer explicitly adds such feature.

  • Observer pattern
    If I library were to have set(obj, key, value) and observe(obj, key, callback) methods there would be no way to internally observe or set private fields, this would necessitate anyone using such library to have all fields public.

  • Intentional Escape Hatch For Debug and Tests

Possible Solutions

I would like to open this up to discussion, I don't quite know myself the best way to make this possible, or all the concerns people have about introducing such a feature.

Just to get things started though one possibility might be a new Private Key type. A example might look something like:

class Cat {
  #hunger = 0;
  constructor () {
    if (ENV.DEBUG_PRIVATE) {
      this.__getPrivate = (privateKey) => this.#[privateKey];
      // Set uses the observable `set` method
      this.__setPrivate = (privateKey, value) => set(this, #[privateKey], value);
      
      // If not using a observer library
      // this.__setPrivate = (privateKey, value) => this.#[privateKey] = value;
    }
    observe(this, #['hunger'], () => {
      if (this.#hunger > 10) {
        this.growl();
      }
    })
    setInterval(() => {
      set(this, #['hunger'], this.#hunger + 1);
    }, 1000);
  }
  growl () {
    console.log('Meeooow');
  }
  eat (food) {
    set(this, #['hunger'], this.#hunger - food.value);
  }
}

const cat = new Cat();
// Private fields still directly innaccessible from outside class body
cat.#['hunger']; // throw error or return undefined
cat.#['hunger'] = 2; // throw error
set(cat, #['hunger'], 2); // throw error

cat.__getPrivate('hunger'); // return 0
cat.__setPrivate('hunger', 10); // return 10
// log > Meeooow
cat.eat({ name: 'fish', value: 3 });
cat.__getPrivate('hunger'); // return 7

Open to better ideas for the syntax

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