1

As far as I know the suggestions of private fields/functions of classes came with ES2019 with the # syntax.

I have the following class for example:

class MyClass {
    #firstPrivateMethod(parameter) {
        // method functionality.
    }

    #secondPrivateMethod(parameter) {
        var someValueFromTheOtherMethod = this.#firstPrivateMethod(parameter);
    }

    constructor() {
        var firstPrivateMethod = this.#firstPrivateMethod;
        var secondPrivateMethod = this.#secondPrivateMethod;

        window.onresize = function() {
            this.#firstPrivateMethod = firstPrivateMethod; // <-- error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.
            secondPrivateMethod(parameter);
        }
    }
}

I'd like to temporarily pass the private methods and fields of my class to the event handler's this object then later i'd remove these event handlers or set them to null after they finished a certain class-based task.

The reason why i need this is that my class is attaching elements to the DOM and the style of these elements are calculated from it's dependencies. I think it would be really hard or impossible to make it responsive with media queries. I have some breakpoints for different devices. After every breakpoint i'd drop the attached elements then i'd change the values of the dependencies and re-attach them with the new style.

At first i had an idea. I tried to modify my code like this:

class MyClass {
    #firstPrivateMethod(parameter) {
        // method funcionality.
    }

    #secondPrivateMethod(firstPrivateMethod, parameter) {
        var someValueFromTheOtherMethod = firstPrivateMethod;
    }

    constructor() {
        var firstPrivateMethod = this.#firstPrivateMethod;
        var secondPrivateMethod = this.#secondPrivateMethod;

        window.onresize = function() {
            secondPrivateMethod(firstPrivateMethod, parameter);
        }
    }
}

But it got me nowhere because my code got so messy that it was really hard to read.

Do you have any idea how to solve this problem?

Emma
  • 27,428
  • 11
  • 44
  • 69
  • The second approach already looks much better than the first one ... please provide more information about the contexts each of this methods/functions need to be run within ... talking about the event handler and may be some more insights about `firstPrivateMethod` and `secondPrivateMethod` – Peter Seliger Jul 04 '20 at 17:02
  • I'm trying to create a js library. It will be a wheel for navigation purposes. I have an init() function that creates the "skeleton" of the wheel, then based on the pre-set or given options it creates nodes on the wheel. There are 2 type of nodes: main-menu or another wheel for sub-menus. It's working fine but i'd like to make it responsive with the help of onresize event. At the moment i'm trying to re-run the init() function inside the event handler but my init() function is depends heavily on my private methods. – Foltán Dániel Jul 04 '20 at 17:11
  • 1
    Is this question about private methods at all? Are you saying the code would work if the methods were public? I really cannot figure what you're trying to do here. – Bergi Jul 04 '20 at 17:20
  • ... *"but my init() function is depends heavily on my private methods."* ... this is where one needs more insights, how are your *private* methods build ?.. in which context do they run, which scope do they cover? it could be that your basic structures are in need of a different approach. – Peter Seliger Jul 04 '20 at 17:20
  • "Are you saying the code would work if the methods were public?" - Yes. That way it would work but then i would lost the whole point of they being private. – Foltán Dániel Jul 04 '20 at 17:34
  • "how are your private methods build ?.. in which context do they run, which scope do they cover?" - What do you mean by how are they build? They should only run when the init() function is called and they should not be able to called outside of the class. I delacre them like #method = function () { ... } and call them like this.#method. Their scope should be only the class itself. – Foltán Dániel Jul 04 '20 at 17:37
  • Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – FZs Jul 04 '20 at 21:44

1 Answers1

1

The first provided code tries both anticipating the OP's probably true source of pain and presenting a solution which proves that, for the OP's given example, there might not even be a real need for an approach that is based on private instance fields/methods.

The guessing is based on the error message the OP did provide ...

error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.

... with the affected code (partially) looking like that ...

constructor() {
    var firstPrivateMethod = this.#firstPrivateMethod;
    // ...

    window.onresize = function() {
        this.#firstPrivateMethod = firstPrivateMethod; // <-- error: Cannot write private member #firstPrivateMethod to an object whose class did not declare it.
        // ...
    }
}

The error most probably got raised due to the unfitting this context of the handler function. With the given example the resize handler's this context at call time is the window object itself. But private field values can only be accessed via direct or prototypal methods of the very instance such a field was protective bound to.

Thus the key approach should be providing the correct this context to the resize handler which can easily be done via Function.prototype.bind.

But of course the chances are high that the next example entirely misses the point ... nevertheless, here we go ...

// suggest a module scope for the `MyType` class

function doSomethingElseWithType(type, ...args) {
  console.log('doSomethingElseWithType :: type, args : ', type, args);
  // ... even more computation.
}

function doSomethingWithType(type, ...args) {
  console.log('doSomethingWithType :: type, args : ', type, args);

  // ... more computation.

  doSomethingElseWithType(type, ...args);
}


function handleWindowResizeWithBoundType(evt) {
  const type = this;

  doSomethingWithType(type, evt.type, ...Object.keys(evt));
}


/*default export */class MyType {
  constructor(label) {
    this.label = label;

    window.onresize = handleWindowResizeWithBoundType.bind(this);
  }
}


const type = new MyType('test');

// NOTE: please squeeze the window vertically but be careful
//       about the frequency of this action.
.as-console-wrapper { min-height: 100%!important; top: 0; }

In case the first approach somehow was not entirely missing the point, one could come up with another approach that this time has to make use of a private instance field in order to provide the opportunity of removing a privately held/kept resize handler ...

// suggest a module scope for the `MyType` class

function doSomethingElseWithType(type, ...args) {
  console.log('doSomethingElseWithType :: type, args : ', type, args);
  // ... even more computation.
}

function doSomethingWithType(type, ...args) {
  console.log('doSomethingWithType :: type, args : ', type, args);

  // ... more computation.

  doSomethingElseWithType(type, ...args);
}


function handleWindowResizeWithBoundType(evt) {
  const type = this;

  doSomethingWithType(type, evt.type, ...Object.keys(evt));
}


/*default export */class MyType {

  #resizeHandler;

  constructor(label) {
    this.label = label;

    this.#resizeHandler = handleWindowResizeWithBoundType.bind(this);

    window.addEventListener('resize', this.#resizeHandler);
  }
  unlisten() {
    window.removeEventListener('resize', this.#resizeHandler);
  }
}


const type = new MyType('another test');

setTimeout(type.unlisten.bind(type), 7000);

// NOTE: please squeeze the window vertically.
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37