In this program:
class Example {
#privateMember = 123;
// these are fine
addNumber (n) { return this.#privateMember n; }
doAddNumber (n) { return this.addNumber(n); }
// "cannot read private member #privateMember from an
// object whose class did not declare it"
#operations = { add: this.addNumber };
operate (n) { return this.#operations.add(n); }
}
const ex = new Example();
console.log(ex.addNumber(77));
console.log(ex.doAddNumber(77));
console.log(ex.operate(77));
Calling addNumber
works fine, so does doAddNumber
, but calling operate
yields the error:
TypeError: Cannot read private member #privateMember from an object whose class did not declare it
at Object.addNumber [as add] (<anonymous>:11:17)
at Example.operate (<anonymous>:20:29)
at <anonymous>:27:16
at dn (<anonymous>:16:5449)
It's runnable at https://playcode.io/1033251.
I can't make any sense of this error because:
addNumber
works fine, so its not a syntax error or a typo at least.doAddNumber
works fine, so its not a problem calling functions from other functions.operate
just callsaddNumber
which, from (1), works fine.this
is an object whose class declares#privateMember
... I mean,this
is anExample
and I can see that it's declared inExample
. It's right there... I typed it myself...
I found TypeError: Cannot read private member from an object whose class did not declare it but I can't understand how it applies, if it applies.
I can't figure out what's going on here. Why doesn't operate
work even though addNumber
and doAddNumber
do?
In my real code (this is just a minimal example), I am trying to use a dictionary like #operations
to hold implementations of a number of various algorithms for performing a task, indexed by a string ID, where the string algorithm ID is specified to the constructor. This is also convenient because I can get the keys from this dictionary to provide a list of valid algorithm IDs without having to duplicate that list anywhere. Now, I can just switch it to an if
statement and make sure I keep the queryable list up to date as well, but I can't understand why this doesn't work.
CodePudding user response:
It's occuring because private fields can only be accessed from within the class that declares them, and object literals are not considered part of the class, so, basically if you create a constructor method in your class may solve your problem.
constructor () {
this.#operations = { add: this.addNumber };
}
I just summarize the answer but try to read to understand with more details why this occur: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
CodePudding user response:
When you set #operations.addNumber
and then call it, this === #operations
, not ex
, which fails because ex.#operarions.#privateMember
does not exist.
If I change your Example
class with
#operations = { add: this.addNumber.bind(this)};
then ex.#operations.add
runs with this === ex
and your code returns
> 200
> 200
> 200
CodePudding user response:
Here's a working example of what I believe you're after...
class Example {
#privateMember = 123;
// these are fine
addNumber (n) { return this.#privateMember n; }
doAddNumber (n) { return this.addNumber(n); }
// "cannot read private member #privateMember from an
// object whose class did not declare it"
#operations = { add: this.addNumber };
operate (n) { return this.#operations.add.call(this, n); }
}
const ex = new Example();
console.log(ex.addNumber(77));
console.log(ex.doAddNumber(77));
console.log(ex.operate(77));
In short, in your code the #operations method is simply creating an object with a reference to the Example class method of addNumber, with no association to any object. So, when attempting to make use of this reference, you need to pass an object...
Ie, you're essentially defining...
#operations = { add: Example.prototype.addNumber }
In the proposed solution, the operate
method invokes the reference to the addNumber
method in the #operations object, but of course has to pass an object to addNumber
, and does so by performing a call( this, ...)
.