0

I have a JavaScript ES6 class in a library that uses some private base values to compute another value.

Abstracted and simplified the situation is something like this:

class Example {
  #apples;
  #oranges;
  #cherries;

  constructor() {
    this.#apples = 22;
    this.#oranges = 38;
    this.#cherries = 12;
  }

  get numOfFruits() {
    return this.#apples + this.#oranges + this.#cherries;
  }
}

Now I want to add a function to Example to compute a new value e.g. something like:

Example.prototype.getNumOfApplesAndOranges = function() {
   return this.#apples + this.#oranges;
};

I get that you are not supposed to access private variables outside their declaring class but I thought that by adding a method to the class the access happens within the class.

However this does not work and errors with Uncaught SyntaxError: reference to undeclared private field or method #apples. Is there any way to make this work?

I already tried with eval, Proxy and Object.setPrototypeOf but none of these helped.

3
  • Access to private variables is lexical -- the code has to be textually inside the class block.
    – Barmar
    Commented Jun 25, 2024 at 19:34
  • 1
    The language is specifically designed to prevent what you're trying to do. You cannot add methods after the fact that access private variables. If you could, then they wouldn't be private at all. So, you have to either define those methods as part of the class definition or create accessors for the private variables (making them not so private any more) from which outside code can now access those variables.
    – jfriend00
    Commented Jun 25, 2024 at 19:35
  • "Is there any way to make this work?" - no, there is not. Private means private, and any access to private fields must be within the class declaration. "Abstracted and simplified" - if you could share the actual code, we might be able to provide a workaround.
    – Bergi
    Commented Jun 25, 2024 at 20:00

1 Answer 1

0

As others commented, because of the decision to have '#' be hard private, it seems to be impossible to access the private fields from outside the class block declaring it.

But building on this solution I found a very hacky and terrible way to solve this but it worked for my use case, so I will share it here.

I added a script before the one that contains the class and essentially did this:

<script>
const observer = new MutationObserver( (mutations) =>{
    mutations.forEach(({addedNodes}) => {
        [...addedNodes]
            .forEach(node => {
                if(node.tagName?.toLowerCase() !== "script"){
                    return;
                }
            
                node.innerHTML = node.innerHTML.replace(/(class Example \{)/i, "$1\nget getNumOfApplesAndOranges(){\nreturn this.#apples + this.#oranges;}\n");
                observer.disconnect();
            });
    });
});

observer.observe(document.head, {childList: true});

document.addEventListener("DOMContentLoaded", (ev) => {
    console.log((new Example()).getNumOfApplesAndOranges);
});
</script>
<script>
class Example {
  #apples;
  #oranges;
  #cherries;

  constructor() {
    this.#apples = 22;
    this.#oranges = 38;
    this.#cherries = 12;
  }

  get numOfFruits() {
    return this.#apples + this.#oranges + this.#cherries;
  }
}
</script>

But in my case I had to remove the second script from the DOM, fetch the content of second script (as it was loaded from another server), then modify it to allow access to the private fields and write it to a new script tag and then append it to the DOM.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.