Javascript Class returns private variable and not getter

Viewed 103

I would expect an instance of the following class to return both the "private" variable AND it's getter, but the getter variable is not returned at all. Why?

class Foo {
  _myPrivateVar = null;

  get myPublicVar() {
    return this._myPrivateVar;
  }

  set myPublicVar(v) {
    if (v > 4) {
      this._myPrivateVar = v;
    }
  }
}

const f = new Foo();
console.log(f) // Foo { _myPrivateVar: 5 }

I expected { _myPrivateVar: 5, myPublicVar: 5 }. I understand I can access f.myPublicVar directly, but the problem is when return the instance from an Express server, for example, the resulting JSON object is devoid of the getter property myPublicVar. Why?

2 Answers

The getter and setter is on the prototype, whereas _myPrivateVar is put on the instance itself.

When an object is serialized with JSON.stringify, such as for transfer over the network, only enumerable own properties will be included in the resulting JSON:

const obj = Object.create({ foo: 'foo' });
obj.bar = 'bar';
console.log(JSON.stringify(obj));

Another issue is that class instances usually can't be meaningfully stringified - when parsed on the other side, they'll be interpreted as plain objects, arrays, and values.

If you want to do something like this, you'll have to come up with a way to serialize the instance such that it can be turned into a proper instance on the other side. For a very basic example, if you put all instances into a foos array when sending, you can create a new object with an internal prototype ofFoo.prototype on the other side for every item in the array:

// Sending
class Foo {
  _myPrivateVar = null;

  get myPublicVar() {
    return this._myPrivateVar;
  }

  set myPublicVar(v) {
    if (v > 4) {
      this._myPrivateVar = v;
    }
  }
}

const f1 = new Foo();
f1.myPublicVar = 5;
const f2 = new Foo();
f2.myPublicVar = 1;

console.log(JSON.stringify({ foos: [f1, f2] }));

// Receiving
class Foo {
  _myPrivateVar = null;

  get myPublicVar() {
    return this._myPrivateVar;
  }

  set myPublicVar(v) {
    if (v > 4) {
      this._myPrivateVar = v;
    }
  }
}

const json = `{"foos":[{"_myPrivateVar":5},{"_myPrivateVar":null}]}`;
const { foos } = JSON.parse(json);

const properFoos = foos.map(({ _myPrivateVar }) => {
  const foo = Object.create(Foo.prototype);
  foo._myPrivateVar = _myPrivateVar;
  return foo;
});
console.log(properFoos[0].myPublicVar);
console.log(properFoos[1].myPublicVar);

JavaScript.info

The property name is not placed into User.prototype. Instead, it is created by new before calling the constructor, it’s a property of the object itself.

class User {
  name = "Anonymous";

  sayHi() {
    console.log(`Hello, ${this.name}!`);
  }
}

new User().sayHi();

console.log('sayHi:',User.prototype.sayHi); // placed in User.prototype
console.log('name:',User.prototype.name); // undefined, not placed in User.prototype

Related