最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Cannot read private member from an object whose class did not declare it...? - Stack Overflow

programmeradmin0浏览0评论

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));

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)

I can't make any sense of this error because:

  1. addNumber works fine, so its not a syntax error or a typo at least.
  2. doAddNumber works fine, so its not a problem calling functions from other functions.
  3. operate just calls addNumber which, from (1), works fine.
  4. this is an object whose class declares #privateMember... I mean, this is an Example and I can see that it's declared in Example. 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.

Share Improve this question edited Dec 11, 2022 at 13:02 Jason C asked Dec 11, 2022 at 2:00 Jason CJason C 40.5k15 gold badges135 silver badges198 bronze badges 4
  • 2 "this is an object whose class declares #privateMember" - no it's not. Check in your debugger what this really is. It may be the expected instance of Example in ex.addNumber(…), but it's not in #operations.add(…). – Bergi Commented Dec 11, 2022 at 4:25
  • 2 Btw it seems like your #operations field should be a static member. – Bergi Commented Dec 11, 2022 at 4:26
  • @Bergi Thanks for the hint! As for it being static; I guess I could do that; if I move the required (see answers) call/bind into the method that invokes it, then I don't need to reference this in the #operations declaration. I wanted it to be static when I wrote it, since it's philosophically more appropriate and also I wouldn't need to create a temporary new Example() to get the list of keys from #operations, but couldn't figure out a way to make that happen. It's hard to get used to JS ing from C++, heh. – Jason C Commented Dec 11, 2022 at 13:06
  • @Bergi Ah, but in my real code, the equivalent of addNumber is actually private (starts with #); that didn't make it into this example snippet because it didn't affect the problem I was asking about here. Turns out (from docs) that "Unlike public methods, private methods are not accessible on Class.prototype". So I can't do Example.prototype.#privateMethod, unfortunately. Ah well. But I agree, it should be static. – Jason C Commented Dec 11, 2022 at 13:17
Add a ment  | 

2 Answers 2

Reset to default 4

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

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, ...).

发布评论

评论列表(0)

  1. 暂无评论