0

I need a way to define a getter inside a class constructor, I want to store and get a variable defined outside the class but unique to the instance. Here's what I have in mind:

// this works fine
const uniqueID = 5

class Person {
 get id() {
  return uniqueID
 }
 constructor(element){
  // do the dew with element
  console.log( this.id + ' ' + element )
 }
}

This is fine to have a property that's not in this instance object, but it's not specific to the instance, here's the thing:

// this won't work
let uniqueID = 1

function getUnique(element){
 return element.uniqueID || uniqueID++
}

class Person {
 constructor(element){

  const elementID = getUnique(element)

  element.uniqueID = elementID

  Object.defineProperty(Person, 'id', { get: () => elementID  } )
  // any of the below also fail
  // Object.defineProperty(Person.prototype, 'id', { get: () => elementID  } )
  // Object.defineProperty(Person.constructor, 'id', { get: () => elementID  } )
  // Object.defineProperty(this, 'id', { get: () => elementID  } )

  // do the dew with element
  console.log( this.id + ' ' + element )
 }
}

Any of the above throw Uncaught TypeError: Cannot redefine property: id, but I can set whatever property name, like myUniqueID, it's the same error.

The NEED is that I have to set a UNIQUE ID specific to the element without storing the ID in this instance, which is important to not expose the ID unless allowed to or internally called.

Please feel free to ask further clarification and thanks in advance for any reply.

thednp
  • 4,401
  • 4
  • 33
  • 45
  • 3
    You are getting the error because you are defining the property on the `Person` function. The second time you call the constructor it will try to overwrite the property and this error is thrown. How do you plan to use the getter? If the getter is defined on the instance itself, then it's no different from storing the ID as a normal property on the instance... if you want to be able to access the ID from class methods only, then you probably have to use a `WeakMap` that only the class can access and store `instance -> id` in it. – Felix Kling Jan 29 '21 at 08:53
  • 1
    You can try `Object.defineProperty(Person, 'id', { get: () => elementID, configurable: true } )`, as `configurable` default to `false`. The error I get in Firefox is _“Uncaught TypeError: can't redefine non-configurable property "id"”_. – Sebastian Simon Jan 29 '21 at 08:55
  • 2
    [Private properties in JavaScript ES6 classes](https://stackoverflow.com/a/52237988/218196) shows a couple of solutions, including the new private fields proposal and the `WeakMap` approach I mentioned. – Felix Kling Jan 29 '21 at 08:56
  • @SebastianSimon: In that case `this.id` would still not work and `Person.id` would just return the ID of the last created instance which doesn't seem useful. – Felix Kling Jan 29 '21 at 08:57
  • @FelixKling that solution is not here yet, it's only a draft and the `buble` compiler doesn't support private class properties yet. – thednp Jan 29 '21 at 09:02
  • So, use `WeakMap` then? – Felix Kling Jan 29 '21 at 09:05
  • I'm not actually sure how WeakMap solution would look like, it's a new thing for me tbh. – thednp Jan 29 '21 at 09:09
  • 1
    From the question I linked to: https://stackoverflow.com/a/33533611/218196 "Scoped WeakMap" – Felix Kling Jan 29 '21 at 09:12
  • @FelixKling that solution looks similar to what extended classes look like. I'm now wondering if there's a way to achieve my goal with a base class to store the private variables accessible only inside the extended class, all without `WeakMap`. That would be awesome. – thednp Jan 29 '21 at 10:07
  • "*a variable defined outside the class but unique to the instance.*" - I don't see how that would work. And after all you *want* to access `this.id`, right? Please clarify what you mean by "*without storing the ID in this instance, which is important to not expose the ID unless allowed to or internally called*" and what you need this for. – Bergi Jan 29 '21 at 10:16
  • I don't want `this` to expose that ID, only the instance methods. Here's what I expect to see from `this`: `{ __proto__: { id, method1, method2 } }`, where `id` is our getter. Somehow similar to how native classes work, they don't expose anything, and calling them directly never gets you anything. – thednp Jan 29 '21 at 10:25
  • * calling *getters* directly never gets you anything – thednp Jan 29 '21 at 10:36
  • @thednp: If a getter is anywhere in the prototype chain, any location that has a reference to the instance can access it and get the returned value. I fail to see how a getter would be any different than a normal property in that respect. A getter doesn't allow you to differentiate between "internal access" (from other methods) and "external access" (if that's what you want, I'm not sure). – Felix Kling Jan 29 '21 at 13:04
  • Thanks guys, you are awesome! – thednp Jan 29 '21 at 14:26

0 Answers0